From f7b3987fc3c489341fab25bfd1a2a86c4cb301d0 Mon Sep 17 00:00:00 2001 From: docs-bot <77750099+docs-bot@users.noreply.github.com> Date: Thu, 7 Nov 2024 11:35:13 -0500 Subject: [PATCH 01/12] Update audit log event data (#53012) --- src/audit-logs/lib/config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/audit-logs/lib/config.json b/src/audit-logs/lib/config.json index f7f876565dbe..7f6968e1c8af 100644 --- a/src/audit-logs/lib/config.json +++ b/src/audit-logs/lib/config.json @@ -3,5 +3,5 @@ "apiOnlyEvents": "This event is not available in the web interface, only via the REST API, audit log streaming, or JSON/CSV exports.", "apiRequestEvent": "This event is only available via audit log streaming." }, - "sha": "548a504f9bbeb14e74a0da48a869f8e6239b6d9f" + "sha": "5cdd5d7d8ef0e34ebff6addc8d04b7d3da813589" } \ No newline at end of file From 22094f8e80421255b5057739ff3dd04066661803 Mon Sep 17 00:00:00 2001 From: docs-bot <77750099+docs-bot@users.noreply.github.com> Date: Thu, 7 Nov 2024 11:35:57 -0500 Subject: [PATCH 02/12] GraphQL schema update (#53013) Co-authored-by: rachmari <9831992+rachmari@users.noreply.github.com> --- src/graphql/data/fpt/changelog.json | 15 +++++ src/graphql/data/fpt/schema.docs.graphql | 50 +++++++++++++++ src/graphql/data/fpt/schema.json | 76 +++++++++++++++++++++++ src/graphql/data/ghec/schema.docs.graphql | 50 +++++++++++++++ src/graphql/data/ghec/schema.json | 76 +++++++++++++++++++++++ 5 files changed, 267 insertions(+) diff --git a/src/graphql/data/fpt/changelog.json b/src/graphql/data/fpt/changelog.json index 7f3133801c9f..da32f5c7f8e1 100644 --- a/src/graphql/data/fpt/changelog.json +++ b/src/graphql/data/fpt/changelog.json @@ -1,4 +1,19 @@ [ + { + "schemaChanges": [ + { + "title": "The GraphQL schema includes these changes:", + "changes": [ + "

Type UpdateEnterpriseDeployKeySettingInput was added

", + "

Type UpdateEnterpriseDeployKeySettingPayload was added

", + "

Field updateEnterpriseDeployKeySetting was added to object type Mutation

" + ] + } + ], + "previewChanges": [], + "upcomingChanges": [], + "date": "2024-11-07" + }, { "schemaChanges": [ { diff --git a/src/graphql/data/fpt/schema.docs.graphql b/src/graphql/data/fpt/schema.docs.graphql index b0081145a816..5fa000d7562e 100644 --- a/src/graphql/data/fpt/schema.docs.graphql +++ b/src/graphql/data/fpt/schema.docs.graphql @@ -24830,6 +24830,16 @@ type Mutation { input: UpdateEnterpriseDefaultRepositoryPermissionSettingInput! ): UpdateEnterpriseDefaultRepositoryPermissionSettingPayload + """ + Sets whether deploy keys are allowed to be created and used for an enterprise. + """ + updateEnterpriseDeployKeySetting( + """ + Parameters for UpdateEnterpriseDeployKeySetting + """ + input: UpdateEnterpriseDeployKeySettingInput! + ): UpdateEnterpriseDeployKeySettingPayload + """ Sets whether organization members with admin permissions on a repository can change repository visibility. """ @@ -58729,6 +58739,46 @@ type UpdateEnterpriseDefaultRepositoryPermissionSettingPayload { message: String } +""" +Autogenerated input type of UpdateEnterpriseDeployKeySetting +""" +input UpdateEnterpriseDeployKeySettingInput { + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String + + """ + The ID of the enterprise on which to set the deploy key setting. + """ + enterpriseId: ID! @possibleTypes(concreteTypes: ["Enterprise"]) + + """ + The value for the deploy key setting on the enterprise. + """ + settingValue: EnterpriseEnabledDisabledSettingValue! +} + +""" +Autogenerated return type of UpdateEnterpriseDeployKeySetting. +""" +type UpdateEnterpriseDeployKeySettingPayload { + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String + + """ + The enterprise with the updated deploy key setting. + """ + enterprise: Enterprise + + """ + A message confirming the result of updating the deploy key setting. + """ + message: String +} + """ Autogenerated input type of UpdateEnterpriseMembersCanChangeRepositoryVisibilitySetting """ diff --git a/src/graphql/data/fpt/schema.json b/src/graphql/data/fpt/schema.json index 997ae01b844b..242bbb5e21c6 100644 --- a/src/graphql/data/fpt/schema.json +++ b/src/graphql/data/fpt/schema.json @@ -7681,6 +7681,48 @@ } ] }, + { + "name": "updateEnterpriseDeployKeySetting", + "kind": "mutations", + "id": "updateenterprisedeploykeysetting", + "href": "/graphql/reference/mutations#updateenterprisedeploykeysetting", + "description": "

Sets whether deploy keys are allowed to be created and used for an enterprise.

", + "inputFields": [ + { + "name": "input", + "type": "UpdateEnterpriseDeployKeySettingInput!", + "id": "updateenterprisedeploykeysettinginput", + "kind": "input-objects", + "href": "/graphql/reference/input-objects#updateenterprisedeploykeysettinginput" + } + ], + "returnFields": [ + { + "name": "clientMutationId", + "type": "String", + "id": "string", + "kind": "scalars", + "href": "/graphql/reference/scalars#string", + "description": "

A unique identifier for the client performing the mutation.

" + }, + { + "name": "enterprise", + "type": "Enterprise", + "id": "enterprise", + "kind": "objects", + "href": "/graphql/reference/objects#enterprise", + "description": "

The enterprise with the updated deploy key setting.

" + }, + { + "name": "message", + "type": "String", + "id": "string", + "kind": "scalars", + "href": "/graphql/reference/scalars#string", + "description": "

A message confirming the result of updating the deploy key setting.

" + } + ] + }, { "name": "updateEnterpriseMembersCanChangeRepositoryVisibilitySetting", "kind": "mutations", @@ -103270,6 +103312,40 @@ } ] }, + { + "name": "UpdateEnterpriseDeployKeySettingInput", + "kind": "inputObjects", + "id": "updateenterprisedeploykeysettinginput", + "href": "/graphql/reference/input-objects#updateenterprisedeploykeysettinginput", + "description": "

Autogenerated input type of UpdateEnterpriseDeployKeySetting.

", + "inputFields": [ + { + "name": "clientMutationId", + "description": "

A unique identifier for the client performing the mutation.

", + "type": "String", + "id": "string", + "kind": "scalars", + "href": "/graphql/reference/scalars#string" + }, + { + "name": "enterpriseId", + "description": "

The ID of the enterprise on which to set the deploy key setting.

", + "type": "ID!", + "id": "id", + "kind": "scalars", + "href": "/graphql/reference/scalars#id", + "isDeprecated": false + }, + { + "name": "settingValue", + "description": "

The value for the deploy key setting on the enterprise.

", + "type": "EnterpriseEnabledDisabledSettingValue!", + "id": "enterpriseenableddisabledsettingvalue", + "kind": "enums", + "href": "/graphql/reference/enums#enterpriseenableddisabledsettingvalue" + } + ] + }, { "name": "UpdateEnterpriseMembersCanChangeRepositoryVisibilitySettingInput", "kind": "inputObjects", diff --git a/src/graphql/data/ghec/schema.docs.graphql b/src/graphql/data/ghec/schema.docs.graphql index b0081145a816..5fa000d7562e 100644 --- a/src/graphql/data/ghec/schema.docs.graphql +++ b/src/graphql/data/ghec/schema.docs.graphql @@ -24830,6 +24830,16 @@ type Mutation { input: UpdateEnterpriseDefaultRepositoryPermissionSettingInput! ): UpdateEnterpriseDefaultRepositoryPermissionSettingPayload + """ + Sets whether deploy keys are allowed to be created and used for an enterprise. + """ + updateEnterpriseDeployKeySetting( + """ + Parameters for UpdateEnterpriseDeployKeySetting + """ + input: UpdateEnterpriseDeployKeySettingInput! + ): UpdateEnterpriseDeployKeySettingPayload + """ Sets whether organization members with admin permissions on a repository can change repository visibility. """ @@ -58729,6 +58739,46 @@ type UpdateEnterpriseDefaultRepositoryPermissionSettingPayload { message: String } +""" +Autogenerated input type of UpdateEnterpriseDeployKeySetting +""" +input UpdateEnterpriseDeployKeySettingInput { + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String + + """ + The ID of the enterprise on which to set the deploy key setting. + """ + enterpriseId: ID! @possibleTypes(concreteTypes: ["Enterprise"]) + + """ + The value for the deploy key setting on the enterprise. + """ + settingValue: EnterpriseEnabledDisabledSettingValue! +} + +""" +Autogenerated return type of UpdateEnterpriseDeployKeySetting. +""" +type UpdateEnterpriseDeployKeySettingPayload { + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String + + """ + The enterprise with the updated deploy key setting. + """ + enterprise: Enterprise + + """ + A message confirming the result of updating the deploy key setting. + """ + message: String +} + """ Autogenerated input type of UpdateEnterpriseMembersCanChangeRepositoryVisibilitySetting """ diff --git a/src/graphql/data/ghec/schema.json b/src/graphql/data/ghec/schema.json index 997ae01b844b..242bbb5e21c6 100644 --- a/src/graphql/data/ghec/schema.json +++ b/src/graphql/data/ghec/schema.json @@ -7681,6 +7681,48 @@ } ] }, + { + "name": "updateEnterpriseDeployKeySetting", + "kind": "mutations", + "id": "updateenterprisedeploykeysetting", + "href": "/graphql/reference/mutations#updateenterprisedeploykeysetting", + "description": "

Sets whether deploy keys are allowed to be created and used for an enterprise.

", + "inputFields": [ + { + "name": "input", + "type": "UpdateEnterpriseDeployKeySettingInput!", + "id": "updateenterprisedeploykeysettinginput", + "kind": "input-objects", + "href": "/graphql/reference/input-objects#updateenterprisedeploykeysettinginput" + } + ], + "returnFields": [ + { + "name": "clientMutationId", + "type": "String", + "id": "string", + "kind": "scalars", + "href": "/graphql/reference/scalars#string", + "description": "

A unique identifier for the client performing the mutation.

" + }, + { + "name": "enterprise", + "type": "Enterprise", + "id": "enterprise", + "kind": "objects", + "href": "/graphql/reference/objects#enterprise", + "description": "

The enterprise with the updated deploy key setting.

" + }, + { + "name": "message", + "type": "String", + "id": "string", + "kind": "scalars", + "href": "/graphql/reference/scalars#string", + "description": "

A message confirming the result of updating the deploy key setting.

" + } + ] + }, { "name": "updateEnterpriseMembersCanChangeRepositoryVisibilitySetting", "kind": "mutations", @@ -103270,6 +103312,40 @@ } ] }, + { + "name": "UpdateEnterpriseDeployKeySettingInput", + "kind": "inputObjects", + "id": "updateenterprisedeploykeysettinginput", + "href": "/graphql/reference/input-objects#updateenterprisedeploykeysettinginput", + "description": "

Autogenerated input type of UpdateEnterpriseDeployKeySetting.

", + "inputFields": [ + { + "name": "clientMutationId", + "description": "

A unique identifier for the client performing the mutation.

", + "type": "String", + "id": "string", + "kind": "scalars", + "href": "/graphql/reference/scalars#string" + }, + { + "name": "enterpriseId", + "description": "

The ID of the enterprise on which to set the deploy key setting.

", + "type": "ID!", + "id": "id", + "kind": "scalars", + "href": "/graphql/reference/scalars#id", + "isDeprecated": false + }, + { + "name": "settingValue", + "description": "

The value for the deploy key setting on the enterprise.

", + "type": "EnterpriseEnabledDisabledSettingValue!", + "id": "enterpriseenableddisabledsettingvalue", + "kind": "enums", + "href": "/graphql/reference/enums#enterpriseenableddisabledsettingvalue" + } + ] + }, { "name": "UpdateEnterpriseMembersCanChangeRepositoryVisibilitySettingInput", "kind": "inputObjects", From d638285cc5189ba2cc36506820b7cc6dd6f9ea41 Mon Sep 17 00:00:00 2001 From: Isaac Brown <101839405+isaacmbrown@users.noreply.github.com> Date: Thu, 7 Nov 2024 17:01:22 +0000 Subject: [PATCH 03/12] Refresh links to Azure subscription video (#52428) --- .../connecting-an-azure-subscription.md | 6 ++++++ ...ling-github-consumption-through-an-azure-subscription.md | 6 ++++-- .../copilot-business-for-non-ghe/link-azure-subscription.md | 2 ++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/content/billing/managing-the-plan-for-your-github-account/connecting-an-azure-subscription.md b/content/billing/managing-the-plan-for-your-github-account/connecting-an-azure-subscription.md index 9702aec86bbc..c6cb9b53c0ed 100644 --- a/content/billing/managing-the-plan-for-your-github-account/connecting-an-azure-subscription.md +++ b/content/billing/managing-the-plan-for-your-github-account/connecting-an-azure-subscription.md @@ -80,6 +80,12 @@ For example, you link your Azure subscription to your organization {% ifversion * You must know your Azure subscription ID. See [Get subscription and tenant IDs in the Azure portal](https://learn.microsoft.com/en-us/azure/azure-portal/get-subscription-tenant-id) in the Microsoft Docs or [contact Azure support](https://azure.microsoft.com/support/). +## Video demonstration of connecting a subscription + +To connect an Azure subscription, you'll need appropriate access permissions on both {% data variables.product.product_name %} and the Azure billing portal. This may require coordination between two different people. + +To see a demo of the process from beginning to end, see [Billing GitHub consumption through an Azure subscription](https://www.youtube.com/watch?v=Y-f7JKJ4_8Y) on {% data variables.product.company_short %}'s YouTube channel. This video demonstrates the process for an enterprise account. If you're connecting a subscription to an organization account, see "[Connecting your Azure subscription to your organization account](/free-pro-team@latest/billing/managing-the-plan-for-your-github-account/connecting-an-azure-subscription#connecting-your-azure-subscription-to-your-organization-account)." + {% ifversion fpt %} ## Connecting your Azure subscription to your organization account diff --git a/content/video-transcripts/transcript-billing-github-consumption-through-an-azure-subscription.md b/content/video-transcripts/transcript-billing-github-consumption-through-an-azure-subscription.md index 134cc588005c..f6747fbb08a3 100644 --- a/content/video-transcripts/transcript-billing-github-consumption-through-an-azure-subscription.md +++ b/content/video-transcripts/transcript-billing-github-consumption-through-an-azure-subscription.md @@ -3,7 +3,7 @@ title: Transcript - "Billing GitHub consumption through an Azure subscription" intro: Audio and visual transcript. shortTitle: Billing through Azure allowTitleToDifferFromFilename: true -product_video: 'https://www.youtube.com/watch?v=DAiIhJKCt8s' +product_video: 'https://www.youtube.com/watch?v=Y-f7JKJ4_8Y' topics: - Transcripts versions: @@ -27,7 +27,9 @@ And finally, if a Microsoft customer has an Azure discount, it will automaticall If a Microsoft customer also has a Microsoft Azure Consumption Commitment, or MACC, all future GitHub consumption will decrement their MACC as well. -So what GitHub products are eligible for Azure billing? Any GitHub consumption products are eligible today, meaning products that customers pay for based on actual usage, including Copilot for Business, GitHub-hosted actions, larger hosted runners, GitHub Packages and storage, and GitHub Codespaces. Please note that GitHub Enterprise and GitHub Advanced Security are currently not able to be billed through Azure, but are instead invoiced on an annual basis. +So what GitHub products are eligible for Azure billing? Any GitHub consumption products are eligible today, meaning products that customers pay for based on actual usage, including things like GitHub Copilot, GitHub-hosted actions, larger hosted runners, GitHub Packages and storage, and GitHub Codespaces. + +Historically, GitHub Enterprise and Advanced Security were only available through an annual license. However, as of August 1, 2024, they are now also available for metered billing through Azure, for additional flexibility and pay-as-you-go pricing. For existing licensed customers, be sure to connect with your GitHub seller to learn more, as certain restrictions may apply. [A table shows eligibility for Azure billing and MACCs for the products mentioned. In the table, all products eligible for Azure billing are also eligible for MACCs.] diff --git a/data/reusables/copilot-business-for-non-ghe/link-azure-subscription.md b/data/reusables/copilot-business-for-non-ghe/link-azure-subscription.md index 71e35bd53779..ae5c5ed57853 100644 --- a/data/reusables/copilot-business-for-non-ghe/link-azure-subscription.md +++ b/data/reusables/copilot-business-for-non-ghe/link-azure-subscription.md @@ -2,3 +2,5 @@ To pay for licenses, you must connect your enterprise to an Azure subscription. * "[Prerequisites](/billing/managing-the-plan-for-your-github-account/connecting-an-azure-subscription#prerequisites)" * "[Connecting your Azure subscription to an enterprise account](/billing/managing-the-plan-for-your-github-account/connecting-an-azure-subscription#connecting-your-azure-subscription-to-your-enterprise-account)" + +If you prefer a visual overview of the process, watch [Billing {% data variables.product.company_short %} consumption through an Azure subscription](https://www.youtube.com/watch?v=Y-f7JKJ4_8Y) on our YouTube channel. From ccd01e0f52053d419ac67cc0897a2673b2d6b031 Mon Sep 17 00:00:00 2001 From: docs-bot <77750099+docs-bot@users.noreply.github.com> Date: Thu, 7 Nov 2024 12:36:21 -0500 Subject: [PATCH 04/12] Update CodeQL CLI manual (#52973) --- .../codeql-cli/codeql-cli-manual/generate-query-help.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/code-security/codeql-cli/codeql-cli-manual/generate-query-help.md b/content/code-security/codeql-cli/codeql-cli-manual/generate-query-help.md index 1ffeffeada5f..3ac61d92733f 100644 --- a/content/code-security/codeql-cli/codeql-cli-manual/generate-query-help.md +++ b/content/code-security/codeql-cli/codeql-cli-manual/generate-query-help.md @@ -35,7 +35,7 @@ Generate end-user query help from .qhelp files. ### Primary Options -#### `...` +#### `...` \[Mandatory] Query help files to render. Each argument is one of: From 200c927e5d538c82f90d6e7aa0db387d1317100c Mon Sep 17 00:00:00 2001 From: docs-bot <77750099+docs-bot@users.noreply.github.com> Date: Thu, 7 Nov 2024 12:36:28 -0500 Subject: [PATCH 05/12] Update CodeQL query tables (#52974) --- data/reusables/code-scanning/codeql-query-tables/ruby.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/reusables/code-scanning/codeql-query-tables/ruby.md b/data/reusables/code-scanning/codeql-query-tables/ruby.md index 02485583481a..df4a039d0234 100644 --- a/data/reusables/code-scanning/codeql-query-tables/ruby.md +++ b/data/reusables/code-scanning/codeql-query-tables/ruby.md @@ -35,7 +35,7 @@ | [Use of `Kernel.open` or `IO.read` or similar sinks with a non-constant value](https://codeql.github.com/codeql-query-help/ruby/rb-non-constant-kernel-open/) | 078, 088, 073 | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | | [Use of `Kernel.open`, `IO.read` or similar sinks with user-controlled input](https://codeql.github.com/codeql-query-help/ruby/rb-kernel-open/) | 078, 088, 073 | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | | [Use of a broken or weak cryptographic algorithm](https://codeql.github.com/codeql-query-help/ruby/rb-weak-cryptographic-algorithm/) | 327 | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | -| [Use of a broken or weak cryptographic hashing algorithm on sensitive data](https://codeql.github.com/codeql-query-help/ruby/rb-weak-sensitive-data-hashing/) | 327, 328, 916 | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "x" aria-label="Not included" %} | +| [Use of a broken or weak cryptographic hashing algorithm on sensitive data](https://codeql.github.com/codeql-query-help/ruby/rb-weak-sensitive-data-hashing/) | 327, 328, 916 | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | | [Use of externally-controlled format string](https://codeql.github.com/codeql-query-help/ruby/rb-tainted-format-string/) | 134 | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | | [Weak cookie configuration](https://codeql.github.com/codeql-query-help/ruby/rb-weak-cookie-configuration/) | 732, 1275 | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | | [XML external entity expansion](https://codeql.github.com/codeql-query-help/ruby/rb-xxe/) | 611, 776, 827 | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | {% octicon "check" aria-label="Included" %} | From 94f05082994daa5491d47a1a2cfb89f713985b3d Mon Sep 17 00:00:00 2001 From: Pallavi <96553709+pallsama@users.noreply.github.com> Date: Thu, 7 Nov 2024 10:00:04 -0800 Subject: [PATCH 06/12] Undecryptable records in 3.13 and 3.14 (#52879) Co-authored-by: Alejandro Pauly Co-authored-by: Sophie <29382425+sophietheking@users.noreply.github.com> --- ...n-issues-with-upgrades-to-your-instance.md | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/content/admin/upgrading-your-instance/troubleshooting-upgrades/known-issues-with-upgrades-to-your-instance.md b/content/admin/upgrading-your-instance/troubleshooting-upgrades/known-issues-with-upgrades-to-your-instance.md index 3e7892fef4dc..8cfe99cbbbb9 100644 --- a/content/admin/upgrading-your-instance/troubleshooting-upgrades/known-issues-with-upgrades-to-your-instance.md +++ b/content/admin/upgrading-your-instance/troubleshooting-upgrades/known-issues-with-upgrades-to-your-instance.md @@ -212,3 +212,30 @@ If your appliance averages more than 70% CPU utilization, {% data variables.prod As part of upgrading GitHub Enterprise Server to version 3.13 or later, the Elasticsearch service will be upgraded. {% data variables.product.company_short %} strongly recommends following the guidance in "[AUTOTITLE](/admin/upgrading-your-instance/performing-an-upgrade/preparing-for-the-elasticsearch-upgrade)." {% endif %} + +{% ifversion ghes > 3.12 and ghes < 3.15 %} + +## Undecryptable records + +If you are upgrading from {% data variables.product.prodname_ghe_server %} 3.11 or 3.12 to 3.13, or from 3.12 to 3.14, you may run into an issue with undecryptable records due to missing required keys for decryption. The only solution is to delete the undecryptable records. The type of records impacted by this issue are 2FA records, that means you might need to ask users to re-enable two-factor authentication (2FA). + +### Before upgrading + +If you are upgrading from {% data variables.product.prodname_ghe_server %} 3.11 or 3.12 to 3.13, or from 3.12 to 3.14, you can run the encryption diagnostics script to identify the undecryptable records ahead of time. This will give you the opportunity to understand the impact and plan for it. + +1. Download the [encryption diagnostics script](https://gh.io/ghes-encryption-diagnostics). You can use a command like `curl -L -O https://gh.io/ghes-encryption-diagnostics` to download the script. +1. Save the script to the `/data/user/common` directory on the appliance. +1. Follow the instructions at the top of the script and execute it on the appliance. If there are any undecryptable records, they are logged in `/tmp/column_encryption_records_to_be_deleted.log`. Any records logged here means that the system was not able to find the keys for them and hence was not able to decrypt the data in those records. + +At this stage, please note that these records will be deleted as part of the process. The script will warn you about the users who will need to re-enroll into 2FA after the upgrade. The impacted users' handles are logged in `/tmp/column_encryption_users_to_have_2fa_disabled.log`. These users will need to be re-enrolled into 2FA. + +If the script runs into unexpected issues, you will be prompted to [contact {% data variables.contact.github_support %}](/support/contacting-github-support). Errors related to these issues will be logged in `/tmp/column_encryption_unexpected_errors.log`. If you are in a dire situation and are unable to have users re-enroll into 2FA, [contact {% data variables.contact.github_support %}](/support/contacting-github-support) for help. + +### During the upgrade + +In case you did not have the opportunity to run the encryption diagnostics script ahead of time, there are mechanisms in the product to help you. The pre-flight checks during the upgrade process will detect undecryptable records and log them in `/tmp/column_encryption_records_to_be_deleted.log`. The sequence will warn you of the users who will need to re-enable 2FA after the upgrade. The impacted users records are logged in `/tmp/column_encryption_users_to_have_2fa_disabled.log`. + +If undecryptable records are detected, you will be prompted whether you want to proceed with the upgrade or not. If you proceed, the upgrade process deletes the undecryptable records. Otherwise, the upgrade process will exit. + +If you have any questions during the upgrade, you can reach out to {% data variables.contact.github_support %}. Once you have had the time and opportunity to understand the impact, you can retrigger the upgrade. +{% endif %} From 630efbd0b36cc65363c61e2a06049f39c24c3c4c Mon Sep 17 00:00:00 2001 From: "release-controller[bot]" <110195724+release-controller[bot]@users.noreply.github.com> Date: Thu, 7 Nov 2024 18:00:26 +0000 Subject: [PATCH 07/12] Patch release notes for GitHub Enterprise Server (#52971) Co-authored-by: Release-Controller Co-authored-by: Pallavi <96553709+pallsama@users.noreply.github.com> Co-authored-by: Sophie <29382425+sophietheking@users.noreply.github.com> Co-authored-by: Jo <82854796+AskJo@users.noreply.github.com> --- .../enterprise-server/3-10/17.yml | 2 + .../enterprise-server/3-10/19.yml | 34 +++++++++ .../enterprise-server/3-11/15.yml | 2 + .../enterprise-server/3-11/17.yml | 60 +++++++++++++++ .../enterprise-server/3-12/11.yml | 58 ++++++++++++++ .../enterprise-server/3-12/9.yml | 2 + .../enterprise-server/3-13/2.yml | 4 +- .../enterprise-server/3-13/4.yml | 2 + .../enterprise-server/3-13/6.yml | 62 +++++++++++++++ .../enterprise-server/3-14/1.yml | 2 + .../enterprise-server/3-14/3.yml | 76 +++++++++++++++++++ ...023-10-git-push-made-but-not-registered.md | 2 +- 12 files changed, 303 insertions(+), 3 deletions(-) create mode 100644 data/release-notes/enterprise-server/3-10/19.yml create mode 100644 data/release-notes/enterprise-server/3-11/17.yml create mode 100644 data/release-notes/enterprise-server/3-12/11.yml create mode 100644 data/release-notes/enterprise-server/3-13/6.yml create mode 100644 data/release-notes/enterprise-server/3-14/3.yml diff --git a/data/release-notes/enterprise-server/3-10/17.yml b/data/release-notes/enterprise-server/3-10/17.yml index 056014b7fb71..f790b376b3ea 100644 --- a/data/release-notes/enterprise-server/3-10/17.yml +++ b/data/release-notes/enterprise-server/3-10/17.yml @@ -5,6 +5,8 @@ sections: **MEDIUM:** An attacker could steal sensitive information by exploiting a Cross-Site Scripting vulnerability in the repository transfer feature. This exploitation would require social engineering. GitHub has requested CVE ID [CVE-2024-8770](https://www.cve.org/cverecord?id=CVE-2024-8770) for this vulnerability, which was reported via the [GitHub Bug Bounty program](https://bounty.github.com/). - | **MEDIUM:** An attacker could push a commit with changes to a workflow using a PAT or OAuth app that lacks the appropriate `workflow` scope by pushing a triple-nested tag pointing at the associated commit. GitHub has requested CVE ID [CVE-2024-8263](https://www.cve.org/cverecord?id=CVE-2024-8263) for this vulnerability, which was reported via the [GitHub Bug Bounty program](https://bounty.github.com/). + - | + **HIGH:** A GitHub App installed in organizations could upgrade some permissions from read to write access without approval from an organization administrator. An attacker would require an account with administrator access to install a malicious GitHub App. GitHub has requested [CVE ID CVE-2024-8810](https://www.cve.org/cverecord?id=CVE-2024-8810) for this vulnerability, which was reported via the [GitHub Bug Bounty Program](https://bounty.github.com/). [Updated: 2024-11-07] bugs: - | For instances deployed on AWS with IMDSv2 enforced, fallback to private IPs was not successful. diff --git a/data/release-notes/enterprise-server/3-10/19.yml b/data/release-notes/enterprise-server/3-10/19.yml new file mode 100644 index 000000000000..7ac6e1e21477 --- /dev/null +++ b/data/release-notes/enterprise-server/3-10/19.yml @@ -0,0 +1,34 @@ +date: '2024-11-07' +sections: + security_fixes: + - | + **HIGH**: An attacker could bypass SAML single sign-on (SSO) authentication with the optional encrypted assertions feature, allowing unauthorized provisioning of users and access to the instance, by exploiting an improper verification of cryptographic signatures vulnerability in GitHub Enterprise Server. This is a follow up fix for [CVE-2024-9487](https://www.cve.org/cverecord?id=CVE-2024-9487) to further harden the encrypted assertions feature against this type of attack. Please note that encrypted assertions are not enabled by default. Instances not utilizing SAML SSO, or utilizing SAML SSO authentication without encrypted assertions, are not impacted. Additionally, an attacker would require direct network access as well as a signed SAML response or metadata document to exploit this vulnerability. + known_issues: + - | + Custom firewall rules are removed during the upgrade process. + - | + During the validation phase of a configuration run, a `No such object` error may occur for the Notebook and Viewscreen services. This error can be ignored as the services should still correctly start. + - | + If the root site administrator is locked out of the Management Console after failed login attempts, the account does not unlock automatically after the defined lockout time. Someone with administrative SSH access to the instance must unlock the account using the administrative shell. For more information, see "[AUTOTITLE](/admin/configuration/administering-your-instance-from-the-management-console/troubleshooting-access-to-the-management-console#unlocking-the-root-site-administrator-account)." + - | + The `mbind: Operation not permitted` error in the `/var/log/mysql/mysql.err` file can be ignored. MySQL 8 does not gracefully handle when the `CAP_SYS_NICE` capability isn't required, and outputs an error instead of a warning. + - | + {% data reusables.release-notes.2023-11-aws-system-time %} + - | + On an instance with the HTTP `X-Forwarded-For` header configured for use behind a load balancer, all client IP addresses in the instance's audit log erroneously appear as 127.0.0.1. + - | + {% data reusables.release-notes.2023-10-git-push-made-but-not-registered %} + - | + {% data reusables.release-notes.large-adoc-files-issue %} + - | + {% data reusables.release-notes.2024-01-haproxy-upgrade-causing-increased-errors %} + - | + The `reply.[HOSTNAME]` subdomain is falsely always displaying as having no SSL and DNS record, when testing the domain settings via the Management Console without subdomain isolation. + - | + Admin stats REST API endpoints may timeout on appliances with many users or repositories. Retrying the request until data is returned is advised. + - | + {% data reusables.release-notes.2024-06-possible-frontend-5-minute-outage-during-hotpatch-upgrade %} + - | + When restoring from a backup snapshot, a large number of `mapper_parsing_exception` errors may be displayed. + - | + Services may respond with a `503` status due to an out of date `haproxy` configuration. This can usually be resolved with a `ghe-config-apply` run. diff --git a/data/release-notes/enterprise-server/3-11/15.yml b/data/release-notes/enterprise-server/3-11/15.yml index 1466ca937782..6432ca7dda9d 100644 --- a/data/release-notes/enterprise-server/3-11/15.yml +++ b/data/release-notes/enterprise-server/3-11/15.yml @@ -5,6 +5,8 @@ sections: **MEDIUM:** An attacker could steal sensitive information by exploiting a Cross-Site Scripting vulnerability in the repository transfer feature. This exploitation would require social engineering. GitHub has requested CVE ID [CVE-2024-8770](https://www.cve.org/cverecord?id=CVE-2024-8770) for this vulnerability, which was reported via the [GitHub Bug Bounty program](https://bounty.github.com/). - | **MEDIUM:** An attacker could push a commit with changes to a workflow using a PAT or OAuth app that lacks the appropriate `workflow` scope by pushing a triple-nested tag pointing at the associated commit. GitHub has requested CVE ID [CVE-2024-8263](https://www.cve.org/cverecord?id=CVE-2024-8263) for this vulnerability, which was reported via the [GitHub Bug Bounty program](https://bounty.github.com/). + - | + **HIGH:** A GitHub App installed in organizations could upgrade some permissions from read to write access without approval from an organization administrator. An attacker would require an account with administrator access to install a malicious GitHub App. GitHub has requested [CVE ID CVE-2024-8810](https://www.cve.org/cverecord?id=CVE-2024-8810) for this vulnerability, which was reported via the [GitHub Bug Bounty Program](https://bounty.github.com/). [Updated: 2024-11-07] bugs: - | For instances deployed on AWS with IMDSv2 enforced, fallback to private IPs was not successful. diff --git a/data/release-notes/enterprise-server/3-11/17.yml b/data/release-notes/enterprise-server/3-11/17.yml new file mode 100644 index 000000000000..49082d4c81c7 --- /dev/null +++ b/data/release-notes/enterprise-server/3-11/17.yml @@ -0,0 +1,60 @@ +date: '2024-11-07' +sections: + security_fixes: + - | + **HIGH**: An attacker could bypass SAML single sign-on (SSO) authentication with the optional encrypted assertions feature, allowing unauthorized provisioning of users and access to the instance, by exploiting an improper verification of cryptographic signatures vulnerability in GitHub Enterprise Server. This is a follow up fix for [CVE-2024-9487](https://www.cve.org/cverecord?id=CVE-2024-9487) to further harden the encrypted assertions feature against this type of attack. Please note that encrypted assertions are not enabled by default. Instances not utilizing SAML SSO, or utilizing SAML SSO authentication without encrypted assertions, are not impacted. Additionally, an attacker would require direct network access as well as a signed SAML response or metadata document to exploit this vulnerability. + - | + **HIGH**: An attacker could achieve container escape and privilege escalation to root by exploiting a path collision and arbitrary code execution via the `ghe-firejail` path. GitHub has requested CVE ID [CVE-2024-10007](https://www.cve.org/cverecord?id=CVE-2024-10007) for this vulnerability, which was reported via the [GitHub Bug Bounty program](https://bounty.github.com/). + bugs: + - | + This error message `mbind: Operation not permitted` was repeatedly showing in the `/var/log/mysql/mysql.err` MySQL logs. + - | + When saving settings in the Management Console, the configuration run would stop if the `enterprise-manage` process was restarted. + - | + A missing configuration value prevented Dependabot from creating group update pull requests. + - | + On an instance with GitHub Actions enabled, some maintenance tasks could fail due to incomplete upgrade steps during previous upgrades to new releases of GitHub Enterprise Server. + - | + The initial setup certificate generation in AWS took longer than expected due to fallback to private IPs. The time for this fallback has been reduced. + - | + If the primary instance was unreachable, running `ghe-repl-stop --force` on a replica would fail during the config apply run. + - | + When restoring from a backup, repositories that had been deleted in the last 90 days were not completely restored. + - | + Restoring Git repositories using `backup-utils` occasionally failed. + - | + Some customers upgrading from 3.11 to 3.13 may experience issues with undecryptable records during the upgrade. This issue has now been resolved. We recommend you read "[Undecryptable records](/enterprise-server@3.13/admin/upgrading-your-instance/troubleshooting-upgrades/known-issues-with-upgrades-to-your-instance#undecryptable-records)." + changes: + - | + For instances deployed on AWS, the default settings for Chrony NTP synchronization have been aligned with AWS's suggested default configurations. + known_issues: + - | + Custom firewall rules are removed during the upgrade process. + - | + During the validation phase of a configuration run, a `No such object` error may occur for the Notebook and Viewscreen services. This error can be ignored as the services should still correctly start. + - | + If the root site administrator is locked out of the Management Console after failed login attempts, the account does not unlock automatically after the defined lockout time. Someone with administrative SSH access to the instance must unlock the account using the administrative shell. See "[AUTOTITLE](/admin/configuration/administering-your-instance-from-the-management-console/troubleshooting-access-to-the-management-console#unlocking-the-root-site-administrator-account)." + - | + The `mbind: Operation not permitted` error in the `/var/log/mysql/mysql.err` file can be ignored. MySQL 8 does not gracefully handle when the `CAP_SYS_NICE` capability isn't required, and outputs an error instead of a warning. + - | + {% data reusables.release-notes.2023-11-aws-system-time %} + - | + On an instance with the HTTP `X-Forwarded-For` header configured for use behind a load balancer, all client IP addresses in the instance's audit log erroneously appear as 127.0.0.1. + - | + {% data reusables.release-notes.2023-10-git-push-made-but-not-registered %} + - | + {% data reusables.release-notes.large-adoc-files-issue %} + - | + {% data reusables.release-notes.2024-01-haproxy-upgrade-causing-increased-errors %} + - | + Repositories originally imported using `ghe-migrator` will not correctly track GitHub Advanced Security contributions. + - | + The `reply.[HOSTNAME]` subdomain is falsely always displaying as having no SSL and DNS record, when testing the domain settings via the Management Console without subdomain isolation. + - | + Admin stats REST API endpoints may timeout on appliances with many users or repositories. Retrying the request until data is returned is advised. + - | + {% data reusables.release-notes.2024-06-possible-frontend-5-minute-outage-during-hotpatch-upgrade %} + - | + When restoring from a backup snapshot, a large number of `mapper_parsing_exception` errors may be displayed. + - | + Services may respond with a `503` status due to an out of date `haproxy` configuration. This can usually be resolved with a `ghe-config-apply` run. diff --git a/data/release-notes/enterprise-server/3-12/11.yml b/data/release-notes/enterprise-server/3-12/11.yml new file mode 100644 index 000000000000..bad45f480c66 --- /dev/null +++ b/data/release-notes/enterprise-server/3-12/11.yml @@ -0,0 +1,58 @@ +date: '2024-11-07' +sections: + security_fixes: + - | + **HIGH**: An attacker could bypass SAML single sign-on (SSO) authentication with the optional encrypted assertions feature, allowing unauthorized provisioning of users and access to the instance, by exploiting an improper verification of cryptographic signatures vulnerability in GitHub Enterprise Server. This is a follow up fix for [CVE-2024-9487](https://www.cve.org/cverecord?id=CVE-2024-9487) to further harden the encrypted assertions feature against this type of attack. Please note that encrypted assertions are not enabled by default. Instances not utilizing SAML SSO, or utilizing SAML SSO authentication without encrypted assertions, are not impacted. Additionally, an attacker would require direct network access as well as a signed SAML response or metadata document to exploit this vulnerability. + - | + **HIGH**: An attacker could achieve container escape and privilege escalation to root by exploiting a path collision and arbitrary code execution via the `ghe-firejail` path. GitHub has requested CVE ID [CVE-2024-10007](https://www.cve.org/cverecord?id=CVE-2024-10007) for this vulnerability, which was reported via the [GitHub Bug Bounty program](https://bounty.github.com/). + bugs: + - | + This error message `mbind: Operation not permitted` was repeatedly showing in the `/var/log/mysql/mysql.err` MySQL logs. + - | + When saving settings in the Management Console, the configuration run would stop if the `enterprise-manage` process was restarted. + - | + A missing configuration value prevented Dependabot from creating group update pull requests. + - | + On an instance with GitHub Actions enabled, some maintenance tasks could fail due to incomplete upgrade steps during previous upgrades to new releases of GitHub Enterprise Server. + - | + The initial setup certificate generation in AWS took longer than expected due to fallback to private IPs. The time for this fallback has been reduced. + - | + If the primary instance was unreachable, running `ghe-repl-stop --force` on a replica would fail during the config apply run. + - | + When restoring from a backup, repositories that had been deleted in the last 90 days were not completely restored. + - | + Restoring Git repositories using backup-utils occasionally failed. + - | + Organizations were limited to using 100 Actions organization variables instead of 1,000. + - | + Some customers upgrading from 3.12 to 3.13 or to 3.14 may experience issues with undecryptable records during the upgrade. This issue has now been resolved. We recommend you read "[Undecryptable records](/enterprise-server@3.14/admin/upgrading-your-instance/troubleshooting-upgrades/known-issues-with-upgrades-to-your-instance#undecryptable-records)." + changes: + - | + For instances deployed on AWS, the default settings for Chrony NTP synchronization have been aligned with AWS's suggested default configurations. + known_issues: + - | + Custom firewall rules are removed during the upgrade process. + - | + During the validation phase of a configuration run, a `No such object` error may occur for the Notebook and Viewscreen services. This error can be ignored as the services should still correctly start. + - | + If the root site administrator is locked out of the Management Console after failed login attempts, the account does not unlock automatically after the defined lockout time. Someone with administrative SSH access to the instance must unlock the account using the administrative shell. See "[AUTOTITLE](/admin/configuration/administering-your-instance-from-the-management-console/troubleshooting-access-to-the-management-console#unlocking-the-root-site-administrator-account)." + - | + The `mbind: Operation not permitted` error in the `/var/log/mysql/mysql.err` file can be ignored. MySQL 8 does not gracefully handle when the `CAP_SYS_NICE` capability isn't required, and outputs an error instead of a warning. + - | + {% data reusables.release-notes.2023-11-aws-system-time %} + - | + On an instance with the HTTP `X-Forwarded-For` header configured for use behind a load balancer, all client IP addresses in the instance's audit log erroneously appear as 127.0.0.1. + - | + {% data reusables.release-notes.large-adoc-files-issue %} + - | + Repositories originally imported using `ghe-migrator` will not correctly track GitHub Advanced Security contributions. + - | + The `reply.[HOSTNAME]` subdomain is falsely always displaying as having no SSL and DNS record, when testing the domain settings via the Management Console without subdomain isolation. + - | + Admin stats REST API endpoints may timeout on appliances with many users or repositories. Retrying the request until data is returned is advised. + - | + {% data reusables.release-notes.2024-06-possible-frontend-5-minute-outage-during-hotpatch-upgrade %} + - | + When restoring from a backup snapshot, a large number of `mapper_parsing_exception` errors may be displayed. + - | + Services may respond with a `503` status due to an out of date `haproxy` configuration. This can usually be resolved with a `ghe-config-apply` run. diff --git a/data/release-notes/enterprise-server/3-12/9.yml b/data/release-notes/enterprise-server/3-12/9.yml index 5dcd8e37d9e6..c06549c0c159 100644 --- a/data/release-notes/enterprise-server/3-12/9.yml +++ b/data/release-notes/enterprise-server/3-12/9.yml @@ -5,6 +5,8 @@ sections: **MEDIUM:** An attacker could steal sensitive information by exploiting a Cross-Site Scripting vulnerability in the repository transfer feature. This exploitation would require social engineering. GitHub has requested CVE ID [CVE-2024-8770](https://www.cve.org/cverecord?id=CVE-2024-8770) for this vulnerability, which was reported via the [GitHub Bug Bounty program](https://bounty.github.com/). - | **MEDIUM:** An attacker could push a commit with changes to a workflow using a PAT or OAuth app that lacks the appropriate `workflow` scope by pushing a triple-nested tag pointing at the associated commit. GitHub has requested CVE ID [CVE-2024-8263](https://www.cve.org/cverecord?id=CVE-2024-8263) for this vulnerability, which was reported via the [GitHub Bug Bounty program](https://bounty.github.com/). + - | + **HIGH:** A GitHub App installed in organizations could upgrade some permissions from read to write access without approval from an organization administrator. An attacker would require an account with administrator access to install a malicious GitHub App. GitHub has requested [CVE ID CVE-2024-8810](https://www.cve.org/cverecord?id=CVE-2024-8810) for this vulnerability, which was reported via the [GitHub Bug Bounty Program](https://bounty.github.com/). [Updated: 2024-11-07] bugs: - | For instances deployed on AWS with IMDSv2 enforced, fallback to private IPs was not successful. diff --git a/data/release-notes/enterprise-server/3-13/2.yml b/data/release-notes/enterprise-server/3-13/2.yml index 2709cd53e56b..1e91efe3f296 100644 --- a/data/release-notes/enterprise-server/3-13/2.yml +++ b/data/release-notes/enterprise-server/3-13/2.yml @@ -23,9 +23,9 @@ sections: - | **MEDIUM:** An attacker could have unauthorized read access to issue content inside an internal repository via GitHub projects. This attack required attacker access to the corresponding project board. GitHub has requested CVE ID [CVE-2024-5817](https://nvd.nist.gov/vuln/detail/CVE-2024-5817) for this vulnerability, which was reported via the [GitHub Bug Bounty program](https://bounty.github.com/). - | - An attacker could access previously executed private required workflows by changing the repository visibility from private to public. This occurred despite the repositories with the required workflows remaining private. This vulnerability was reported via the [GitHub Bug Bounty program](https://bounty.github.com/). + **MEDIUM**: An attacker could gain unauthorized access to secret scanning alert data because the [REST API secret scanning endpoint](/rest/secret-scanning/secret-scanning?apiVersion=2022-11-28) did not properly verify whether the user account has the business owner role. Only organization members can exploit this vulnerability, requiring a {% data variables.product.pat_generic %} (PAT) with `repo` or `security_events` scopes, limiting exposure to internal actors. Exploitation also required secret scanning to be enabled on user-owned repositories. GitHub has requested CVE ID [CVE-2024-10824](https://www.cve.org/CVERecord?id=CVE-2024-10824) for this vulnerability. [Updated: 2024-11-07] - | - A user without the enterprise owner role could view all secret scanning alerts for user-owned repositories using the REST API. Alerts in user-owned repositories are now properly restricted to only be visible to enterprise owners. + An attacker could access previously executed private required workflows by changing the repository visibility from private to public. This occurred despite the repositories with the required workflows remaining private. This vulnerability was reported via the [GitHub Bug Bounty program](https://bounty.github.com/). - | Packages have been updated to the latest security versions. bugs: diff --git a/data/release-notes/enterprise-server/3-13/4.yml b/data/release-notes/enterprise-server/3-13/4.yml index 5b87c637d477..de07283f7eb1 100644 --- a/data/release-notes/enterprise-server/3-13/4.yml +++ b/data/release-notes/enterprise-server/3-13/4.yml @@ -5,6 +5,8 @@ sections: **MEDIUM:** An attacker could steal sensitive information by exploiting a Cross-Site Scripting vulnerability in the repository transfer feature. This exploitation would require social engineering. GitHub has requested CVE ID [CVE-2024-8770](https://www.cve.org/cverecord?id=CVE-2024-8770) for this vulnerability, which was reported via the [GitHub Bug Bounty program](https://bounty.github.com/). - | **MEDIUM:** An attacker could push a commit with changes to a workflow using a PAT or OAuth app that lacks the appropriate `workflow` scope by pushing a triple-nested tag pointing at the associated commit. GitHub has requested CVE ID [CVE-2024-8263](https://www.cve.org/cverecord?id=CVE-2024-8263) for this vulnerability, which was reported via the [GitHub Bug Bounty program](https://bounty.github.com/). + - | + **HIGH:** A GitHub App installed in organizations could upgrade some permissions from read to write access without approval from an organization administrator. An attacker would require an account with administrator access to install a malicious GitHub App. GitHub has requested [CVE ID CVE-2024-8810](https://www.cve.org/cverecord?id=CVE-2024-8810) for this vulnerability, which was reported via the [GitHub Bug Bounty Program](https://bounty.github.com/). [Updated: 2024-11-07] bugs: - | For instances deployed on AWS with IMDSv2 enforced, fallback to private IPs was not successful. diff --git a/data/release-notes/enterprise-server/3-13/6.yml b/data/release-notes/enterprise-server/3-13/6.yml new file mode 100644 index 000000000000..849c6f6c71ca --- /dev/null +++ b/data/release-notes/enterprise-server/3-13/6.yml @@ -0,0 +1,62 @@ +date: '2024-11-07' +sections: + security_fixes: + - | + Elasticsearch packages have been updated to the latest security versions. + - | + **HIGH**: An attacker could bypass SAML single sign-on (SSO) authentication with the optional encrypted assertions feature, allowing unauthorized provisioning of users and access to the instance, by exploiting an improper verification of cryptographic signatures vulnerability in GitHub Enterprise Server. This is a follow up fix for [CVE-2024-9487](https://www.cve.org/cverecord?id=CVE-2024-9487) to further harden the encrypted assertions feature against this type of attack. Please note that encrypted assertions are not enabled by default. Instances not utilizing SAML SSO, or utilizing SAML SSO authentication without encrypted assertions, are not impacted. Additionally, an attacker would require direct network access as well as a signed SAML response or metadata document to exploit this vulnerability. + - | + **HIGH**: An attacker could achieve container escape and privilege escalation to root by exploiting a path collision and arbitrary code execution via the `ghe-firejail` path. GitHub has requested CVE ID [CVE-2024-10007](https://www.cve.org/cverecord?id=CVE-2024-10007) for this vulnerability, which was reported via the [GitHub Bug Bounty program](https://bounty.github.com/). + bugs: + - | + A missing configuration value prevented Dependabot from creating group update pull requests. + - | + When saving settings in the Management Console, the configuration run would stop if the `enterprise-manage` process was restarted. + - | + On an instance with GitHub Actions enabled, some maintenance tasks could fail due to incomplete upgrade steps during previous upgrades to new releases of GitHub Enterprise Server. + - | + The initial setup certificate generation in AWS took longer than expected due to fallback to private IPs. The time for this fallback has been reduced. + - | + The `ghe-support-bundle` generation would fail when the `aqueduct-lite` service is down. + - | + If the primary instance was unreachable, running `ghe-repl-stop --force` on a replica would fail during the config apply run. + - | + For instances that use the mandatory message feature logging in to certain URLs may have caused a 500 error. + - | + When restoring from a backup, repositories that had been deleted in the last 90 days were not completely restored. + - | + Restoring Git repositories using backup-utils occasionally failed. + - | + Enterprise installations experienced unpredictable repository search results due to the default 4,000 repository limit. A relaxed repository filter mode, which includes all single-tenant organization repositories and bypasses the limit, has been introduced. Administrators can enable this mode using `ghe-config app.github.enterprise-repo-search-filter-enabled true && ghe-config-apply`. + - | + Organizations were limited to using 100 Actions organization variables instead of 1,000. + - | + Running `config-apply` became stuck under certain circumstances due to a misconfiguration with Packages and Elasticsearch. + - | + Some customers upgrading to 3.13 may experience issues with undecryptable records during the upgrade. This issue has now been resolved. We recommend you read "[Undecryptable records](/admin/upgrading-your-instance/troubleshooting-upgrades/known-issues-with-upgrades-to-your-instance#undecryptable-records)." + changes: + - | + When connecting to an appliance via SSH, a notification about upcoming root disk changes displays. + known_issues: + - | + During the validation phase of a configuration run, a `No such object` error may occur for the Notebook and Viewscreen services. This error can be ignored as the services should still correctly start. + - | + If the root site administrator is locked out of the Management Console after failed login attempts, the account does not unlock automatically after the defined lockout time. Someone with administrative SSH access to the instance must unlock the account using the administrative shell. See "[AUTOTITLE](/admin/configuration/administering-your-instance-from-the-management-console/troubleshooting-access-to-the-management-console#unlocking-the-root-site-administrator-account)." + - | + On an instance with the HTTP `X-Forwarded-For` header configured for use behind a load balancer, all client IP addresses in the instance's audit log erroneously appear as 127.0.0.1. + - | + Repositories originally imported using `ghe-migrator` will not correctly track GitHub Advanced Security contributions. + - | + For an instance in a cluster configuration and with GitHub Actions enabled, restoring a cluster from backup requires targeting the primary DB node. + - | + When following the steps for [Replacing the primary MySQL node](/admin/monitoring-managing-and-updating-your-instance/configuring-clustering/replacing-a-cluster-node#replacing-the-primary-mysql-node), step 14 (running `ghe-cluster-config-apply`) might fail with errors. If this occurs, re-running `ghe-cluster-config-apply` is expected to succeed. + - | + Running a `config apply` as part of the steps for [Replacing a node in an emergency](/admin/monitoring-managing-and-updating-your-instance/configuring-clustering/replacing-a-cluster-node#replacing-a-node-in-an-emergency) may fail with errors if the node being replaced is still reachable. If this occurs, shutdown the node and repeat the steps. + - | + {% data reusables.release-notes.2024-06-possible-frontend-5-minute-outage-during-hotpatch-upgrade %} + - | + When restoring data originally backed up from a 3.13 appliance onto a 3.13 appliance, the elasticsearch indices need to be reindexed before some of the data will show up. This happens via a nightly scheduled job. It can also be forced by running `/usr/local/share/enterprise/ghe-es-search-repair`. + - | + When restoring from a backup snapshot, a large number of `mapper_parsing_exception` errors may be displayed. + - | + Services may respond with a `503` status due to an out of date `haproxy` configuration. This can usually be resolved with a `ghe-config-apply` run. diff --git a/data/release-notes/enterprise-server/3-14/1.yml b/data/release-notes/enterprise-server/3-14/1.yml index 70c5bda5781a..37c23240f47b 100644 --- a/data/release-notes/enterprise-server/3-14/1.yml +++ b/data/release-notes/enterprise-server/3-14/1.yml @@ -3,6 +3,8 @@ sections: security_fixes: - | **MEDIUM:** An attacker could steal sensitive information by exploiting a Cross-Site Scripting vulnerability in the repository transfer feature. This exploitation would require social engineering. GitHub has requested CVE ID [CVE-2024-8770](https://www.cve.org/cverecord?id=CVE-2024-8770) for this vulnerability, which was reported via the [GitHub Bug Bounty program](https://bounty.github.com/). + - | + **HIGH:** A GitHub App installed in organizations could upgrade some permissions from read to write access without approval from an organization administrator. An attacker would require an account with administrator access to install a malicious GitHub App. GitHub has requested [CVE ID CVE-2024-8810](https://www.cve.org/cverecord?id=CVE-2024-8810) for this vulnerability, which was reported via the [GitHub Bug Bounty Program](https://bounty.github.com/). [Updated: 2024-11-07] bugs: - | On an instance with GitHub Actions enabled, due to an insufficient wait time, MS SQL and MySQL replication could fail with the error message `Failed to start nomad service!`. diff --git a/data/release-notes/enterprise-server/3-14/3.yml b/data/release-notes/enterprise-server/3-14/3.yml new file mode 100644 index 000000000000..af29cdfdfb81 --- /dev/null +++ b/data/release-notes/enterprise-server/3-14/3.yml @@ -0,0 +1,76 @@ +date: '2024-11-07' +sections: + security_fixes: + - | + Elasticsearch packages have been updated to the latest security versions. + - | + Packages have been updated to the latest security version. + - | + **HIGH**: An attacker could bypass SAML single sign-on (SSO) authentication with the optional encrypted assertions feature, allowing unauthorized provisioning of users and access to the instance, by exploiting an improper verification of cryptographic signatures vulnerability in GitHub Enterprise Server. This is a follow up fix for [CVE-2024-9487](https://www.cve.org/cverecord?id=CVE-2024-9487) to further harden the encrypted assertions feature against this type of attack. Please note that encrypted assertions are not enabled by default. Instances not utilizing SAML SSO, or utilizing SAML SSO authentication without encrypted assertions, are not impacted. Additionally, an attacker would require direct network access as well as a signed SAML response or metadata document to exploit this vulnerability. + - | + **HIGH**: An attacker could achieve container escape and privilege escalation to root by exploiting a path collision and arbitrary code execution via the `ghe-firejail` path. GitHub has requested CVE ID [CVE-2024-10007](https://www.cve.org/cverecord?id=CVE-2024-10007) for this vulnerability, which was reported via the [GitHub Bug Bounty program](https://bounty.github.com/). + bugs: + - | + When saving settings in the Management Console, the configuration run would stop if the `enterprise-manage` process was restarted. + - | + On an instance with GitHub Actions enabled, some maintenance tasks could fail due to incomplete upgrade steps during previous upgrades to new releases of GitHub Enterprise Server. + - | + A repeated error message concerning connectivity to port 6002 was emitted to the system logs when GitHub Actions was enabled. + - | + The initial setup certificate generation in AWS took longer than expected due to fallback to private IPs. The time for this fallback has been reduced. + - | + The `ghe-support-bundle` generation would fail when the `aqueduct-lite` service is down. + - | + If the primary instance was unreachable, running `ghe-repl-stop --force` on a replica would fail during the config apply run. + - | + Administrators in the SCIM private beta (versions < 3.14) that decided to upgrade their private beta appliance see an incorrectly checked box in the "SCIM Configuration" section of the Enterprise settings authentication security page in 3.14. + - | + Certain URLs may have caused a 500 error on instances that use the mandatory message feature logging. + - | + When restoring from a backup, repositories that had been deleted in the last 90 days were not completely restored. + - | + For instances that use secret scanning, custom messages for push protection set by the enterprise did not display to users. + - | + Restoring Git repositories using `backup-utils` occasionally failed. + - | + Enterprise installations experienced unpredictable repository search results due to the default 4,000 repository limit. A relaxed repository filter mode, which includes all single-tenant organization repositories and bypasses the limit, has been introduced. Administrators can enable this mode using `ghe-config app.github.enterprise-repo-search-filter-enabled true && ghe-config-apply`. + - | + Running `config-apply` became stuck under certain circumstances due to a misconfiguration with Packages and Elasticsearch. + - | + Audit log events for secret scanning alerts incorrectly displayed a blank secret type when generated for a custom pattern. + - | + Some customers upgrading to 3.14 may experience issues with undecryptable records during the upgrade. This issue has now been resolved. We recommend you read "[Undecryptable records](/admin/upgrading-your-instance/troubleshooting-upgrades/known-issues-with-upgrades-to-your-instance#undecryptable-records)." + changes: + - | + When connecting to an appliance via SSH, a notification about upcoming root disk changes displays. + known_issues: + - | + During the validation phase of a configuration run, a `No such object` error may occur for the Notebook and Viewscreen services. This error can be ignored as the services should still correctly start. + - | + If the root site administrator is locked out of the Management Console after failed login attempts, the account does not unlock automatically after the defined lockout time. Someone with administrative SSH access to the instance must unlock the account using the administrative shell. See "[AUTOTITLE](/admin/configuration/administering-your-instance-from-the-management-console/troubleshooting-access-to-the-management-console#unlocking-the-root-site-administrator-account)." + - | + On an instance with the HTTP `X-Forwarded-For` header configured for use behind a load balancer, all client IP addresses in the instance's audit log erroneously appear as 127.0.0.1. + - | + {% data reusables.release-notes.large-adoc-files-issue %} + - | + Repositories originally imported using `ghe-migrator` will not correctly track GitHub Advanced Security contributions. + - | + Admin stats REST API endpoints may timeout on appliances with many users or repositories. Retrying the request until data is returned is advised. + - | + When following the steps for [Replacing the primary MySQL node](/admin/monitoring-managing-and-updating-your-instance/configuring-clustering/replacing-a-cluster-node#replacing-the-primary-mysql-node), step 14 (running `ghe-cluster-config-apply`) might fail with errors. If this occurs, re-running `ghe-cluster-config-apply` is expected to succeed. + - | + Running a `config apply` as part of the steps for [Replacing a node in an emergency](/admin/monitoring-managing-and-updating-your-instance/configuring-clustering/replacing-a-cluster-node#replacing-a-node-in-an-emergency) may fail with errors if the node being replaced is still reachable. If this occurs, shutdown the node and repeat the steps. + - | + {% data reusables.release-notes.2024-06-possible-frontend-5-minute-outage-during-hotpatch-upgrade %} + - | + When restoring data originally backed up from a 3.13 appliance onto a 3.13 appliance, the Elasticsearch indices need to be reindexed before some of the data will show up. This happens via a nightly scheduled job. It can also be forced by running `/usr/local/share/enterprise/ghe-es-search-repair`. + - | + An organization-level code scanning configuration page is displayed on instances that do not use GitHub Advanced Security or code scanning. + - | + In the header bar displayed to site administrators, some icons are not available. + - | + When enabling automatic update checks for the first time in the Management Console, the status is not dynamically reflected until the "Updates" page is reloaded. + - | + When restoring from a backup snapshot, a large number of `mapper_parsing_exception` errors may be displayed. + - | + Services may respond with a `503` status due to an out of date `haproxy` configuration. This can usually be resolved with a `ghe-config-apply` run. diff --git a/data/reusables/release-notes/2023-10-git-push-made-but-not-registered.md b/data/reusables/release-notes/2023-10-git-push-made-but-not-registered.md index 4c4e63915e5c..1deff098f8ba 100644 --- a/data/reusables/release-notes/2023-10-git-push-made-but-not-registered.md +++ b/data/reusables/release-notes/2023-10-git-push-made-but-not-registered.md @@ -5,6 +5,6 @@ CA certificate key too weak ``` To resolve this issue, confirm that your certificate complies -with level 2 of the OpenSSL security specification. For more information, see [SSL_CTX_set_security_level](https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_security_level.html#DEFAULT-CALLBACK-BEHAVIOUR) in the OpenSSL docs. For more information about reviewing your instance's logs, see "[AUTOTITLE](/admin/monitoring-and-managing-your-instance/monitoring-your-instance/about-system-logs#system-logs-in-the-systemd-journal)". +with level 2 of the OpenSSL security specification. For more information, see [SSL_CTX_set_security_level](https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_security_level.html#DEFAULT-CALLBACK-BEHAVIOUR) in the OpenSSL docs. For more information about reviewing your instance's logs, see "[AUTOTITLE](/admin/monitoring-and-managing-your-instance/monitoring-your-instance/about-system-logs#system-logs-in-the-systemd-journal)." If the error appears in `babeld` logs because your TLS certificate does not comply with level 2 of the specification, you must create and upload a new certificate with stronger security before you upgrade to GitHub Enterprise Server 3.10 or later. For more information, see "[AUTOTITLE](/admin/configuration/hardening-security-for-your-enterprise/configuring-tls)." From 97f2feb8d50ac46ee68d82581f8108e413c4e856 Mon Sep 17 00:00:00 2001 From: Ashley Date: Thu, 7 Nov 2024 13:02:18 -0500 Subject: [PATCH 08/12] Content Linter Rule - Whitespace (#52622) Co-authored-by: Rachael Sewell --- .../contributing/content-linter-rules.md | 3 +- src/content-linter/lib/linting-rules/index.js | 2 + .../linting-rules/liquid-tag-whitespace.js | 64 +++++++++++++++++ src/content-linter/style/github-docs.js | 6 ++ .../tests/unit/liquid-tag-whitespace.js | 71 +++++++++++++++++++ 5 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 src/content-linter/lib/linting-rules/liquid-tag-whitespace.js create mode 100644 src/content-linter/tests/unit/liquid-tag-whitespace.js diff --git a/data/reusables/contributing/content-linter-rules.md b/data/reusables/contributing/content-linter-rules.md index 8733117230b9..82d8de234160 100644 --- a/data/reusables/contributing/content-linter-rules.md +++ b/data/reusables/contributing/content-linter-rules.md @@ -64,4 +64,5 @@ | GHD038 | expired-content | Expired content must be remediated. | error | expired | | GHD039 | expiring-soon | Content that expires soon should be proactively addressed. | warning | expired | | [GHD040](https://github.com/github/docs/blob/main/src/content-linter/README.md) | table-liquid-versioning | Tables must use the correct liquid versioning format | error | tables | -| GHD041 | third-party-action-pinning | Code examples that use third-party actions must always pin to a full length commit SHA | error | feature, actions | \ No newline at end of file +| GHD041 | third-party-action-pinning | Code examples that use third-party actions must always pin to a full length commit SHA | error | feature, actions | +| GHD042 | liquid-tag-whitespace | Liquid tags should start and end with one whitespace. Liquid tag arguments should be separated by only one whitespace. | error | liquid, format | \ No newline at end of file diff --git a/src/content-linter/lib/linting-rules/index.js b/src/content-linter/lib/linting-rules/index.js index b361473375d4..6c37c8f6016a 100644 --- a/src/content-linter/lib/linting-rules/index.js +++ b/src/content-linter/lib/linting-rules/index.js @@ -31,6 +31,7 @@ import { imageNoGif } from './image-no-gif.js' import { expiredContent, expiringSoon } from './expired-content.js' import { tableLiquidVersioning } from './table-liquid-versioning.js' import { thirdPartyActionPinning } from './third-party-action-pinning.js' +import { liquidTagWhitespace } from './liquid-tag-whitespace.js' const noDefaultAltText = markdownlintGitHub.find((elem) => elem.names.includes('no-default-alt-text'), @@ -77,5 +78,6 @@ export const gitHubDocsMarkdownlint = { expiringSoon, tableLiquidVersioning, thirdPartyActionPinning, + liquidTagWhitespace, ], } diff --git a/src/content-linter/lib/linting-rules/liquid-tag-whitespace.js b/src/content-linter/lib/linting-rules/liquid-tag-whitespace.js new file mode 100644 index 000000000000..9abaa94913c7 --- /dev/null +++ b/src/content-linter/lib/linting-rules/liquid-tag-whitespace.js @@ -0,0 +1,64 @@ +import { TokenKind } from 'liquidjs' + +import { getLiquidTokens, getPositionData } from '../helpers/liquid-utils.js' +import { addFixErrorDetail } from '../helpers/utils.js' + +/* +Liquid tags should start and end with one whitespace. For example: + + DO use a single whitespace character + {% data %} + + DON'T use 0 or more than 1 whitespace + {%data %} + + DON'T use more than 1 whitespace between args + {%data arg1 arg2 %} +*/ + +export const liquidTagWhitespace = { + names: ['GHD042', 'liquid-tag-whitespace'], + description: + 'Liquid tags should start and end with one whitespace. Liquid tag arguments should be separated by only one whitespace.', + tags: ['liquid', 'format'], + function: (params, onError) => { + const content = params.lines.join('\n') + const tokens = getLiquidTokens(content).filter((token) => token.kind === TokenKind.Tag) + for (const token of tokens) { + const { lineNumber, column, length } = getPositionData(token, params.lines) + + const range = [column, length] + const tag = params.lines[lineNumber - 1].slice(column - 1, column - 1 + length) + + // Get just the opening and closing tags, which includes any whitespace + // added before the tag name or any arguments + const openTag = tag.slice(0, token.contentRange[0] - token.begin) + const closeTag = tag.slice(-(token.end - token.contentRange[1])) + + const isOpenTagOneSpace = openTag !== openTag.trim() + ' ' + const isCloseTagOneSpace = closeTag !== ' ' + closeTag.trim() + + const moreThanOneSpace = /\s{2,}/ + const isArgOneSpace = moreThanOneSpace.test(tag) + + const fixedContent = + openTag.trim() + ' ' + token.content.replace(moreThanOneSpace, ' ') + ' ' + closeTag.trim() + + if (isOpenTagOneSpace || isCloseTagOneSpace || isArgOneSpace) { + addFixErrorDetail( + onError, + lineNumber, + fixedContent, + params.lines[lineNumber - 1].slice(column - 1, column - 1 + length), + range, + { + lineNumber, + editColumn: column, + deleteCount: length, + insertText: fixedContent, + }, + ) + } + } + }, +} diff --git a/src/content-linter/style/github-docs.js b/src/content-linter/style/github-docs.js index d2db8d18d50c..ca9119db3510 100644 --- a/src/content-linter/style/github-docs.js +++ b/src/content-linter/style/github-docs.js @@ -161,6 +161,12 @@ const githubDocsConfig = { 'partial-markdown-files': true, 'yml-files': true, }, + 'liquid-tag-whitespace': { + // GHD042 + severity: 'error', + 'partial-markdown-files': true, + 'yml-files': true, + }, } export const githubDocsFrontmatterConfig = { diff --git a/src/content-linter/tests/unit/liquid-tag-whitespace.js b/src/content-linter/tests/unit/liquid-tag-whitespace.js new file mode 100644 index 000000000000..b942c1573253 --- /dev/null +++ b/src/content-linter/tests/unit/liquid-tag-whitespace.js @@ -0,0 +1,71 @@ +import { describe, expect, test } from 'vitest' + +import { runRule } from '../../lib/init-test.js' +import { liquidTagWhitespace } from '../../lib/linting-rules/liquid-tag-whitespace.js' + +describe(liquidTagWhitespace.names.join(' - '), () => { + test('liquid tags with correct whitespace pass', async () => { + const markdown = [ + '{% data variables.location.product_location %}', + '{% assign my_variable = "value" %}', + '{% if user %}Hello, {{ user.name }}{% endif %}', + ].join('\n') + + const result = await runRule(liquidTagWhitespace, { strings: { markdown } }) + const errors = result.markdown + expect(errors.length).toBe(0) + }) + + test('liquid tags with incorrect whitespace fail', async () => { + const markdown = [ + '{%data variables.location.product_location %}', + '{% assign my_variable = "value"%}', + '{% if user %}Hello, {{ user.name }} {%endif %}', + '{% data variables.location.product_location %}', + '{%-data variables.location.product_location -%}', + '{%- assign my_variable = "value"-%}', + '{%- if user -%}Hello, {{ user.name }} {%endif %}', + '{%- data variables.location.product_location -%}', + ].join('\n') + + const result = await runRule(liquidTagWhitespace, { strings: { markdown } }) + const errors = result.markdown + expect(errors.length).toBe(8) + expect(errors[2].lineNumber).toBe(3) + expect(errors[2].fixInfo).toEqual({ + deleteCount: 10, + editColumn: 37, + lineNumber: 3, + insertText: '{% endif %}', + }) + }) + + test('liquid tags with multiple spaces between arguments fail', async () => { + const markdown = [ + '{% assign my_variable = "value" %}', + '{% if user %}Hello, {{ user.name }}{% endif %}', + ].join('\n') + + const result = await runRule(liquidTagWhitespace, { strings: { markdown } }) + const errors = result.markdown + expect(errors.length).toBe(2) + expect(errors[1].lineNumber).toBe(2) + expect(errors[0].fixInfo).toEqual({ + deleteCount: 35, + editColumn: 1, + lineNumber: 1, + insertText: '{% assign my_variable = "value" %}', + }) + }) + + test('liquid tags with single spaces between arguments pass', async () => { + const markdown = [ + '{% assign my_variable = "value" %}', + '{% if user %}Hello, {{ user.name }}{% endif %}', + ].join('\n') + + const result = await runRule(liquidTagWhitespace, { strings: { markdown } }) + const errors = result.markdown + expect(errors.length).toBe(0) + }) +}) From c0c5b6aef011ef6a6afcdab8176dbf197f9548f2 Mon Sep 17 00:00:00 2001 From: Evan Bonsignori Date: Thu, 7 Nov 2024 10:14:18 -0800 Subject: [PATCH 09/12] Populate `.env.example` with examples and instructions (#52912) --- .env.example | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/.env.example b/.env.example index e69de29bb2d1..2387db4d6922 100644 --- a/.env.example +++ b/.env.example @@ -0,0 +1,23 @@ +# This file is a template for what your untracked .env file might look like for local development. +# Please copy this to a new .env file and fill in the values as needed. + +# Requires a running local Elasticsearch service. Can be started via Docker, see https://github.com/github/docs-engineering/blob/main/docs/elasticsearch/elasticsearch-locally.md +# When this value is unset searches will be proxied to the production Elasticsearch endpoint +ELASTICSEARCH_URL=http://localhost:9200 + +# Set for sending events in local development. See https://github.com/github/docs-engineering/blob/main/docs/analytics/hydro-mock.md +HYDRO_ENDPOINT= +HYDRO_SECRET= + +# Localization variables +# See https://github.com/github/docs-internal/tree/main/src/languages#working-with-translated-content-locally +ENABLED_LANGUAGES= +TRANSLATIONS_ROOT= + +# For running the src/search/scripts/scrape script +# You may want a lower value depending on your CPU +BUILD_RECORDS_MAX_CONCURRENT=100 +BUILD_RECORDS_MIN_TIME= + +# Set to true to enable the /fastly-cache-test route for debugging Fastly headers +ENABLE_FASTLY_TESTING= \ No newline at end of file From 1a99ce6a37b48e81d611496e79f83a10d33b7895 Mon Sep 17 00:00:00 2001 From: Evan Bonsignori Date: Thu, 7 Nov 2024 10:15:57 -0800 Subject: [PATCH 10/12] `src/search` refactor + new endpoint: AI Search Autocomplete (#52822) --- ...arch.yml => index-autocomplete-search.yml} | 17 +- ...rch-pr.yml => index-general-search-pr.yml} | 23 +- ...ticsearch.yml => index-general-search.yml} | 12 +- .gitignore | 6 + package-lock.json | 27 + package.json | 20 +- src/fixtures/tests/breadcrumbs.ts | 4 +- src/frame/middleware/api.ts | 2 +- src/frame/middleware/index.ts | 4 +- src/frame/tests/favicons.ts | 12 +- src/frame/tests/manifest.ts | 3 + .../lib/release-templates/release-steps-1.md | 8 +- src/languages/tests/frame.ts | 6 +- .../scripts/rendered-content-link-checker.ts | 20 +- src/search/README.md | 66 +- src/search/components/Aggregations.tsx | 3 +- src/search/components/SearchResults.tsx | 29 +- src/search/components/ValidationErrors.tsx | 4 +- .../components/context/SearchContext.tsx | 7 +- src/search/components/index.tsx | 11 +- src/search/components/types.ts | 59 +- src/search/lib/config.js | 5 - src/search/lib/elasticsearch-indexes.ts | 91 +++ src/search/lib/elasticsearch-versions.ts | 107 ++++ .../ai-search-autocomplete.ts | 125 ++++ .../general-autocomplete.ts | 100 +++ .../general-search.ts} | 364 ++++------- .../helpers/elasticsearch-highlight-config.ts | 86 +++ .../lib/get-elasticsearch-results/types.ts | 23 + src/search/lib/helpers/get-client.ts | 31 + src/search/lib/helpers/old-version-logic.ts | 44 ++ src/search/lib/helpers/strings.ts | 10 + .../lib/utils.ts => lib/helpers/time.ts} | 25 + .../get-search-from-request-params.ts | 96 +++ .../search-params-objects.ts | 153 +++++ src/search/lib/search-request-params/types.ts | 52 ++ src/search/middleware/contextualize.js | 153 ----- .../middleware/general-search-middleware.ts | 174 ++++++ src/search/middleware/get-search-request.js | 229 ------- src/search/middleware/search-routes.ts | 150 +++++ src/search/middleware/search.js | 160 ----- src/search/pages/search.tsx | 25 +- .../{analyze-text.js => analyze-text.ts} | 86 +-- src/search/scripts/index-elasticsearch.js | 575 ------------------ src/search/scripts/index-test-fixtures.sh | 9 +- src/search/scripts/index/README.md | 24 + .../scripts/index/index-autocomplete.ts | 167 ----- src/search/scripts/index/index-cli.ts | 158 +++++ src/search/scripts/index/index.ts | 44 -- src/search/scripts/index/lib/get-client.ts | 27 - .../index/lib/index-ai-search-autocomplete.ts | 112 ++++ .../index/lib/index-general-autocomplete.ts | 134 ++++ .../scripts/index/lib/index-general-search.ts | 145 +++++ src/search/scripts/index/lib/populate.ts | 107 ---- src/search/scripts/index/lib/repoint-alias.ts | 77 --- src/search/scripts/index/types.ts | 57 +- src/search/scripts/index/utils/constants.ts | 11 + .../utils/indexing-elasticsearch-utils.ts | 178 ++++++ src/search/scripts/index/utils/mappings.ts | 52 ++ .../{lib => utils}/retry-on-error-test.ts | 6 +- src/search/scripts/index/utils/settings.ts | 118 ++++ src/search/scripts/retry-on-error-test.js | 76 --- src/search/scripts/scrape/README.md | 40 ++ .../lib/build-records.ts} | 40 +- .../{domwaiter.js => scrape/lib/domwaiter.ts} | 37 +- .../lib/find-indexable-pages.ts} | 9 +- .../lib/parse-page-sections-into-records.ts} | 14 +- .../lib/popular-pages.ts} | 38 +- .../lib/scrape-into-index-json.ts} | 42 +- .../lib/search-index-records.ts} | 35 +- .../scrape-cli.ts} | 80 +-- src/search/scripts/scrape/types.ts | 70 +++ src/search/scripts/search-index-records.js | 17 - .../tests/api-ai-search-autocomplete.ts | 164 +++++ ....js => api-general-autocomplete-search.ts} | 59 +- .../tests/{api-search.js => api-search.ts} | 184 +++--- .../queries/en/enterprise-cloud/queries.json | 52 ++ .../queries/en/free-pro-team/queries.json | 52 ++ ...b-docs_general-search_fpt_en-records.json} | 0 ...b-docs_general-search_fpt_ja-records.json} | 0 ...-docs_general-search_ghec_en-records.json} | 0 ...-docs_general-search_ghec_ja-records.json} | 0 ...js => parse-page-sections-into-records.ts} | 75 +-- .../tests/{rendering.js => rendering.ts} | 12 +- src/search/tests/search.js | 37 -- src/search/tests/search.ts | 40 ++ src/search/tests/topics.js | 39 -- src/search/tests/topics.ts | 44 ++ src/search/types.ts | 76 +++ src/tests/README.md | 4 + src/tests/helpers/e2etest-ts.ts | 181 ++++++ tsconfig.json | 1 + 92 files changed, 3697 insertions(+), 2454 deletions(-) rename .github/workflows/{index-autocomplete-elasticsearch.yml => index-autocomplete-search.yml} (69%) rename .github/workflows/{sync-search-pr.yml => index-general-search-pr.yml} (81%) rename .github/workflows/{sync-search-elasticsearch.yml => index-general-search.yml} (94%) delete mode 100644 src/search/lib/config.js create mode 100644 src/search/lib/elasticsearch-indexes.ts create mode 100644 src/search/lib/elasticsearch-versions.ts create mode 100644 src/search/lib/get-elasticsearch-results/ai-search-autocomplete.ts create mode 100644 src/search/lib/get-elasticsearch-results/general-autocomplete.ts rename src/search/{middleware/es-search.js => lib/get-elasticsearch-results/general-search.ts} (59%) create mode 100644 src/search/lib/get-elasticsearch-results/helpers/elasticsearch-highlight-config.ts create mode 100644 src/search/lib/get-elasticsearch-results/types.ts create mode 100644 src/search/lib/helpers/get-client.ts create mode 100644 src/search/lib/helpers/old-version-logic.ts create mode 100644 src/search/lib/helpers/strings.ts rename src/search/{scripts/index/lib/utils.ts => lib/helpers/time.ts} (56%) create mode 100644 src/search/lib/search-request-params/get-search-from-request-params.ts create mode 100644 src/search/lib/search-request-params/search-params-objects.ts create mode 100644 src/search/lib/search-request-params/types.ts delete mode 100644 src/search/middleware/contextualize.js create mode 100644 src/search/middleware/general-search-middleware.ts delete mode 100644 src/search/middleware/get-search-request.js create mode 100644 src/search/middleware/search-routes.ts delete mode 100644 src/search/middleware/search.js rename src/search/scripts/{analyze-text.js => analyze-text.ts} (61%) delete mode 100755 src/search/scripts/index-elasticsearch.js create mode 100644 src/search/scripts/index/README.md delete mode 100644 src/search/scripts/index/index-autocomplete.ts create mode 100644 src/search/scripts/index/index-cli.ts delete mode 100644 src/search/scripts/index/index.ts delete mode 100644 src/search/scripts/index/lib/get-client.ts create mode 100644 src/search/scripts/index/lib/index-ai-search-autocomplete.ts create mode 100644 src/search/scripts/index/lib/index-general-autocomplete.ts create mode 100644 src/search/scripts/index/lib/index-general-search.ts delete mode 100644 src/search/scripts/index/lib/populate.ts delete mode 100644 src/search/scripts/index/lib/repoint-alias.ts create mode 100644 src/search/scripts/index/utils/constants.ts create mode 100644 src/search/scripts/index/utils/indexing-elasticsearch-utils.ts create mode 100644 src/search/scripts/index/utils/mappings.ts rename src/search/scripts/index/{lib => utils}/retry-on-error-test.ts (97%) create mode 100644 src/search/scripts/index/utils/settings.ts delete mode 100644 src/search/scripts/retry-on-error-test.js create mode 100644 src/search/scripts/scrape/README.md rename src/search/scripts/{build-records.js => scrape/lib/build-records.ts} (75%) rename src/search/scripts/{domwaiter.js => scrape/lib/domwaiter.ts} (50%) rename src/search/scripts/{find-indexable-pages.js => scrape/lib/find-indexable-pages.ts} (70%) rename src/search/scripts/{parse-page-sections-into-records.js => scrape/lib/parse-page-sections-into-records.ts} (91%) rename src/search/scripts/{popular-pages.js => scrape/lib/popular-pages.ts} (61%) rename src/search/scripts/{sync.js => scrape/lib/scrape-into-index-json.ts} (64%) rename src/search/scripts/{validate-records.js => scrape/lib/search-index-records.ts} (61%) rename src/search/scripts/{sync-search-indices.js => scrape/scrape-cli.ts} (64%) mode change 100755 => 100644 create mode 100644 src/search/scripts/scrape/types.ts delete mode 100644 src/search/scripts/search-index-records.js create mode 100644 src/search/tests/api-ai-search-autocomplete.ts rename src/search/tests/{api-autocomplete-search.js => api-general-autocomplete-search.ts} (62%) rename src/search/tests/{api-search.js => api-search.ts} (66%) create mode 100644 src/search/tests/fixtures/data/ai/search/queries/en/enterprise-cloud/queries.json create mode 100644 src/search/tests/fixtures/data/ai/search/queries/en/free-pro-team/queries.json rename src/search/tests/fixtures/search-indexes/{github-docs-dotcom-en-records.json => tests_github-docs_general-search_fpt_en-records.json} (100%) rename src/search/tests/fixtures/search-indexes/{github-docs-dotcom-ja-records.json => tests_github-docs_general-search_fpt_ja-records.json} (100%) rename src/search/tests/fixtures/search-indexes/{github-docs-ghec-en-records.json => tests_github-docs_general-search_ghec_en-records.json} (100%) rename src/search/tests/fixtures/search-indexes/{github-docs-ghec-ja-records.json => tests_github-docs_general-search_ghec_ja-records.json} (100%) rename src/search/tests/{parse-page-sections-into-records.js => parse-page-sections-into-records.ts} (63%) rename src/search/tests/{rendering.js => rendering.ts} (93%) delete mode 100644 src/search/tests/search.js create mode 100644 src/search/tests/search.ts delete mode 100644 src/search/tests/topics.js create mode 100644 src/search/tests/topics.ts create mode 100644 src/search/types.ts create mode 100644 src/tests/helpers/e2etest-ts.ts diff --git a/.github/workflows/index-autocomplete-elasticsearch.yml b/.github/workflows/index-autocomplete-search.yml similarity index 69% rename from .github/workflows/index-autocomplete-elasticsearch.yml rename to .github/workflows/index-autocomplete-search.yml index a316812c81e4..da837deec763 100644 --- a/.github/workflows/index-autocomplete-elasticsearch.yml +++ b/.github/workflows/index-autocomplete-search.yml @@ -1,7 +1,7 @@ -name: Index autocomplete Elasticsearch +name: Index autocomplete search in Elasticsearch -# **What it does**: Indexes autocomplete data into Elasticsearch. -# **Why we have it**: So we can power the API for autocomplete. +# **What it does**: Indexes autocomplete data (general and AI search) into Elasticsearch. +# **Why we have it**: So we can power the APIs for autocomplete. # **Who does it impact**: docs-engineering on: @@ -10,7 +10,7 @@ on: - cron: '20 16 * * *' # Run every day at 16:20 UTC / 8:20 PST pull_request: paths: - - .github/workflows/index-autocomplete-elasticsearch.yml + - .github/workflows/index-autocomplete-search.yml - 'src/search/scripts/index/**' - 'package*.json' @@ -40,10 +40,15 @@ jobs: if: ${{ github.event_name == 'pull_request' }} run: curl --fail --retry-connrefused --retry 5 -I http://localhost:9200 - - name: Run indexing + - name: Run general auto-complete indexing env: ELASTICSEARCH_URL: ${{ github.event_name == 'pull_request' && 'http://localhost:9200' || secrets.ELASTICSEARCH_URL }} - run: npm run index -- autocomplete docs-internal-data + run: npm run index-general-autocomplete -- docs-internal-data + + - name: Run AI search auto-complete indexing + env: + ELASTICSEARCH_URL: ${{ github.event_name == 'pull_request' && 'http://localhost:9200' || secrets.ELASTICSEARCH_URL }} + run: npm run index-ai-search-autocomplete -- docs-internal-data - uses: ./.github/actions/slack-alert if: ${{ failure() && github.event_name == 'schedule' }} diff --git a/.github/workflows/sync-search-pr.yml b/.github/workflows/index-general-search-pr.yml similarity index 81% rename from .github/workflows/sync-search-pr.yml rename to .github/workflows/index-general-search-pr.yml index f7d504a77f6f..3f819ce556af 100644 --- a/.github/workflows/sync-search-pr.yml +++ b/.github/workflows/index-general-search-pr.yml @@ -1,6 +1,6 @@ -name: Sync search - PR +name: Index general search in Elasticsearch on PR -# **What it does**: This does what `sync-sarch-elasticsearch.yml` does but +# **What it does**: This does what `index-general-search-elasticsearch.yml` does but # with a localhost Elasticsearch and only for English. # **Why we have it**: To test that the script works and the popular pages json is valid. # **Who does it impact**: Docs engineering @@ -11,8 +11,8 @@ on: paths: - 'src/search/**' - 'package*.json' - # Ultimately, for debugging this workflow itself - - .github/workflows/sync-search-pr.yml + # For debugging this workflow + - .github/workflows/index-general-search-pr.yml # Make sure we run this if the composite action changes - .github/actions/setup-elasticsearch/action.yml @@ -25,9 +25,6 @@ concurrency: cancel-in-progress: true env: - # Yes, it's hardcoded but it makes all the steps look exactly the same - # as they do in `sync-search-elasticsearch.yml` where it uses - # that `${{ env.ELASTICSEARCH_URL }}` ELASTICSEARCH_URL: http://localhost:9200 # Since we'll run in NDOE_ENV=production, we need to be explicit that # we don't want Hydro configured. @@ -63,7 +60,7 @@ jobs: env: ENABLE_DEV_LOGGING: false run: | - npm run sync-search-server > /tmp/stdout.log 2> /tmp/stderr.log & + npm run general-search-scrape-server > /tmp/stdout.log 2> /tmp/stderr.log & # first sleep to give it a chance to start sleep 6 @@ -88,15 +85,13 @@ jobs: # let's just accept an empty string instead. THROW_ON_EMPTY: false - # The sync-search-index recognizes this env var if you don't - # use the `--docs-internal-data ` option. DOCS_INTERNAL_DATA: docs-internal-data run: | mkdir /tmp/records - npm run sync-search-indices -- /tmp/records \ + npm run general-search-scrape -- /tmp/records \ --language en \ - --version dotcom + --version fpt ls -lh /tmp/records @@ -106,9 +101,9 @@ jobs: - name: Index into Elasticsearch run: | - npm run index-elasticsearch -- /tmp/records \ + npm run index-general-search -- /tmp/records \ --language en \ - --version dotcom + --version fpt - name: Check created indexes and aliases run: | diff --git a/.github/workflows/sync-search-elasticsearch.yml b/.github/workflows/index-general-search.yml similarity index 94% rename from .github/workflows/sync-search-elasticsearch.yml rename to .github/workflows/index-general-search.yml index 4ca84e08993f..0f175cf2c675 100644 --- a/.github/workflows/sync-search-elasticsearch.yml +++ b/.github/workflows/index-general-search.yml @@ -1,4 +1,4 @@ -name: Sync search Elasticsearch +name: Index general search in Elasticsearch # **What it does**: It scrapes the whole site and dumps the records in a # temp directory. Then it indexes that into Elasticsearch. @@ -140,7 +140,7 @@ jobs: env: ENABLE_DEV_LOGGING: false run: | - npm run sync-search-server > /tmp/stdout.log 2> /tmp/stderr.log & + npm run general-search-scrape-server > /tmp/stdout.log 2> /tmp/stderr.log & # first sleep to give it a chance to start sleep 6 @@ -169,13 +169,11 @@ jobs: # the same as not set within the script. VERSION: ${{ inputs.version }} - # The sync-search-index recognizes this env var if you don't - # use the `--docs-internal-data ` option. DOCS_INTERNAL_DATA: docs-internal-data run: | mkdir /tmp/records - npm run sync-search-indices -- /tmp/records \ + npm run general-search-scrape -- /tmp/records \ --language ${{ matrix.language }} ls -lh /tmp/records @@ -186,12 +184,12 @@ jobs: - name: Index into Elasticsearch env: - # Must match what we used when scraping (npm run sync-search-indices) + # Must match what we used when scraping (npm run general-search-scrape) # otherwise the script will seek other versions from disk that might # not exist. VERSION: ${{ inputs.version }} run: | - npm run index-elasticsearch -- /tmp/records \ + npm run index-general-search -- /tmp/records \ --language ${{ matrix.language }} \ --stagger-seconds 5 \ --retries 5 diff --git a/.gitignore b/.gitignore index 218dc0efa2cf..0591ebbdb049 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,9 @@ assets/images/help/writing/unordered-list-rendered (1).png # Used by precompute-pageinfo .pageinfo-cache.json.br + +# Cloned and used for indexing Elasticsearch data +docs-internal-data/ + +# For intermediate data (like scraping for Elasticsearch indexing) +tmp/ \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index ccebe9df887f..44c695262210 100644 --- a/package-lock.json +++ b/package-lock.json @@ -109,10 +109,13 @@ "@octokit/rest": "21.0.2", "@playwright/test": "^1.48.1", "@types/accept-language-parser": "1.5.6", + "@types/cheerio": "^0.22.35", "@types/connect-datadog": "0.0.10", "@types/connect-timeout": "0.0.39", "@types/cookie": "0.6.0", "@types/cookie-parser": "1.4.7", + "@types/elasticsearch": "^5.0.43", + "@types/event-to-promise": "^0.7.5", "@types/express": "4.17.21", "@types/imurmurhash": "^0.1.4", "@types/js-cookie": "^3.0.6", @@ -3165,6 +3168,15 @@ "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==", "dev": true }, + "node_modules/@types/cheerio": { + "version": "0.22.35", + "resolved": "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.35.tgz", + "integrity": "sha512-yD57BchKRvTV+JD53UZ6PD8KWY5g5rvvMLRnZR3EQBCZXiDT/HR+pKpMzFGlWNhFrXlo7VPZXtKvIEwZkAWOIA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -3228,12 +3240,27 @@ "@types/ms": "*" } }, + "node_modules/@types/elasticsearch": { + "version": "5.0.43", + "resolved": "https://registry.npmjs.org/@types/elasticsearch/-/elasticsearch-5.0.43.tgz", + "integrity": "sha512-N+MpzURpDCWd7zaJ7CE1aU+nBSeAABLhDE0lGodQ0LLftx7ku6hjTXLr9OAFZLSXiWL3Xxx8jts485ynrcm5NA==", + "dev": true + }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, + "node_modules/@types/event-to-promise": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@types/event-to-promise/-/event-to-promise-0.7.5.tgz", + "integrity": "sha512-h10M3ybTySQFVP4N1uiEgPwbpHExNS8UMpCqRUJFkMhlpgSlWsyYsGMmkrJIKRnhGfYDOb4LD3U+SSPujoMHNA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/express": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", diff --git a/package.json b/package.json index 17ae073dc21c..569dfee80c0b 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "exports": "./src/frame/server.ts", "scripts": { "all-documents": "tsx src/content-render/scripts/all-documents/cli.ts", - "analyze-text": "node src/search/scripts/analyze-text.js", + "analyze-text": "tsx src/search/scripts/analyze-text.ts", "analyze-comment": "tsx src/events/scripts/analyze-comment-cli.ts", "archive-version": "tsx --max-old-space-size=16384 src/ghes-releases/scripts/archive-version.ts", "audit-log-sync": "tsx src/audit-logs/scripts/sync.ts", @@ -39,8 +39,14 @@ "find-unused-variables": "tsx src/content-linter/scripts/find-unsed-variables.ts", "fixture-dev": "cross-env ROOT=src/fixtures/fixtures npm start", "fixture-test": "cross-env ROOT=src/fixtures/fixtures npm test -- src/fixtures/tests", - "index": "tsx src/search/scripts/index/index.ts", - "index-elasticsearch": "node src/search/scripts/index-elasticsearch.js", + "general-search-scrape": "tsx src/search/scripts/scrape/scrape-cli.ts", + "general-search-scrape-server": "cross-env NODE_ENV=production PORT=4002 MINIMAL_RENDER=true CHANGELOG_DISABLED=true tsx src/frame/server.ts", + "ghes-release-scrape-with-server": "cross-env GHES_RELEASE=1 start-server-and-test general-search-scrape-server 4002 general-search-scrape", + "general-search-scrape-with-server": "cross-env NODE_OPTIONS='--max_old_space_size=8192' start-server-and-test general-search-scrape-server 4002 general-search-scrape", + "index": "tsx src/search/scripts/index/index-cli autocomplete docs-internal-data", + "index-ai-search-autocomplete": "tsx src/search/scripts/index/index-cli ai-search-autocomplete", + "index-general-autocomplete": "tsx src/search/scripts/index/index-cli general-autocomplete", + "index-general-search": "tsx src/search/scripts/index/index-cli general-search", "index-test-fixtures": "./src/search/scripts/index-test-fixtures.sh", "lint": "eslint '**/*.{js,mjs,ts,tsx}'", "lint-content": "node src/content-linter/scripts/lint-content.js", @@ -70,10 +76,6 @@ "start-for-playwright": "cross-env ROOT=src/fixtures/fixtures TRANSLATIONS_FIXTURE_ROOT=src/fixtures/fixtures/translations ENABLED_LANGUAGES=en,ja NODE_ENV=test tsx src/frame/server.ts", "symlink-from-local-repo": "node src/early-access/scripts/symlink-from-local-repo.js", "sync-rest": "tsx src/rest/scripts/update-files.ts", - "sync-search": "cross-env NODE_OPTIONS='--max_old_space_size=8192' start-server-and-test sync-search-server 4002 sync-search-indices", - "sync-search-ghes-release": "cross-env GHES_RELEASE=1 start-server-and-test sync-search-server 4002 sync-search-indices", - "sync-search-indices": "node src/search/scripts/sync-search-indices.js", - "sync-search-server": "cross-env NODE_ENV=production PORT=4002 MINIMAL_RENDER=true CHANGELOG_DISABLED=true tsx src/frame/server.ts", "sync-secret-scanning": "tsx src/secret-scanning/scripts/sync.ts", "sync-webhooks": "npx tsx src/rest/scripts/update-files.ts -o webhooks", "test": "vitest", @@ -222,6 +224,7 @@ "src/open-source/scripts/add-pr-links.js", "src/open-source/scripts/pr-link-source.js", "rest-api-description/", + "docs-internal-data/", "src/code-scanning/scripts/generate-code-scanning-query-list.ts" ] }, @@ -327,10 +330,13 @@ "@octokit/rest": "21.0.2", "@playwright/test": "^1.48.1", "@types/accept-language-parser": "1.5.6", + "@types/cheerio": "^0.22.35", "@types/connect-datadog": "0.0.10", "@types/connect-timeout": "0.0.39", "@types/cookie": "0.6.0", "@types/cookie-parser": "1.4.7", + "@types/elasticsearch": "^5.0.43", + "@types/event-to-promise": "^0.7.5", "@types/express": "4.17.21", "@types/imurmurhash": "^0.1.4", "@types/js-cookie": "^3.0.6", diff --git a/src/fixtures/tests/breadcrumbs.ts b/src/fixtures/tests/breadcrumbs.ts index e51a7d678dfb..41bc836ea1d5 100644 --- a/src/fixtures/tests/breadcrumbs.ts +++ b/src/fixtures/tests/breadcrumbs.ts @@ -68,7 +68,7 @@ describe('breadcrumbs', () => { expect($breadcrumbTitles.length).toBe(0) expect($breadcrumbLinks.length).toBe(2) - expect($breadcrumbLinks[0].attribs.title).toBe('Deeper secrets') - expect($breadcrumbLinks[1].attribs.title).toBe('Mariana Trench') + expect(($breadcrumbLinks[0] as cheerio.TagElement).attribs.title).toBe('Deeper secrets') + expect(($breadcrumbLinks[1] as cheerio.TagElement).attribs.title).toBe('Mariana Trench') }) }) diff --git a/src/frame/middleware/api.ts b/src/frame/middleware/api.ts index 1770a0b742ef..62d77ef61916 100644 --- a/src/frame/middleware/api.ts +++ b/src/frame/middleware/api.ts @@ -3,7 +3,7 @@ import { createProxyMiddleware } from 'http-proxy-middleware' import events from '@/events/middleware.js' import anchorRedirect from '@/rest/api/anchor-redirect.js' -import search from '@/search/middleware/search.js' +import search from '@/search/middleware/search-routes.js' import pageInfo from '@/pageinfo/middleware' import pageList from '@/pagelist/middleware' import webhooks from '@/webhooks/middleware/webhooks.js' diff --git a/src/frame/middleware/index.ts b/src/frame/middleware/index.ts index fe11bf133f59..d93377e09ed4 100644 --- a/src/frame/middleware/index.ts +++ b/src/frame/middleware/index.ts @@ -61,7 +61,7 @@ import fastlyCacheTest from './fastly-cache-test' import trailingSlashes from './trailing-slashes' import mockVaPortal from './mock-va-portal' import dynamicAssets from '@/assets/middleware/dynamic-assets' -import contextualizeSearch from '@/search/middleware/contextualize.js' +import generalSearchMiddleware from '@/search/middleware/general-search-middleware' import shielding from '@/shielding/middleware' import tracking from '@/tracking/middleware' import { MAX_REQUEST_TIMEOUT } from '@/frame/lib/constants.js' @@ -275,7 +275,7 @@ export default function (app: Express) { app.use(asyncMiddleware(productExamples)) app.use(asyncMiddleware(productGroups)) app.use(asyncMiddleware(glossaries)) - app.use(asyncMiddleware(contextualizeSearch)) + app.use(asyncMiddleware(generalSearchMiddleware)) app.use(asyncMiddleware(featuredLinks)) app.use(asyncMiddleware(learningTrack)) diff --git a/src/frame/tests/favicons.ts b/src/frame/tests/favicons.ts index 030b35fdd19f..28b873047233 100644 --- a/src/frame/tests/favicons.ts +++ b/src/frame/tests/favicons.ts @@ -15,7 +15,10 @@ describe('favicon assets', () => { expect(res.headers['cache-control']).toContain('public') expect(res.headers['cache-control']).toContain('immutable') expect(res.headers['cache-control']).toMatch(/max-age=\d+/) - const maxAgeSeconds = parseInt(res.headers['cache-control'].match(/max-age=(\d+)/)[1], 10) + const maxAgeSeconds = parseInt( + (res.headers['cache-control'] || '').match(/max-age=(\d+)/)?.[1] || '', + 10, + ) // Let's not be too specific in the tests, just as long as it's testing // that it's a reasonably large number of seconds. expect(maxAgeSeconds).toBeGreaterThanOrEqual(60 * 60) @@ -25,13 +28,16 @@ describe('favicon assets', () => { test('should serve a valid and aggressively caching /apple-touch-icon.png', async () => { const res = await get('/apple-touch-icon.png') expect(res.statusCode).toBe(200) - expect(parseInt(res.headers['content-length'], 10)).toBeGreaterThan(0) + expect(parseInt(res.headers['content-length'] || '', 10)).toBeGreaterThan(0) expect(res.headers['content-type']).toBe('image/png') expect(res.headers['set-cookie']).toBeUndefined() expect(res.headers['cache-control']).toContain('public') expect(res.headers['cache-control']).toContain('immutable') expect(res.headers['cache-control']).toMatch(/max-age=\d+/) - const maxAgeSeconds = parseInt(res.headers['cache-control'].match(/max-age=(\d+)/)[1], 10) + const maxAgeSeconds = parseInt( + (res.headers['cache-control'] || '').match(/max-age=(\d+)/)?.[1] || '', + 10, + ) // Let's not be too specific in the tests, just as long as it's testing // that it's a reasonably large number of seconds. expect(maxAgeSeconds).toBeGreaterThanOrEqual(60 * 60) diff --git a/src/frame/tests/manifest.ts b/src/frame/tests/manifest.ts index dcdc74eb5c2c..c1ba9e00fc58 100644 --- a/src/frame/tests/manifest.ts +++ b/src/frame/tests/manifest.ts @@ -20,6 +20,9 @@ describe('manifest', () => { test('download manifest from HTML and check content', async () => { const $ = await getDOM('/') const url = $('link[rel="manifest"]').attr('href') + if (!url) { + throw new Error('No manifest URL found') + } const res = await get(url) expect(res.statusCode).toBe(200) diff --git a/src/ghes-releases/lib/release-templates/release-steps-1.md b/src/ghes-releases/lib/release-templates/release-steps-1.md index 33ebc7b82fb7..4f01a44dc099 100644 --- a/src/ghes-releases/lib/release-templates/release-steps-1.md +++ b/src/ghes-releases/lib/release-templates/release-steps-1.md @@ -17,7 +17,7 @@ labels: - [Prerequisites](#prerequisites) - [Create publication branch for a new version of GHES](#creation) - [Resolve check failures](#check-failures) -- [Sync the search indices](#sync-search-indices) +- [Scrape the search indices](#scrape-search-indices) - [Maintain the publication branch](#maintenance) - [Complete preparation for the RC and publish the docset](#publication) @@ -110,11 +110,11 @@ For content from the OpenAPI schema, note the affected content with broken links
- + -### [🔎](#sync-search-indices) Sync the search indices +### [🔎](#scrape-search-indices) Scrape the search indices -1. Go to the [`sync-search-elasticsearch` workflow](https://github.com/github/docs-internal/actions/workflows/sync-search-elasticsearch.yml) ([permalink](https://github.com/github/docs-internal/blob/f8ca45703c48c7d1976a278337bc3391fb14fe9e/.github/workflows/sync-search-elasticsearch.yml) in case it moves) +1. Go to the [`index-general-search.yml` workflow](https://github.com/github/docs-internal/actions/workflows/index-general-search.yml) 1. Click on the **Run workflow** drop down and set the following parameters: - `Branch:` set to the name of the publication branch - `Version` set to the version you're publishing (e.g., `ghes-3.12` if you're publishing GHES 3.12) diff --git a/src/languages/tests/frame.ts b/src/languages/tests/frame.ts index d75ee9a484f1..e00c61fffd0b 100644 --- a/src/languages/tests/frame.ts +++ b/src/languages/tests/frame.ts @@ -17,15 +17,15 @@ describe('frame', () => { test.each(langs)('breadcrumbs link to %s pages', async (lang) => { const $ = await getDOM(`/${lang}/get-started/learning-about-github`) const $breadcrumbs = $('[data-testid=breadcrumbs-in-article] a') - expect($breadcrumbs[0].attribs.href).toBe(`/${lang}/get-started`) + expect(($breadcrumbs[0] as cheerio.TagElement).attribs.href).toBe(`/${lang}/get-started`) }) test.each(langs)('homepage links go to %s pages', async (lang) => { const $ = await getDOM(`/${lang}`) const $links = $('[data-testid=bump-link]') - $links.each((i: number, el: Element) => { + $links.each((i: number, el: cheerio.Element) => { const linkUrl = $(el).attr('href') - expect(linkUrl.startsWith(`/${lang}/`)).toBe(true) + expect((linkUrl || '').startsWith(`/${lang}/`)).toBe(true) }) }) diff --git a/src/links/scripts/rendered-content-link-checker.ts b/src/links/scripts/rendered-content-link-checker.ts index ba87dbd28a31..db4aef1c5964 100755 --- a/src/links/scripts/rendered-content-link-checker.ts +++ b/src/links/scripts/rendered-content-link-checker.ts @@ -3,7 +3,7 @@ import fs from 'fs' import path from 'path' -import cheerio, { type CheerioAPI, type Element } from 'cheerio' +import cheerio from 'cheerio' import coreLib from '@actions/core' import got, { RequestError } from 'got' import chalk from 'chalk' @@ -339,7 +339,15 @@ async function main( const t0 = new Date().getTime() const flawsGroups = await Promise.all( pages.map((page: Page) => - processPage(core, page, pageMap, redirects, opts, externalLinkCheckerDB, versions), + processPage( + core, + page, + pageMap, + redirects, + opts, + externalLinkCheckerDB, + versions as string[], + ), ), ) const t1 = new Date().getTime() @@ -695,13 +703,13 @@ async function processPermalink( } const $ = cheerio.load(html, { xmlMode: true }) const flaws: LinkFlaw[] = [] - const links: Element[] = [] + const links: cheerio.Element[] = [] $('a[href]').each((i, link) => { links.push(link) }) const newFlaws: LinkFlaw[] = await Promise.all( links.map(async (link) => { - const { href } = link.attribs + const { href } = (link as cheerio.TagElement).attribs // The global cache can't be used for anchor links because they // depend on each page it renders @@ -752,7 +760,7 @@ async function processPermalink( if (checkImages) { $('img[src]').each((i, img) => { - let { src } = img.attribs + let { src } = (img as cheerio.TagElement).attribs // Images get a cache-busting prefix injected in the image // E.g. @@ -874,7 +882,7 @@ let globalCacheMissCount = 0 async function checkHrefLink( core: any, href: string, - $: CheerioAPI, + $: cheerio.Root, redirects: Redirects, pageMap: PageMap, checkAnchors = false, diff --git a/src/search/README.md b/src/search/README.md index eef9a860a478..4532f3ebdc71 100644 --- a/src/search/README.md +++ b/src/search/README.md @@ -16,9 +16,36 @@ The site search is part of every version of docs.github.com. This endpoint respo You can also query our search endpoint directly at: `https://docs.github.com/search?version=&language=&query=` -- The VERSION can be any numbered supported GitHub Enterprise Server version (e.g., `3.12`), Enterprise Cloud (`ghec`), or the Free pro team plan (`dotcom`). -- The LANGUAGE CODE can be one of: `zh`, `es`, `pt`, `ru`, `ja`, `fr`, `de`, `ko` -- Any search QUERY you'd like. +- The `VERSION` can be any numbered supported GitHub Enterprise Server version (e.g., `3.12`), Enterprise Cloud (`ghec`), or the Free pro team plan (`dotcom`). +- The `LANGUAGE CODE` can be one of: `zh`, `es`, `pt`, `ru`, `ja`, `fr`, `de`, `ko` +- The `QUERY` can be any alphanumeric string value. + +## Types of search + +Our backend currently supports 3 "types" of searching. + +All searches accept a `query` param, e.g. `?query=how` and return results based on their type: + +1. **general search** + - Results: The pages of our sites that match the query, sorted by popularity + - Example: Query = "clone" -> Results + - Endpoint: `/api/search/v1` +2. **general autocomplete** + - Results: Potential terms that can be autocompleted from the query based on previous user searches + - Example: Query = "cl" -> A Result = "clone" + - Endpoint: `/api/search/autocomplete/v1` +3. **AI search autocomplete** + - Results: Human-readable full-sentence questions that best match the query. Questions are based on previous searches and popular pages + - Example: Query = "How do I clone" -> A Result = "How do I clone a repository?" + - Endpoint: `/api/search/ai-search-autocomplete/v1` + +## Elasticsearch + +Elasticsearch is an external service that we use for searching. When a user types a search, our backend queries Elasticsearch for the most relevant results. + +### Indexing Elasticsearch + +In order to provide relevant results to queries, we prefill Elasticsearch with data via Indexes. See the [Indexing README](./scripts/index/README.md) for how we index on Docs. ## Production deploys @@ -32,40 +59,25 @@ You can manually run the workflow to generate the indexes after you push your ch ### Build and sync -The preferred way to build and sync the search indices is to do so via the [GitHub Actions workflow](/.github/workflows/sync-search-elasticsearch.yml). +The preferred way to build and sync the search indices is to do so via the [GitHub Actions workflow](/.github/workflows/index-general-search.yml). ## Files ### Actions workflow files -- [`.github/workflows/sync-search-elasticsearch.yml`](/.github/workflows/sync-search-elasticsearch.yml) - Builds and syncs search indices on the `main` branch every four hours. Search indices are stored in an internal-only Elasticsearch instance. To run it manually, click "Run workflow" button in the Actions tab. +- [`.github/workflows/index-general-search.yml`](/.github/workflows/index-general-search.yml) - Populates search indices for **general search** using the `main` branch every four hours. Search indices are stored in an internal-only Elasticsearch instance. To run it manually, click "Run workflow" button in the Actions tab. +- [`.github/workflows/index-autocomplete-search.yml`](/.github/workflows/index-general-search.yml) - Populates search indices for both **general autocomplete** and **AI search autocomplete** using data from an internal repo. Runs daily. ### Notable code files and directories - [src/search/components/Search.tsx](/src/search/components/Search.tsx) - The browser-side code that enables the search. - [src/search/components/SearchResults.tsx](/src/search/components/SearchResults.tsx) - The browser-side code that displays search results. -- [src/search/middleware/es-search.js](/src/search/middleware/es-search.js) - A wrapper around the Node.js Elasticsearch module for interacting with the search API. -- [src/search/scripts/](/src/search/scripts/) - Scripts used by Actions workflows or for manual operations. -- [src/search/tests](/src/search/tests) - Tests! - -## Records - -Each record represents a page. Each record has `breadcrumbs`, `title`, `headings`, `content` (the article content in text, not HTML), `intro` (if one exists in the frontmatter), and a unique `objectID` that is currently just the permalink of the article. Here's an example: - -```json -{ - "objectID":"/en/actions/creating-actions/about-custom-actions", - "breadcrumbs":"GitHub Actions / Creating actions", - "title":"About custom actions", - "headings":"About custom actions\nTypes of actions\n[...]", - "content":"Actions are individual tasks that you can combine to create jobs and customize your workflow. You can create your own 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 GitHub community.", - "toplevel":"GitHub Actions", - "popularity":0 -} -``` - -## Notes +- [src/search/middleware/general-search-middleware.ts](src/search/middleware/general-search-middleware.ts) - Entrypoint to general search when you hit docs.github/search +- [src/search/middleware/search-routes](src/search/middleware/general-search-middleware.ts) - Entrypoint to the API endpoints for our search routes +- [src/search/scripts/](/src/search/scripts/) - Scripts used by Actions workflows or for manual operations like scraping data for indexing and performing the indexing. +- [src/search/tests](/src/search/tests) - Tests relevant to searching. + +## Miscellaneous Notes - It's not strictly necessary to set an `objectID` as the search index will create one automatically, but by creating our own we have a guarantee that subsequent invocations of this upload script will overwrite existing records instead of creating numerous duplicate records with differing IDs. - Our search querying has typo tolerance. Try spelling something wrong and see what you get! diff --git a/src/search/components/Aggregations.tsx b/src/search/components/Aggregations.tsx index cde2aadf7ba8..415acbde5e58 100644 --- a/src/search/components/Aggregations.tsx +++ b/src/search/components/Aggregations.tsx @@ -2,9 +2,10 @@ import { CheckboxGroup, Checkbox, FormControl } from '@primer/react' import { useRouter } from 'next/router' import Link from 'next/link' -import type { SearchResultAggregations } from './types' import { useTranslation } from 'src/languages/components/useTranslation' +import type { SearchResultAggregations } from 'src/search/types' + type Props = { aggregations: SearchResultAggregations } diff --git a/src/search/components/SearchResults.tsx b/src/search/components/SearchResults.tsx index 054e1a14e3e4..3e628b6a1370 100644 --- a/src/search/components/SearchResults.tsx +++ b/src/search/components/SearchResults.tsx @@ -4,30 +4,39 @@ import { useRouter } from 'next/router' import { useEffect, useState } from 'react' import cx from 'classnames' -import type { SearchResultsT, SearchResultHitT, SearchQueryT } from './types' import { useTranslation } from 'src/languages/components/useTranslation' import { Link } from 'src/frame/components/Link' import { sendEvent, EventType } from 'src/events/components/events' import styles from './SearchResults.module.scss' +import type { SearchQueryContentT } from 'src/search/components/types' +import type { GeneralSearchHitWithoutIncludes, GeneralSearchResponse } from 'src/search/types' +import type { SearchTotalHits } from '@elastic/elasticsearch/lib/api/types' + type Props = { - results: SearchResultsT - search: SearchQueryT + results: GeneralSearchResponse + searchParams: SearchQueryContentT } -export function SearchResults({ results, search }: Props) { - const pages = Math.ceil(results.meta.found.value / results.meta.size) +export function SearchResults({ results, searchParams }: Props) { + const pages = Math.ceil((results.meta.found as SearchTotalHits).value / results.meta.size) const { page } = results.meta return (
- + {pages > 1 && }
) } -function SearchResultHits({ hits, search }: { hits: SearchResultHitT[]; search: SearchQueryT }) { +function SearchResultHits({ + hits, + searchParams, +}: { + hits: GeneralSearchHitWithoutIncludes[] + searchParams: SearchQueryContentT +}) { return (
{hits.length === 0 && } @@ -35,10 +44,10 @@ function SearchResultHits({ hits, search }: { hits: SearchResultHitT[]; search: ))}
@@ -64,7 +73,7 @@ function SearchResultHit({ index, debug, }: { - hit: SearchResultHitT + hit: GeneralSearchHitWithoutIncludes query: string totalHits: number index: number diff --git a/src/search/components/ValidationErrors.tsx b/src/search/components/ValidationErrors.tsx index 3be3074b35fa..ce8bf15c5577 100644 --- a/src/search/components/ValidationErrors.tsx +++ b/src/search/components/ValidationErrors.tsx @@ -1,10 +1,10 @@ import { Flash } from '@primer/react' import { useTranslation } from 'src/languages/components/useTranslation' -import type { SearchValidationErrorT } from './types' +import type { SearchValidationErrorEntry } from '../types' interface Props { - errors: SearchValidationErrorT[] + errors: SearchValidationErrorEntry[] } export function ValidationErrors({ errors }: Props) { diff --git a/src/search/components/context/SearchContext.tsx b/src/search/components/context/SearchContext.tsx index 08ff25d14d97..086896cb8bce 100644 --- a/src/search/components/context/SearchContext.tsx +++ b/src/search/components/context/SearchContext.tsx @@ -1,10 +1,5 @@ import { createContext, useContext } from 'react' - -import type { SearchT } from '../types' - -export type SearchContextT = { - search: SearchT -} +import type { SearchContextT } from '../types' export const SearchContext = createContext(null) diff --git a/src/search/components/index.tsx b/src/search/components/index.tsx index 39399b3c08f7..26fc041be6a6 100644 --- a/src/search/components/index.tsx +++ b/src/search/components/index.tsx @@ -7,8 +7,9 @@ import { useNumberFormatter } from 'src/search/components/useNumberFormatter' import { SearchResults } from 'src/search/components/SearchResults' import { NoQuery } from 'src/search/components/NoQuery' import { useMainContext } from 'src/frame/components/context/MainContext' -import { ValidationErrors } from './ValidationErrors' -import { useSearchContext } from './context/SearchContext' +import { ValidationErrors } from 'src/search/components/ValidationErrors' +import { useSearchContext } from 'src/search/components/context/SearchContext' +import type { SearchTotalHits } from '@elastic/elasticsearch/lib/api/types' export function Search() { const { search } = useSearchContext() @@ -17,7 +18,7 @@ export function Search() { const { t } = useTranslation('search_results') const { currentVersion } = useVersion() - const { query } = search.search + const { query } = search.searchParams // A reference to the `content/search/index.md` Page object. // Not to be confused with the "page" that is for paginating @@ -37,7 +38,7 @@ export function Search() { pageTitle += ` (${searchVersion})` } if (results) { - pageTitle = `${formatInteger(results.meta.found.value)} ${pageTitle}` + pageTitle = `${formatInteger((results.meta.found as SearchTotalHits).value)} ${pageTitle}` } } @@ -63,7 +64,7 @@ export function Search() { ) : null} - {results ? : null} + {results ? : null} ) } diff --git a/src/search/components/types.ts b/src/search/components/types.ts index ce6c8a80ef77..1dad85ca9dec 100644 --- a/src/search/components/types.ts +++ b/src/search/components/types.ts @@ -1,58 +1,15 @@ -export type SearchResultHitT = { - id: string - url: string - title: string - breadcrumbs: string - highlights: { - title?: string[] - content?: string[] - content_explicit?: string[] - } - score?: number - popularity?: number - es_url?: string -} +import { GeneralSearchResponse, SearchValidationErrorEntry } from 'src/search/types' -type SearchResultsMeta = { - found: { - value: number - relation: string +export interface SearchContextT { + search: { + results?: GeneralSearchResponse + searchParams: SearchQueryContentT + validationErrors: SearchValidationErrorEntry[] } - took: { - query_msec: number - total_msec: number - } - page: number - size: number -} - -type Aggregation = { - key: string - count: number -} - -export type SearchResultAggregations = { - [key: string]: Aggregation[] } -export type SearchResultsT = { - meta: SearchResultsMeta - hits: SearchResultHitT[] - aggregations?: SearchResultAggregations -} - -export type SearchQueryT = { +// Parts of the search query that are set to the search context +export type SearchQueryContentT = { query: string debug: boolean } - -export type SearchValidationErrorT = { - error: string - // key: string -} - -export type SearchT = { - search: SearchQueryT - results?: SearchResultsT - validationErrors: SearchValidationErrorT[] -} diff --git a/src/search/lib/config.js b/src/search/lib/config.js deleted file mode 100644 index 21ec77955be7..000000000000 --- a/src/search/lib/config.js +++ /dev/null @@ -1,5 +0,0 @@ -export const namePrefix = 'github-docs' - -export default { - namePrefix, -} diff --git a/src/search/lib/elasticsearch-indexes.ts b/src/search/lib/elasticsearch-indexes.ts new file mode 100644 index 000000000000..4990d229a97b --- /dev/null +++ b/src/search/lib/elasticsearch-indexes.ts @@ -0,0 +1,91 @@ +import languages from '@/languages/lib/languages.js' +import { utcTimestamp } from '@/search/lib/helpers/time' +import { allIndexVersionKeys, versionToIndexVersionMap } from '@/search/lib/elasticsearch-versions' + +import type { SearchTypes } from '@/search/types' + +export type SearchIndexes = { + [key in SearchTypes]: SearchIndex +} + +export type SearchIndex = { + prefix: string + type: string +} + +/* Elasticsearch uses indexes to group categories of data + + We currently have 3 top-level categories of indexes: + 1. General search: This is populated using data from all of our Docs pages + 2. General autocomplete: This is populated using analytics search history in docs-internal-data + 3. AI autocomplete: This is populated with human-readable questions using a GPT query in docs-internal-data + + This file is intended to be the source of truth for Docs Elasticsearch indexes. + + Indexes are in the form: + --- + e.g. github-docs-general-search-fpt-en + + might be "tests_" for tests +*/ +const prefix = 'github-docs' +const indexes: SearchIndexes = { + generalSearch: { + prefix, + type: 'general-search', + }, + generalAutocomplete: { + prefix, + type: 'general-autocomplete', + }, + aiSearchAutocomplete: { + prefix, + type: 'ai-search-autocomplete', + }, +} + +// Source of truth for determining the index name for the Elastic Search index given a version and language +export function getElasticSearchIndex( + type: SearchTypes, + version: string, + language: string, + manualPrefix = '', +): { + indexName: string + indexAlias: string +} { + if (!(type in indexes)) { + throw new Error(`Type ${type} not found in indexes for getElasticSearchIndex function.`) + } + const index = indexes[type] as SearchIndex + + // Validate language + if (!(language in languages)) { + throw new Error( + `Language ${language} not found in languages for getElasticSearchIndex function.`, + ) + } + + // Validate version + if (!allIndexVersionKeys.includes(version)) { + throw new Error( + `Version '${version}' does not map to a valid version for getElasticSearchIndex function.`, + ) + } + + // e.g. free-pro-team becomes fpt for the index name + const indexVersion = versionToIndexVersionMap[version] + + // In the index-test-fixtures.sh script, we use the tests_ prefix index for testing + const testPrefix = process.env.NODE_ENV === 'test' ? 'tests_' : '' + + // If a manual prefix is provided, append an underscore to it + if (manualPrefix && !manualPrefix.endsWith('_')) { + manualPrefix += '_' + } + + const indexName = `${testPrefix || manualPrefix}${index.prefix}_${index.type}_${indexVersion}_${language}` + const indexAlias = `${indexName}__${utcTimestamp()}` + + return { indexName, indexAlias } +} diff --git a/src/search/lib/elasticsearch-versions.ts b/src/search/lib/elasticsearch-versions.ts new file mode 100644 index 000000000000..7d6cb0dd070a --- /dev/null +++ b/src/search/lib/elasticsearch-versions.ts @@ -0,0 +1,107 @@ +/* + * Source of truth for versioning in the context of Elasticsearch + * We have a unique index for each version of the docs + * so consistency is important for creating/accessing ES Indexes. + * + * Example versions (these may not be up to date): + * + * 1. free-pro-team@latest. Previously known as "dotcom". This is the default version of the docs. + * - short name: fpt + * 2. enterprise-cloud@latest + * - short name: ghec + * 3. enterprise-server@X: This is the source of versioning complexity since the version is dynamic + * - short name: ghes-X + * + * However, for (3) someone might enter `&version=3.5` as the version in the request query string. + * This would map to `ghes-3.5` + */ + +import { allVersions } from '@/versions/lib/all-versions' + +// versionToIndexVersionMap examples: +// free-pro-team@latest -> fpt +// free-pro-team -> fpt +// dotcom -> fpt +// enterprise-cloud@latest -> ghec +// enterprise-server@3.5 -> ghes-3.5 +// 3.5 -> ghes-3.5 +export const versionToIndexVersionMap: { [key: string]: string } = {} + +// For each potential input (from request query string, CLI, etc), map it to the appropriate index version +for (const versionSource of Object.values(allVersions)) { + if (versionSource.hasNumberedReleases) { + versionToIndexVersionMap[versionSource.currentRelease] = versionSource.miscVersionName + // Map shortname or plan, e.g. `ghes` or `enterprise-server` to the latest release, e.g. `ghes-3.14` + if (versionSource.latestRelease === versionSource.currentRelease) { + versionToIndexVersionMap[versionSource.plan] = versionSource.miscVersionName + versionToIndexVersionMap[versionSource.shortName] = versionSource.miscVersionName + } + } else { + versionToIndexVersionMap[versionSource.version] = versionSource.shortName + versionToIndexVersionMap[versionSource.miscVersionName] = versionSource.shortName + // The next two lines map things like `?version=free-pro-team` -> `?version=fpt` + versionToIndexVersionMap[versionSource.plan] = versionSource.shortName + versionToIndexVersionMap[versionSource.shortName] = versionSource.shortName + } +} + +// All of the possible keys that can be input to access a version +export const allIndexVersionKeys = Array.from( + new Set([...Object.keys(versionToIndexVersionMap), ...Object.keys(allVersions)]), +) + +// These should be the only possible values that an ES index will use (source of truth) +// allIndexVersionOptions example: +// fpt, ghec, ghes-3.14, ghes-3.13, ghes-3.12, ghes-3.11, ghes-3.10 +export const allIndexVersionOptions = Array.from( + new Set([...Object.values(versionToIndexVersionMap)]), +) + +// Autocomplete only supports 3 "versions": free-pro-team, enterprise-cloud, and enterprise-server +// docs-internal-data stores data under directories with these names. It does not account for individual enterprise-server versions +// These are the "plan" names on the allVersions object +const allVersionPlans: string[] = [] +for (const version of Object.values(allVersions)) { + if (version.plan) { + allVersionPlans.push(version.plan) + } +} +// Remove duplicates +export const supportedAutocompletePlanVersions = Array.from(new Set(allVersionPlans)) + +// Returns the plan name for the given version +// Needed because {version} in the docs-internal-data paths use the version's 'plan' name, e.g. `free-pro-team` instead of `fpt` +export function getPlanVersionFromIndexVersion(indexVersion: string): string { + const planVersion = + Object.values(allVersions).find( + (info) => + info.shortName === indexVersion || + info.plan === indexVersion || + info.miscVersionName === indexVersion || + info.currentRelease === indexVersion, + )?.plan || '' + + if (!planVersion) { + throw new Error(`Plan version not found for index version ${indexVersion}`) + } + + return planVersion +} + +// Gets the matching key from allVersions for the given index version +// This is needed for scraping since the pages use the 'allVersions' key as their version +export function getAllVersionsKeyFromIndexVersion(indexVersion: string): string { + const key = Object.keys(allVersions).find( + (key) => + key === indexVersion || + allVersions[key].shortName === indexVersion || + allVersions[key].plan === indexVersion || + allVersions[key].miscVersionName === indexVersion, + ) + + if (!key) { + throw new Error(`No key found for index version ${indexVersion}`) + } + + return key +} diff --git a/src/search/lib/get-elasticsearch-results/ai-search-autocomplete.ts b/src/search/lib/get-elasticsearch-results/ai-search-autocomplete.ts new file mode 100644 index 000000000000..4d63dd62d3a9 --- /dev/null +++ b/src/search/lib/get-elasticsearch-results/ai-search-autocomplete.ts @@ -0,0 +1,125 @@ +import { Client } from '@elastic/elasticsearch' +import { getElasticsearchClient } from '@/search/lib/helpers/get-client' +import { getHighlightConfiguration } from '@/search/lib/get-elasticsearch-results/helpers/elasticsearch-highlight-config' + +import type { AutocompleteSearchResponse } from '@/search/types' +import type { + AutocompleteMatchQueriesOptions, + AutocompleteResultsArgs, +} from '@/search/lib/get-elasticsearch-results/types' +import type { QueryDslQueryContainer, SearchTotalHits } from '@elastic/elasticsearch/lib/api/types' + +// Query Elasticsearch for AI Search autocomplete results +export async function getAISearchAutocompleteResults({ + indexName, + query, + size, +}: AutocompleteResultsArgs): Promise { + const t0 = new Date() + const client = getElasticsearchClient() as Client + + const matchQueries = getAISearchAutocompleteMatchQueries(query.trim(), { + fuzzy: { + minLength: 3, + maxLength: 20, + }, + }) + const matchQuery = { + bool: { + should: matchQueries, + }, + } + + const highlight = getHighlightConfiguration(query, ['term']) + + const searchQuery = { + index: indexName, + highlight, + size, + query: matchQuery, + _source_includes: ['term'], + } + + const result = await client.search<{ term: string }>(searchQuery) + + const hitsAll = result.hits + const hits = hitsAll.hits.map((hit) => ({ + term: hit._source?.term, + highlights: (hit.highlight && hit.highlight.term) || [], + })) + + return { + meta: { + found: hitsAll.total as SearchTotalHits, + took: { query_msec: result.took, total_msec: new Date().getTime() - t0.getTime() }, + size, + }, + hits, + } +} + +function getAISearchAutocompleteMatchQueries( + query: string, + { fuzzy }: AutocompleteMatchQueriesOptions, +) { + const BOOST_PHRASE = 4.0 + const BOOST_REGULAR = 2.0 + const BOOST_PREFIX = 1.0 + const BOOST_FUZZY = 0.1 + + const matchQueries: QueryDslQueryContainer[] = [] + + // Use match_phrase for exact term matches + matchQueries.push({ + match_phrase: { + term: { + query, + boost: BOOST_PHRASE, + slop: 1, // Allows minor word reordering + }, + }, + }) + + // Use match for general matching + matchQueries.push({ + match: { + term: { + query, + boost: BOOST_PREFIX, + }, + }, + }) + + // Match phrase prefix for partial term matches + matchQueries.push({ + match_phrase_prefix: { + term: { + query, + boost: BOOST_PREFIX, + }, + }, + }) + matchQueries.push({ + match_bool_prefix: { + term: { + query, + boost: BOOST_REGULAR, + }, + }, + }) + + // Add fuzzy matching for typos and variations + if (query.length > fuzzy.minLength && query.length < fuzzy.maxLength) { + matchQueries.push({ + fuzzy: { + term: { + value: query, + boost: BOOST_FUZZY, + fuzziness: 'AUTO', + }, + }, + }) + } + + return matchQueries +} diff --git a/src/search/lib/get-elasticsearch-results/general-autocomplete.ts b/src/search/lib/get-elasticsearch-results/general-autocomplete.ts new file mode 100644 index 000000000000..0f3653940e72 --- /dev/null +++ b/src/search/lib/get-elasticsearch-results/general-autocomplete.ts @@ -0,0 +1,100 @@ +import { Client } from '@elastic/elasticsearch' +import { getElasticsearchClient } from '@/search/lib/helpers/get-client' +import { getHighlightConfiguration } from '@/search/lib/get-elasticsearch-results/helpers/elasticsearch-highlight-config' + +import type { QueryDslQueryContainer, SearchTotalHits } from '@elastic/elasticsearch/lib/api/types' +import type { AutocompleteSearchResponse } from '@/search/types' +import type { + AutocompleteMatchQueriesOptions, + AutocompleteResultsArgs, + AutocompleteElasticsearchItem, +} from '@/search/lib/get-elasticsearch-results/types' + +// Query Elasticsearch for general autocomplete results +export async function getAutocompleteSearchResults({ + indexName, + query, + size, +}: AutocompleteResultsArgs): Promise { + const t0 = new Date() + const client = getElasticsearchClient() as Client + + const matchQueries = getAutocompleteMatchQueries(query.trim(), { + fuzzy: { + minLength: 3, + maxLength: 20, + }, + }) + const matchQuery = { + bool: { + should: matchQueries, + }, + } + + const highlight = getHighlightConfiguration(query, ['term']) + + const searchQuery = { + index: indexName, + highlight, + size, + query: matchQuery, + // Send absolutely minimal from Elasticsearch to here. Less data => faster. + _source_includes: ['term'], + } + + const result = await client.search(searchQuery) + + const hitsAll = result.hits + const hits = hitsAll.hits.map((hit) => ({ + term: hit._source?.term, + highlights: (hit.highlight && hit.highlight.term) || [], + })) + + return { + meta: { + found: hitsAll.total as SearchTotalHits, + took: { query_msec: result.took, total_msec: new Date().getTime() - t0.getTime() }, + size, + }, + hits, + } +} + +function getAutocompleteMatchQueries(query: string, { fuzzy }: AutocompleteMatchQueriesOptions) { + const BOOST_PHRASE = 4.0 + const BOOST_REGULAR = 2.0 + const BOOST_FUZZY = 0.1 + + const matchQueries: QueryDslQueryContainer[] = [] + const isMultiWordQuery = query.includes(' ') || query.includes('-') + + if (isMultiWordQuery) { + matchQueries.push({ + match_phrase_prefix: { + term: { + query, + boost: BOOST_PHRASE, + }, + }, + }) + } + + matchQueries.push({ + match_bool_prefix: { + term: { + query, + boost: BOOST_REGULAR, + }, + }, + }) + + if (query.length > fuzzy.minLength && query.length < fuzzy.maxLength) { + matchQueries.push({ + fuzzy: { + term: { value: query, boost: BOOST_FUZZY, fuzziness: 'AUTO' }, + }, + }) + } + + return matchQueries +} diff --git a/src/search/middleware/es-search.js b/src/search/lib/get-elasticsearch-results/general-search.ts similarity index 59% rename from src/search/middleware/es-search.js rename to src/search/lib/get-elasticsearch-results/general-search.ts index a23e2314dec6..263a7b787739 100644 --- a/src/search/middleware/es-search.js +++ b/src/search/lib/get-elasticsearch-results/general-search.ts @@ -1,57 +1,54 @@ -import { Client } from '@elastic/elasticsearch' - -export const POSSIBLE_HIGHLIGHT_FIELDS = ['title', 'content'] -// This needs to match what we *use* in the `` component. -// For example, if we don't display "headings" we shouldn't request -// highlights for it either. -export const DEFAULT_HIGHLIGHT_FIELDS = ['title', 'content'] - -const ELASTICSEARCH_URL = process.env.ELASTICSEARCH_URL +import { getElasticsearchClient } from '@/search/lib/helpers/get-client' +import { DEFAULT_HIGHLIGHT_FIELDS } from '@/search/lib/search-request-params/search-params-objects' +import { getHighlightConfiguration } from '@/search/lib/get-elasticsearch-results/helpers/elasticsearch-highlight-config' + +import type { + SearchHit as ElasticsearchHit, + QueryDslQueryContainer, + SearchRequest, + SearchTotalHits, +} from '@elastic/elasticsearch/lib/api/types' +import type { + AdditionalIncludes, + ComputedSearchQueryParamsMap, +} from '@/search/lib/search-request-params/types' +import type { SearchAggregation, GeneralSearchHit, GeneralSearchResponse } from '@/search/types' const MAX_AGGREGATE_SIZE = 30 -const isDevMode = process.env.NODE_ENV !== 'production' +const isDevMode: boolean = process.env.NODE_ENV !== 'production' -function getClient() { - if (!ELASTICSEARCH_URL) { - // If this was mistakenly not set, it will eventually fail - // when you use the Client. But `new Client({node: undefined})` - // won't throw. And the error you get when you actually do try - // to use that Client instance is cryptic compared to this - // plain and simple thrown error. - throw new Error(`$ELASTICSEARCH_URL is not set`) - } - return new Client({ - node: ELASTICSEARCH_URL, - // The default is 30,000ms but we noticed that the median time is about - // 100-150ms with some occasional searches taking multiple seconds. - // The default `maxRetries` is 3 which is a sensible number. - // If a query gets stuck, it's better to (relatively) quickly give up - // and retry. So if it takes longer than this time here, we're banking on - // that it was just bad luck and that it'll work if we simply try again. - // See internal issue #2318. - requestTimeout: 1900, - // It's important that requestTimeout * maxRetries is less than 10 seconds. - maxRetries: 5, - }) +type getGeneralSearchResultsParams = { + indexName: string + searchParams: ComputedSearchQueryParamsMap['generalSearch'] + topics?: string[] + includeTopics?: boolean } -// The true work horse that actually performs the Elasticsearch query -export async function getSearchResults({ - indexName, - query, - page, - size, - debug, - sort, - topics, - includeTopics, - usePrefixSearch, - highlights, - include, - toplevel, - aggregate, -}) { +// Query Elasticsearch for general search results +export async function getGeneralSearchResults( + args: getGeneralSearchResultsParams, +): Promise { + const { + indexName, + searchParams: { + highlights, + include, + toplevel, + aggregate, + autocomplete, + query, + page, + size, + debug, + sort, + }, + topics, + includeTopics, + } = args + + const usePrefixSearch = autocomplete + if (topics && !Array.isArray(topics)) { throw new Error("'topics' has to be an array") } @@ -71,8 +68,8 @@ export async function getSearchResults({ throw new Error("Every entry in the 'toplevel' must be a string") } } - const t0 = new Date() - const client = getClient() + const t0 = Date.now() + const client = getElasticsearchClient() const from = size * (page - 1) const matchQueries = getMatchQueries(query.trim(), { @@ -83,7 +80,7 @@ export async function getSearchResults({ }, }) - const matchQuery = { + const matchQuery: Record = { bool: { should: matchQueries, // This allows filtering by toplevel later. @@ -91,7 +88,8 @@ export async function getSearchResults({ }, } - const topicsFilter = (topics || []).map((topic) => { + const topicsArray = Array.isArray(topics) ? topics : topics ? [topics] : [] + const topicsFilter = topicsArray.map((topic) => { return { term: { // Remember, 'topics' is a keyword field, meaning you need @@ -101,15 +99,18 @@ export async function getSearchResults({ } }) if (topicsFilter.length) { - matchQuery.bool.filter = topicsFilter + matchQuery.bool.filter = matchQuery.bool.filter || [] + matchQuery.bool.filter.push(...topicsFilter) } - if (toplevel && toplevel.length) { - matchQuery.bool.filter = { + const toplevelArray = toplevel || [] + if (toplevelArray.length) { + matchQuery.bool.filter = matchQuery.bool.filter || [] + matchQuery.bool.filter.push({ terms: { - toplevel, + toplevel: toplevelArray, }, - } + }) } const highlightFields = Array.from(highlights || DEFAULT_HIGHLIGHT_FIELDS) @@ -121,7 +122,7 @@ export async function getSearchResults({ const aggs = getAggregations(aggregate) - const searchQuery = { + const searchQuery: SearchRequest = { index: indexName, highlight, from, @@ -136,13 +137,13 @@ export async function getSearchResults({ _source_includes: ['title', 'url', 'breadcrumbs', 'popularity', 'toplevel'], } - if (includeTopics) { - searchQuery._source_includes.push('topics') + if (includeTopics && Array.isArray(searchQuery._source_includes)) { + searchQuery._source_includes?.push('topics') } - for (const key of ['intro', 'headings']) { - if (include.includes(key)) { - searchQuery._source_includes.push(key) + for (const key of ['intro', 'headings'] as const) { + if (include.includes(key) && Array.isArray(searchQuery._source_includes)) { + searchQuery._source_includes?.push(key) } } @@ -193,26 +194,26 @@ export async function getSearchResults({ highlightFields, include, }) - const aggregations = getAggregationsResult(aggregate, result.aggregations) - const t1 = new Date() + const aggregationsResult = getAggregationsResult(aggregate, result.aggregations) + const t1 = Date.now() const meta = { - found: hitsAll.total, + found: hitsAll.total as SearchTotalHits, took: { query_msec: result.took, - total_msec: t1.getTime() - t0.getTime(), + total_msec: t1 - t0, }, page, size, } - return { meta, hits, aggregations } + return { meta, hits, aggregations: aggregationsResult } } -function getAggregations(aggregate) { +function getAggregations(aggregate?: string[]): Record | undefined { if (!aggregate || !aggregate.length) return undefined - const aggs = {} + const aggs: Record = {} for (const key of aggregate) { aggs[key] = { terms: { @@ -224,66 +225,37 @@ function getAggregations(aggregate) { return aggs } -function getAggregationsResult(aggregate, result) { - if (!aggregate || !aggregate.length) return - return Object.fromEntries( - aggregate.map((key) => [ - key, - result[key].buckets - .map((bucket) => { - return { - key: bucket.key, - count: bucket.doc_count, - } - }) - .sort((a, b) => a.key.localeCompare(b.key)), - ]), - ) -} - -export async function getAutocompleteSearchResults({ indexName, query, size }) { - const client = getClient() - - const matchQueries = getAutocompleteMatchQueries(query.trim(), { - fuzzy: { - minLength: 3, - maxLength: 20, - }, - }) - const matchQuery = { - bool: { - should: matchQueries, - }, - } - - const highlight = getHighlightConfiguration(query, ['term']) - - const searchQuery = { - index: indexName, - highlight, - size, - query: matchQuery, - // Send absolutely minimal from Elasticsearch to here. Less data => faster. - _source_includes: ['term'], - } - const result = await client.search(searchQuery) - - const hitsAll = result.hits - const hits = hitsAll.hits.map((hit) => { - return { - term: hit._source.term, - highlights: (hit.highlight && hit.highlight.term) || [], +function getAggregationsResult( + aggregate?: string[], + result?: Record, +): Record | undefined { + if (!aggregate || !aggregate.length || !result) return undefined + const aggregations: Record = {} + for (const key of aggregate) { + if (result[key]?.buckets) { + aggregations[key] = result[key].buckets + .map((bucket: any) => ({ + key: bucket.key as string, + count: bucket.doc_count as number, + })) + .sort((a: { key: string }, b: { key: string }) => a.key.localeCompare(b.key)) } - }) - - const meta = { - found: hitsAll.total, } + return aggregations +} - return { meta, hits } +interface GetMatchQueriesOptions { + usePrefixSearch: boolean + fuzzy: { + minLength: number + maxLength: number + } } -function getMatchQueries(query, { usePrefixSearch, fuzzy }) { +function getMatchQueries( + query: string, + { usePrefixSearch, fuzzy }: GetMatchQueriesOptions, +): QueryDslQueryContainer[] { const BOOST_PHRASE = 10.0 const BOOST_TITLE = 4.0 const BOOST_HEADINGS = 3.0 @@ -296,7 +268,7 @@ function getMatchQueries(query, { usePrefixSearch, fuzzy }) { // which wouldn't find anything else anyway. const BOOST_FUZZY = 0.1 - const matchQueries = [] + const matchQueries: QueryDslQueryContainer[] = [] // If the query input is multiple words, it's good to know because you can // make the query do `match_phrase` and you can make `match` query @@ -453,12 +425,12 @@ function getMatchQueries(query, { usePrefixSearch, fuzzy }) { } else if (query.startsWith('http')) { // E.g. `https://docs.github.com/en/some/page?foo=bar` // will become a search on `{url: '/en/some/page'}` - let pathname + let pathname: string | undefined try { pathname = new URL(query).pathname } catch { // If it failed, it can't be initialized with the `URL` constructor - // we so we can deem it *not* a valid URL. + // so we can deem it *not* a valid URL. } if (pathname) { matchQueries.push({ @@ -471,47 +443,18 @@ function getMatchQueries(query, { usePrefixSearch, fuzzy }) { return matchQueries } -function getAutocompleteMatchQueries(query, { fuzzy }) { - const BOOST_PHRASE = 4.0 - const BOOST_REGULAR = 2.0 - const BOOST_FUZZY = 0.1 // make it always last in ranking - const matchQueries = [] - - // If the query input is multiple words, it's good to know because you can - // make the query do `match_phrase` and you can make `match` query - // with the `AND` operator (`OR` is the default). - const isMultiWordQuery = query.includes(' ') || query.includes('-') - - if (isMultiWordQuery) { - matchQueries.push({ - match_phrase_prefix: { - term: { - query, - boost: BOOST_PHRASE, - }, - }, - }) - } - matchQueries.push({ - match_bool_prefix: { - term: { - query, - boost: BOOST_REGULAR, - }, - }, - }) - if (query.length > fuzzy.minLength && query.length < fuzzy.maxLength) { - matchQueries.push({ - fuzzy: { - term: { value: query, boost: BOOST_FUZZY, fuzziness: 'AUTO' }, - }, - }) - } - - return matchQueries +interface GetHitsOptions { + indexName: string + debug?: boolean + includeTopics?: boolean + highlightFields: string[] + include: AdditionalIncludes[] } -function getHits(hits, { indexName, debug, includeTopics, highlightFields, include }) { +function getHits( + hits: ElasticsearchHit[], + { indexName, debug = false, includeTopics = false, highlightFields, include }: GetHitsOptions, +): GeneralSearchHit[] { return hits.map((hit) => { // Return `hit.highlights[...]` based on the highlight fields requested. // So if you searched with `&highlights=headings&highlights=content` @@ -521,11 +464,12 @@ function getHits(hits, { indexName, debug, includeTopics, highlightFields, inclu // headings: [...] // } // even if there was a match on 'title'. - const hitHighlights = Object.fromEntries( - highlightFields.map((key) => [key, (hit.highlight && hit.highlight[key]) || []]), - ) + const hitHighlights: Record = {} + for (const key of highlightFields) { + hitHighlights[key] = (hit.highlight && hit.highlight[key]) || [] + } - const result = { + const result: GeneralSearchHit = { id: hit._id, url: hit._source.url, title: hit._source.title, @@ -536,87 +480,15 @@ function getHits(hits, { indexName, debug, includeTopics, highlightFields, inclu result.topics = hit._source.topics || [] } if (debug) { - result.score = hit._score || 0.0 - result.popularity = hit._source.popularity || 0.0 + result.score = hit._score ?? 0.0 + result.popularity = hit._source.popularity ?? 0.0 if (isDevMode) { result.es_url = `http://localhost:9200/${indexName}/_doc/${hit._id}` } } - for (const field of include || []) { + for (const field of include) { result[field] = hit._source[field] } return result }) } - -// The highlight configuration is dependent on how we use the content -// in the UI. For example, we feel we need about 3 lines (max) -// of highlights of content under each title. If we feel it shows too -// many highlights in the search result UI, we can come back here -// and change it to something more appropriate. -function getHighlightConfiguration(query, highlights) { - const fields = {} - if (highlights.includes('title')) { - fields.title = { - // Fast Vector Highlighter - // Using this requires that you first index these fields - // with {term_vector: 'with_positions_offsets'} - type: 'fvh', - fragment_size: 200, - number_of_fragments: 1, - } - } - if (highlights.includes('content')) { - // The 'no_match_size' is so we can display *something* for the - // preview if there was no highlight match at all within the content. - fields.content = { - // Fast Vector Highlighter - // Using this requires that you first index these fields - // with {term_vector: 'with_positions_offsets'} - type: 'fvh', - fragment_size: 150, - number_of_fragments: 1, - no_match_size: 150, - - highlight_query: { - match_phrase_prefix: { - content: { - query, - }, - }, - }, - } - fields.content_explicit = { - // Fast Vector Highlighter - // Using this requires that you first index these fields - // with {term_vector: 'with_positions_offsets'} - type: 'fvh', - fragment_size: 150, - number_of_fragments: 1, - no_match_size: 0, - - highlight_query: { - match_phrase_prefix: { - content_explicit: { - query, - }, - }, - }, - } - } - if (highlights.includes('term')) { - fields.term = { - // Fast Vector Highlighter - // Using this requires that you first index these fields - // with {term_vector: 'with_positions_offsets'} - type: 'fvh', - // fragment_size: 200, - // number_of_fragments: 1, - } - } - return { - pre_tags: [''], - post_tags: [''], - fields, - } -} diff --git a/src/search/lib/get-elasticsearch-results/helpers/elasticsearch-highlight-config.ts b/src/search/lib/get-elasticsearch-results/helpers/elasticsearch-highlight-config.ts new file mode 100644 index 000000000000..98c9bc5ff6ec --- /dev/null +++ b/src/search/lib/get-elasticsearch-results/helpers/elasticsearch-highlight-config.ts @@ -0,0 +1,86 @@ +import { SearchHighlight } from '@elastic/elasticsearch/lib/api/types' + +import type { HighlightOptions } from '@/search/lib/search-request-params/types' + +export interface HighlightConfig { + type: string + fragment_size?: number + number_of_fragments?: number + no_match_size?: number + highlight_query?: object +} + +export type HighlightFields = { + [key in HighlightOptions]: HighlightConfig +} + +// When we query Elasticsearch, we can specify a highlight configuration +export function getHighlightConfiguration( + query: string, + highlightsFields: HighlightOptions[], +): SearchHighlight { + const fields = {} as HighlightFields + if (highlightsFields.includes('title')) { + fields.title = { + // Fast Vector Highlighter + // Using this requires that you first index these fields + // with {term_vector: 'with_positions_offsets'} + type: 'fvh', + fragment_size: 200, + number_of_fragments: 1, + } + } + if (highlightsFields.includes('content')) { + fields.content = { + // Fast Vector Highlighter + // Using this requires that you first index these fields + // with {term_vector: 'with_positions_offsets'} + type: 'fvh', + fragment_size: 150, + number_of_fragments: 1, + // So we can at least display something if there was no highlight match within the content. + no_match_size: 150, + + highlight_query: { + match_phrase_prefix: { + content: { + query, + }, + }, + }, + } + fields.content_explicit = { + // Fast Vector Highlighter + // Using this requires that you first index these fields + // with {term_vector: 'with_positions_offsets'} + type: 'fvh', + fragment_size: 150, + number_of_fragments: 1, + no_match_size: 0, + + highlight_query: { + match_phrase_prefix: { + content_explicit: { + query, + }, + }, + }, + } + } + if (highlightsFields.includes('term')) { + fields.term = { + // Fast Vector Highlighter + // Using this requires that you first index these fields + // with {term_vector: 'with_positions_offsets'} + type: 'fvh', + } + } + + const highlightConfig: SearchHighlight = { + pre_tags: [''], + post_tags: [''], + fields, + } + + return highlightConfig +} diff --git a/src/search/lib/get-elasticsearch-results/types.ts b/src/search/lib/get-elasticsearch-results/types.ts new file mode 100644 index 000000000000..da6fa59f5612 --- /dev/null +++ b/src/search/lib/get-elasticsearch-results/types.ts @@ -0,0 +1,23 @@ +export interface AutocompleteResultsArgs { + indexName: string + query: string + size: number +} + +export interface FuzzyConfig { + minLength: number + maxLength: number +} + +export interface MatchQueriesOptions { + usePrefixSearch?: boolean + fuzzy: FuzzyConfig +} + +export interface AutocompleteMatchQueriesOptions { + fuzzy: FuzzyConfig +} + +export interface AutocompleteElasticsearchItem { + term: string +} diff --git a/src/search/lib/helpers/get-client.ts b/src/search/lib/helpers/get-client.ts new file mode 100644 index 000000000000..b6b4c1106452 --- /dev/null +++ b/src/search/lib/helpers/get-client.ts @@ -0,0 +1,31 @@ +import { Client } from '@elastic/elasticsearch' +import { safeUrlDisplay } from '@/search/lib/helpers/strings' + +export function getElasticsearchClient(overrideURL = '', verbose = false): Client { + const node = getElasticsearchURL(overrideURL) + if (verbose) { + console.log('Connecting to Elasticsearch URL:', safeUrlDisplay(node)) + } + const client = new Client({ node }) + return client +} + +function getElasticsearchURL(overrideURL = ''): string { + if (!process.env.ELASTICSEARCH_URL && !overrideURL) { + throw new Error( + 'Must pass the elasticsearch URL option or ' + + 'set the environment variable ELASTICSEARCH_URL', + ) + } + let node = overrideURL || process.env.ELASTICSEARCH_URL || '' + + // Allow the user to lazily set it to `localhost:9200` for example. + if (!node.startsWith('http') && !node.startsWith('://') && node.split(':').length === 2) { + node = `http://${node}` + } + + const parsed = new URL(node) + if (!parsed.hostname) throw new Error('no valid hostname') + + return node +} diff --git a/src/search/lib/helpers/old-version-logic.ts b/src/search/lib/helpers/old-version-logic.ts new file mode 100644 index 000000000000..137c63c71495 --- /dev/null +++ b/src/search/lib/helpers/old-version-logic.ts @@ -0,0 +1,44 @@ +import { allVersions } from '@/versions/lib/all-versions' + +// TODO: Old version logic +type VersionAliases = { [key: string]: string } +export const versionAliases: VersionAliases = {} +export const prefixVersionAliases: VersionAliases = {} + +Object.values(allVersions).forEach((info) => { + if (info.hasNumberedReleases) { + versionAliases[info.currentRelease] = info.miscVersionName + } else { + versionAliases[info.version] = info.miscVersionName + versionAliases[info.miscVersionName] = info.miscVersionName + } + prefixVersionAliases[info.plan] = info.shortName + prefixVersionAliases[info.shortName] = info.shortName +}) + +// Temporary hard-coded switch +// +// We need to run workflows in production to index the search data +// We want the middleware + routes that consume the indexes to consume the old indexes +// until the new indexes are ready. + +// Once they are ready we can remove this file & cleanup the places it is used +export function isBeforeSearchIndexMigration() { + if (process.env.NODE_ENV === 'production') return true + return false +} + +// Old test prefix helper function +export function getGeneralSearchIndexPrefix(): string { + if (process.env.NODE_ENV === 'test') return 'tests_' + return '' +} + +export function getGeneralSearchIndexVersion(paramVersion: string): string { + const version = + prefixVersionAliases[paramVersion] || + versionAliases[paramVersion] || + allVersions[paramVersion].miscVersionName + + return version +} diff --git a/src/search/lib/helpers/strings.ts b/src/search/lib/helpers/strings.ts new file mode 100644 index 000000000000..d8ca26383cc4 --- /dev/null +++ b/src/search/lib/helpers/strings.ts @@ -0,0 +1,10 @@ +export function safeUrlDisplay(url: string): string { + const parsed = new URL(url) + if (parsed.password) { + parsed.password = '***' + } + if (parsed.username) { + parsed.username = parsed.username.slice(0, 4) + '***' + } + return parsed.toString() +} diff --git a/src/search/scripts/index/lib/utils.ts b/src/search/lib/helpers/time.ts similarity index 56% rename from src/search/scripts/index/lib/utils.ts rename to src/search/lib/helpers/time.ts index 779ba85c6990..36579358d4ef 100644 --- a/src/search/scripts/index/lib/utils.ts +++ b/src/search/lib/helpers/time.ts @@ -33,3 +33,28 @@ export function utcTimestamp() { .join('') ) } + +/** + * Converts a given number of seconds into a formatted time string "HH:mm:ss". + * + * @param {number} seconds - The total number of seconds to format. + * @returns {string} A string representing the time in "hours:minutes:seconds" format. + * + * @example + * // returns "01:30:45" + * formatSeconds(5445); + */ +export function formatSecondsToHHMMSS(seconds: number): string { + return new Date(seconds * 1000).toISOString().substr(11, 8) +} + +export function readableTimeMinAndSec(ms: number): string { + if (ms < 1000) { + return `${ms.toFixed(1)}ms` + } + const seconds = ms / 1000 + if (seconds > 60) { + return `${Math.round(seconds / 60)}m${Math.round(seconds % 60)}s` + } + return `${seconds.toFixed(1)}s` +} diff --git a/src/search/lib/search-request-params/get-search-from-request-params.ts b/src/search/lib/search-request-params/get-search-from-request-params.ts new file mode 100644 index 000000000000..1ae3fd38ad85 --- /dev/null +++ b/src/search/lib/search-request-params/get-search-from-request-params.ts @@ -0,0 +1,96 @@ +import type { Request } from 'express' +import { format } from 'node:util' + +import { getElasticSearchIndex } from '@/search/lib/elasticsearch-indexes' +import { + ValidationError, + getSearchRequestParamsObject, +} from '@/search/lib/search-request-params/search-params-objects' +import { + getGeneralSearchIndexVersion, + getGeneralSearchIndexPrefix, + isBeforeSearchIndexMigration, +} from '@/search/lib/helpers/old-version-logic' + +import type { + ComputedSearchQueryParams, + ComputedSearchQueryParamsMap, + GetSearchRequestReturn, +} from '@/search/lib/search-request-params/types' +import type { SearchTypes, SearchValidationErrorEntry } from '@/search/types' + +type ForceParams = { + [K in keyof ComputedSearchQueryParams]?: ComputedSearchQueryParams[K] +} + +// Fetches the Search Params Object based on the type of request and uses that object to validate the passed in request parameters +// For example, if the request is a general search request, the general search params object expects a `page` key, e.g. ?page=1 on the request +// If that key is not present, it will be added to the validation errors array which will result in a 400 to the user. +export function getSearchFromRequestParams( + req: Request, + type: Type, + forceParams: ForceParams = {} as ForceParams, +): GetSearchRequestReturn { + const searchParamsObject = getSearchRequestParamsObject(type) + + const searchParams: ComputedSearchQueryParamsMap[Type] = {} as ComputedSearchQueryParamsMap[Type] + const validationErrors: SearchValidationErrorEntry[] = [] + + for (const { key, default_, cast, validate, multiple } of searchParamsObject) { + if (key in forceParams) { + ;(searchParams[key] as any) = forceParams[key] as any + continue + } + + let value = req.query[key] + if (!value || (typeof value === 'string' && !value.trim())) { + if (default_ === undefined) { + validationErrors.push({ error: `No truthy value for key '${key}'`, key }) + continue + } + value = default_ + } + if (cast) { + value = cast(value) + } + try { + if (validate && !validate(value)) { + validationErrors.push({ + error: format('Not a valid value (%O) for key %O', value, key), + key, + }) + } + } catch (err) { + if (err instanceof ValidationError) { + validationErrors.push({ error: err.toString(), field: key }) + } else { + throw err + } + } + if (!multiple && Array.isArray(value)) { + validationErrors.push({ + error: format('Cannot have multiple values (%O) for key %O', value, key), + key, + }) + } + + ;(searchParams[key] as any) = value + } + + let indexName = '' + if (!validationErrors.length) { + // generalSearch is the only type of search that uses the old index prefix logic, rather than the `getElasticSearchIndex` function logic + if (type === 'generalSearch' && isBeforeSearchIndexMigration()) { + indexName = `${getGeneralSearchIndexPrefix()}github-docs-${getGeneralSearchIndexVersion(searchParams.version)}-${searchParams.language}` + } else { + const getIndexResults = getElasticSearchIndex( + type, + searchParams.version, + searchParams.language, + ) + indexName = getIndexResults.indexName + } + } + + return { indexName, searchParams, validationErrors } +} diff --git a/src/search/lib/search-request-params/search-params-objects.ts b/src/search/lib/search-request-params/search-params-objects.ts new file mode 100644 index 000000000000..76dfce35e61e --- /dev/null +++ b/src/search/lib/search-request-params/search-params-objects.ts @@ -0,0 +1,153 @@ +/* + When a request is made to a /search endpoint with query parameters, e.g. ?query=foo&version=free-pro-team, + we need to validate and parse the parameters. This file contains the configuration for which parameters + to expect based on the type of search request "e.g. general search vs autocomplete search" and how to validate them. + */ +import languages from '@/languages/lib/languages' +import { allIndexVersionKeys, versionToIndexVersionMap } from '@/search/lib/elasticsearch-versions' +import { SearchTypes } from '@/search/types' +import { versionAliases } from '@/search/lib/helpers/old-version-logic' +import { allVersions } from '@/versions/lib/all-versions' + +import type { SearchRequestQueryParams } from '@/search/lib/search-request-params/types' + +// Entry to this file, returns the query parameters to expect based on the type of search request +export function getSearchRequestParamsObject(type: SearchTypes): SearchRequestQueryParams[] { + if (type === 'generalAutocomplete') { + return AUTOCOMPLETE_PARAMS_OBJ + } else if (type === 'aiSearchAutocomplete') { + return AI_SEARCH_AUTOCOMPLETE_PARAMS_OBJ + } + return GENERAL_SEARCH_PARAMS_OBJ +} + +// - - - Everything below this line is for building the search query param objects - - - // + +// Constants +const DEFAULT_AUTOCOMPLETE_SIZE = 5 +const MAX_AUTOCOMPLETE_SIZE = 10 +const DEFAULT_SIZE = 10 +const MAX_SIZE = 50 +const DEFAULT_PAGE = 1 +const POSSIBLE_SORTS = ['best', 'relevance'] as const +const DEFAULT_SORT = POSSIBLE_SORTS[0] +const MAX_PAGE = 10 +const V1_AGGREGATES = ['toplevel'] as const +export const POSSIBLE_HIGHLIGHT_FIELDS = ['title', 'content'] as const +// This needs to match what we *use* in the `` component. +// For example, if we don't display "headings" we shouldn't request +// highlights for it either. +export const DEFAULT_HIGHLIGHT_FIELDS: readonly string[] = ['title', 'content'] + +export const V1_ADDITIONAL_INCLUDES = ['intro', 'headings', 'toplevel'] as const + +export class ValidationError extends Error {} + +const SHARED_PARAMS_OBJ: SearchRequestQueryParams[] = [ + { key: 'query' }, + { + key: 'version', + default_: 'free-pro-team', + validate: (version: string) => { + if (!versionToIndexVersionMap[version]) { + throw new ValidationError(`'${version}' not in ${allIndexVersionKeys.join(', ')}`) + } + return true + }, + }, +] + +const GENERAL_SEARCH_PARAMS_OBJ: SearchRequestQueryParams[] = [ + ...SHARED_PARAMS_OBJ, + { key: 'query' }, + // TODO: Overwrite with old version logic for now + { + key: 'version', + default_: 'dotcom', + validate: (v) => { + if (versionAliases[v] || allVersions[v]) return true + const valid = [...Object.keys(versionAliases), ...Object.keys(allVersions)] + throw new ValidationError(`'${v}' not in ${valid}`) + }, + }, + { key: 'language', default_: 'en', validate: (v) => v in languages }, + { + key: 'size', + default_: DEFAULT_SIZE, + cast: (v) => parseInt(v, 10), + validate: (v) => v >= 0 && v <= MAX_SIZE, + }, + { + key: 'page', + default_: DEFAULT_PAGE, + cast: (v) => parseInt(v, 10), + validate: (v) => v >= 1 && v <= MAX_PAGE, + }, + { key: 'sort', default_: DEFAULT_SORT, validate: (v) => POSSIBLE_SORTS.includes(v as any) }, + { + key: 'highlights', + default_: DEFAULT_HIGHLIGHT_FIELDS, + cast: (v) => (Array.isArray(v) ? v : [v]), + multiple: true, + validate: (v) => { + for (const highlight of v) { + if (!POSSIBLE_HIGHLIGHT_FIELDS.includes(highlight)) { + throw new ValidationError(`highlight value '${highlight}' is not valid`) + } + } + return true + }, + }, + { key: 'autocomplete', default_: false, cast: toBoolean }, + { key: 'debug', default_: process.env.NODE_ENV === 'development', cast: toBoolean }, + { + key: 'include', + default_: [], + cast: toArray, + multiple: true, + validate: (values) => + values.every((value: string) => V1_ADDITIONAL_INCLUDES.includes(value as any)), + }, + { + key: 'toplevel', + default_: [], + cast: toArray, + multiple: true, + }, + { + key: 'aggregate', + default_: [], + cast: toArray, + multiple: true, + validate: (values) => values.every((value: string) => V1_AGGREGATES.includes(value as any)), + }, +] + +const SHARED_AUTOCOMPLETE_PARAMS_OBJ: SearchRequestQueryParams[] = [ + { + key: 'size', + default_: DEFAULT_AUTOCOMPLETE_SIZE, + cast: (size: string) => parseInt(size, 10), + validate: (size: number) => size >= 0 && size <= MAX_AUTOCOMPLETE_SIZE, + }, +] + +const AI_SEARCH_AUTOCOMPLETE_PARAMS_OBJ: SearchRequestQueryParams[] = [ + ...SHARED_PARAMS_OBJ, + ...SHARED_AUTOCOMPLETE_PARAMS_OBJ, + { key: 'language', default_: 'en', validate: (language: string) => language === 'en' }, +] + +const AUTOCOMPLETE_PARAMS_OBJ: SearchRequestQueryParams[] = [ + ...SHARED_PARAMS_OBJ, + ...SHARED_AUTOCOMPLETE_PARAMS_OBJ, + { key: 'language', default_: 'en', validate: (language: string) => language in languages }, +] + +function toBoolean(value: any): boolean { + return value === 'true' || value === '1' +} + +function toArray(value: any): any[] { + return Array.isArray(value) ? value : [value] +} diff --git a/src/search/lib/search-request-params/types.ts b/src/search/lib/search-request-params/types.ts new file mode 100644 index 000000000000..e9673c767769 --- /dev/null +++ b/src/search/lib/search-request-params/types.ts @@ -0,0 +1,52 @@ +import { V1_ADDITIONAL_INCLUDES } from '@/search/lib/search-request-params/search-params-objects' + +import { SearchTypes, SearchValidationErrorEntry } from '@/search/types' + +export type HighlightOptions = 'title' | 'content' | 'content_explicit' | 'term' + +export type AdditionalIncludes = (typeof V1_ADDITIONAL_INCLUDES)[number] + +export interface ComputedSearchQueryParams { + query: string + size: number + version: string + language: string + // These are optional, so we need to use ComputedSearchQueryParamsMap in functions to get the exact types per Search Type + page?: number + sort?: string + highlights?: HighlightOptions[] + autocomplete?: boolean + debug?: boolean + include?: AdditionalIncludes[] + toplevel?: string[] + aggregate?: string[] +} + +export interface ComputedSearchQueryParamsMap { + generalSearch: ComputedSearchQueryParams & { + page: number + sort: string + highlights: HighlightOptions[] + autocomplete: boolean + debug: boolean + include: AdditionalIncludes[] + toplevel: string[] + aggregate: string[] + } + generalAutocomplete: ComputedSearchQueryParams + aiSearchAutocomplete: ComputedSearchQueryParams +} + +export interface SearchRequestQueryParams { + key: keyof ComputedSearchQueryParams + default_?: any + cast?: (value: any) => any + validate?: (value: any) => boolean + multiple?: boolean +} + +export interface GetSearchRequestReturn { + indexName: string + searchParams: ComputedSearchQueryParamsMap[Type] + validationErrors: SearchValidationErrorEntry[] +} diff --git a/src/search/middleware/contextualize.js b/src/search/middleware/contextualize.js deleted file mode 100644 index 20462c06c88d..000000000000 --- a/src/search/middleware/contextualize.js +++ /dev/null @@ -1,153 +0,0 @@ -import got from 'got' -import { errors } from '@elastic/elasticsearch' -import statsd from '#src/observability/lib/statsd.js' - -import { getPathWithoutVersion, getPathWithoutLanguage } from '#src/frame/lib/path-utils.js' -import { getSearchFromRequest } from './get-search-request.js' -import { getSearchResults } from './es-search.js' - -export default async function contextualizeSearch(req, res, next) { - // If it's NextJS fetching or data or it's a direct request, - // the pagePath is the "normalized" version - const { pagePath } = req - if (getPathWithoutLanguage(getPathWithoutVersion(pagePath)) !== '/search') { - return next() - } - - // When you use `/api/search/v1?version=foo&language=xy&...` - // the language and version comes from the query string. - // When you use `/xz/enterprise-cloud@latest/search?query=hello` - // the `version` and `language` is implied from the URL pathname. - // search.version = req.context.currentVersion - // search.language = req.context.currentLanguage - - const { search, validationErrors } = getSearchFromRequest(req, { - version: req.context.currentVersion, - language: req.context.currentLanguage, - }) - - if (validationErrors.map((error) => error.key).includes('query')) { - // 'query' is such an exception because the search result component - // will attempt to display its value even if there was any - // validation error. In a sense, it displays: - // - // You searched for "foo" - // But your 'page' parameter is invalid. - // - // If for example, the search input is an array, we pick the first - // value. If it's too long, we truncate it. - if (Array.isArray(search.query)) { - search.query = search.query[0] - } else if (!search.query) { - // If the 'query' query string parameter wasn't even present, - // it becomes `undefined`. But since `search.query` needs to be - // a *string*, we pretend it was provided but empty. - search.query = '' - } - } - - // This enables so that when the search is sent to Elasticsearch - // it will request an aggregate by these keyword fields. - search.aggregate = ['toplevel'] - - req.context.search = { search, validationErrors } - - if (!validationErrors.length && search.query) { - if (!process.env.ELASTICSEARCH_URL) { - // This is only true in local dev or in Preview environments. - // And in local dev, it's usually for content contributors who - // want to test a preview locally, but don't want to have to - // set up Elasticsearch. - // This same proxying logic happens in `middleware/api/index.js` - // too for the outwards facing `/api/search/v1` endpoint. - if (search.aggregate && search.toplevel && search.toplevel.length > 0) { - // Do 2 searches. One without filtering - const { toplevel, ...searchWithoutFilter } = search - searchWithoutFilter.size = 0 - const { aggregations } = await getProxySearch(searchWithoutFilter) - const { aggregate, ...searchWithoutAggregate } = search - req.context.search.results = await getProxySearch(searchWithoutAggregate) - req.context.search.results.aggregations = aggregations - } else { - req.context.search.results = await getProxySearch(search) - } - } else { - // If this throws, so be it. Let it bubble up. - // In local dev, you get to see the error. In production, - // you get a "Oops! Something went wrong" which involves a Failbot - // send. - const tags = [`indexName:${search.indexName}`, `toplevels:${search.toplevel.length}`] - const timed = statsd.asyncTimer(getSearchResults, 'contextualize.search', tags) - try { - if (search.aggregate && search.toplevel && search.toplevel.length > 0) { - // Do 2 searches. One without filtering - const { toplevel, ...searchWithoutFilter } = search - searchWithoutFilter.size = 0 - const { aggregations } = await timed(searchWithoutFilter) - req.context.search.results = await timed(search) - req.context.search.results.aggregations = aggregations - } else { - req.context.search.results = await timed(search) - } - } catch (error) { - // If the error coming from the Elasticsearch client is any sort - // of 4xx error, it will be bubbled up to the next middleware - // which might think something else is wrong with the *client's* - // request from the outside. But in reality it's not their fault. - // It's our fault in the backend side. So we throw a new error - // so that this failure to seach ultimately bubbles up to a - // proper 500 error (including Failbot reporting). - // In particular, this helps platform developers working on the - // Elasticsearch searching code. - if (error instanceof errors.ElasticsearchClientError) { - console.error('Error calling getSearchResults(%s):', search, error) - if (error.meta?.body) { - console.error(`Meta:`, error.meta.body) - } - throw new Error(error.message) - } else { - throw error - } - } - } - } - - return next() -} - -// When you use the proxy to prod, using its API, we need to "convert" -// the parameters we have figured out here in the contextualizer. -// Thankfully all the names match. For example, we might figure -// the page by doing `req.context.search.page = 123` and now we need to -// add that to the query string for the `/api/search/v1`. -// We inclusion-list all the keys that we want to take from the search -// object into the query string URL. -const SEARCH_KEYS_TO_QUERY_STRING = [ - 'query', - 'version', - 'language', - 'page', - 'aggregate', - 'toplevel', - 'size', -] - -async function getProxySearch(search) { - const url = new URL('https://docs.github.com/api/search/v1') - for (const key of SEARCH_KEYS_TO_QUERY_STRING) { - const value = search[key] - if (typeof value === 'boolean') { - url.searchParams.set(key, value ? 'true' : 'false') - } else if (Array.isArray(value)) { - for (const v of value) { - url.searchParams.append(key, v) - } - } else if (typeof value === 'number') { - url.searchParams.set(key, `${value}`) - } else if (value) { - url.searchParams.set(key, value) - } - } - console.log(`Proxying search to ${url}`) - return got(url).json() -} diff --git a/src/search/middleware/general-search-middleware.ts b/src/search/middleware/general-search-middleware.ts new file mode 100644 index 000000000000..5613d2bb7ef4 --- /dev/null +++ b/src/search/middleware/general-search-middleware.ts @@ -0,0 +1,174 @@ +/* +This file & middleware is for when a user requests our /search page e.g. 'docs.github.com/search?query=foo' + We make whatever search is in the ?query= parameter and attach it to req.search + req.search is then consumed by the search component in 'src/search/pages/search.tsx' + +When a user directly hits our API e.g. /api/search/v1?query=foo, they will hit the routes in ./search-routes.ts +*/ + +import got from 'got' +import { Request, Response, NextFunction } from 'express' +import { errors } from '@elastic/elasticsearch' +import statsd from '@/observability/lib/statsd.js' + +import { getPathWithoutVersion, getPathWithoutLanguage } from '@/frame/lib/path-utils' +import { getGeneralSearchResults } from '@/search/lib/get-elasticsearch-results/general-search' +import { getSearchFromRequestParams } from '@/search/lib/search-request-params/get-search-from-request-params' + +import type { ComputedSearchQueryParamsMap } from '@/search/lib/search-request-params/types' +import type { + GeneralSearchResponse, + SearchOnReqObject, + SearchTypes, + SearchValidationErrorEntry, +} from '@/search/types.js' + +interface Context { + currentVersion: string + currentLanguage: string + search: SearchOnReqObject +} + +interface CustomRequest extends Request { + pagePath: string + context: Context +} + +export default async function contextualizeGeneralSearch( + req: CustomRequest<'generalSearch'>, + res: Response, + next: NextFunction, +): Promise { + const { pagePath } = req + if (getPathWithoutLanguage(getPathWithoutVersion(pagePath)) !== '/search') { + return next() + } + + // Since this is a middleware language & version are already set in req.context via a prior middleware + const { indexName, searchParams, validationErrors } = getSearchFromRequestParams( + req, + 'generalSearch', + // Force the version and language keys to be set from the `req.context` object + { + version: req.context.currentVersion, + language: req.context.currentLanguage, + }, + ) + + if (validationErrors.map((error: SearchValidationErrorEntry) => error.key).includes('query')) { + if (Array.isArray(searchParams.query)) { + searchParams.query = searchParams.query[0] + } else if (!searchParams.query) { + searchParams.query = '' // If 'undefined' we need to cast to string + } + } + + searchParams.aggregate = ['toplevel'] + + req.context.search = { + searchParams, + validationErrors, + } + + if (!validationErrors.length && searchParams.query) { + // In local dev ELASTICSEARCH_URL may not be set, so we proxy the search to prod + if (!process.env.ELASTICSEARCH_URL) { + if (searchParams.aggregate && searchParams.toplevel && searchParams.toplevel.length > 0) { + // Do 2 searches. One without filtering to get the aggregations + const searchWithoutFilter = Object.fromEntries( + Object.entries(searchParams).filter(([key]) => key !== 'topLevel'), + ) + searchWithoutFilter.size = 0 + const { aggregations } = await getProxySearch( + searchWithoutFilter as ComputedSearchQueryParamsMap['generalSearch'], + ) + const searchWithoutAggregate = Object.fromEntries( + Object.entries(searchParams).filter(([key]) => key !== 'aggregate'), + ) + req.context.search.results = await getProxySearch( + searchWithoutAggregate as ComputedSearchQueryParamsMap['generalSearch'], + ) + req.context.search.results.aggregations = aggregations + } else { + req.context.search.results = await getProxySearch(searchParams) + } + } else { + const tags: string[] = [`indexName:${indexName}`, `toplevels:${searchParams.toplevel.length}`] + const timed = statsd.asyncTimer(getGeneralSearchResults, 'contextualize.search', tags) + const getGeneralSearchArgs = { + indexName, + searchParams, + } + try { + if (searchParams.aggregate && searchParams.toplevel && searchParams.toplevel.length > 0) { + // Do 2 searches. One without filtering to get the aggregations + const searchWithoutFilter = Object.fromEntries( + Object.entries(searchParams).filter(([key]) => key !== 'topLevel'), + ) + searchWithoutFilter.size = 0 + const { aggregations } = await timed({ + ...getGeneralSearchArgs, + searchParams: searchWithoutFilter as ComputedSearchQueryParamsMap['generalSearch'], + }) + req.context.search.results = await timed(getGeneralSearchArgs) + req.context.search.results.aggregations = aggregations + } else { + req.context.search.results = await timed(getGeneralSearchArgs) + } + } catch (error) { + // If the Elasticsearch sends a 4XX we want the user to see a 500 + if (error instanceof errors.ResponseError) { + console.error( + 'Error calling getSearchResults(%s):', + JSON.stringify({ + indexName, + searchParams, + }), + error, + ) + if (error?.meta?.body) { + console.error(`Meta:`, error.meta.body) + } + throw new Error(error.message) + } else { + throw error + } + } + } + } + + return next() +} + +const SEARCH_KEYS_TO_QUERY_STRING: (keyof ComputedSearchQueryParamsMap['generalSearch'])[] = [ + 'query', + 'version', + 'language', + 'page', + 'aggregate', + 'toplevel', + 'size', +] + +// Proxy the API endpoint with the relevant search params +async function getProxySearch( + search: ComputedSearchQueryParamsMap['generalSearch'], +): Promise { + const url = new URL('https://docs.github.com/api/search/v1') + for (const key of SEARCH_KEYS_TO_QUERY_STRING) { + const value = search[key] + if (typeof value === 'boolean') { + url.searchParams.set(key, value ? 'true' : 'false') + } else if (Array.isArray(value)) { + for (const v of value) { + url.searchParams.append(key, v) + } + } else if (typeof value === 'number') { + url.searchParams.set(key, `${value}`) + } else if (value) { + url.searchParams.set(key, value) + } + } + console.log(`Proxying search to ${url}`) + return got(url).json() +} diff --git a/src/search/middleware/get-search-request.js b/src/search/middleware/get-search-request.js deleted file mode 100644 index 05b340de00eb..000000000000 --- a/src/search/middleware/get-search-request.js +++ /dev/null @@ -1,229 +0,0 @@ -import { format } from 'node:util' - -import languages from '#src/languages/lib/languages.js' -import { allVersions } from '#src/versions/lib/all-versions.js' -import { POSSIBLE_HIGHLIGHT_FIELDS, DEFAULT_HIGHLIGHT_FIELDS } from './es-search.js' - -const DEFAULT_SIZE = 10 -const DEFAULT_AUTOCOMPLETE_SIZE = 8 -const MAX_SIZE = 50 // How much you return has a strong impact on performance -const MAX_AUTOCOMPLETE_SIZE = 10 -const DEFAULT_PAGE = 1 -const POSSIBLE_SORTS = ['best', 'relevance'] -const DEFAULT_SORT = POSSIBLE_SORTS[0] -const MAX_PAGE = 10 - -// There are some fields you can optionally include in the output. -// These are fields available in Elasticsearch that we don't include in -// the output by default. E.g. `...&include=intro` -// Requesting anything that is not in this list will result in -// a 400 Bad Request. -const V1_ADDITIONAL_INCLUDES = ['intro', 'headings', 'toplevel'] - -const V1_AGGREGATES = ['toplevel'] - -// If someone searches for `...&version=3.5` what they actually mean -// is `ghes-3.5`. This is because of legacy formatting with the old search. -// In some distant future we can clean up any client enough that this -// aliasing won't be necessary. -const versionAliases = {} -const prefixVersionAliases = {} -Object.values(allVersions).forEach((info) => { - if (info.hasNumberedReleases) { - versionAliases[info.currentRelease] = info.miscVersionName - } else { - versionAliases[info.version] = info.miscVersionName - versionAliases[info.miscVersionName] = info.miscVersionName - } - // This makes it so you can search for `?version=enterprise-server` - // and that actually means `?version=ghes` because there's an index - // called `github-autocomplete-en-ghes`. - prefixVersionAliases[info.plan] = info.shortName - prefixVersionAliases[info.shortName] = info.shortName -}) - -function getIndexPrefix() { - // This logic is mirrored in the scripts we use before running tests - // In particular, see the `index-test-fixtures` npm script. - // That's expected to be run before CI and local vitest testing. - // The reason we have a deliberately different index name (by prefix) - // for testing compared to regular operation is to make it convenient - // for engineers working on local manual testing *and* automated - // testing without have to re-index different content (e.g. fixtures - // vs real content) on the same index name. - if (process.env.NODE_ENV === 'test') return 'tests_' - - return '' -} - -class ValidationError extends Error {} - -const PARAMS = [ - { key: 'query' }, - { - key: 'version', - default_: 'dotcom', - validate: (v) => { - if (versionAliases[v] || allVersions[v]) return true - const valid = [...Object.keys(versionAliases), ...Object.keys(allVersions)] - throw new ValidationError(`'${v}' not in ${valid}`) - }, - }, - { key: 'language', default_: 'en', validate: (v) => v in languages }, - { - key: 'size', - default_: DEFAULT_SIZE, - cast: (v) => parseInt(v, 10), - validate: (v) => v >= 0 && v <= MAX_SIZE, - }, - { - key: 'page', - default_: DEFAULT_PAGE, - cast: (v) => parseInt(v, 10), - validate: (v) => v >= 1 && v <= MAX_PAGE, - }, - { key: 'sort', default_: DEFAULT_SORT, validate: (v) => POSSIBLE_SORTS.includes(v) }, - { - key: 'highlights', - default_: DEFAULT_HIGHLIGHT_FIELDS, - cast: (v) => (Array.isArray(v) ? v : [v]), - multiple: true, - validate: (v) => { - for (const highlight of v) { - if (!POSSIBLE_HIGHLIGHT_FIELDS.includes(highlight)) { - throw new ValidationError(`highlight value '${highlight}' is not valid`) - } - } - return true - }, - }, - { key: 'autocomplete', default_: false, cast: toBoolean }, - { key: 'debug', default_: process.env.NODE_ENV === 'development', cast: toBoolean }, - { - key: 'include', - default_: [], - cast: toArray, - multiple: true, - // Note: At the time of writing this general validator middleware - // doesn't yet know it's being used by the v1 version. - // But we don't have any other versions yet so no need to - // over-engineer this more. - validate: (values) => values.every((value) => V1_ADDITIONAL_INCLUDES.includes(value)), - }, - { - key: 'toplevel', - default_: [], - cast: toArray, - multiple: true, - }, - { - key: 'aggregate', - default_: [], - cast: toArray, - multiple: true, - validate: (values) => values.every((value) => V1_AGGREGATES.includes(value)), - }, -] - -const AUTOCOMPLETE_PARAMS = [ - { key: 'query' }, - { key: 'language', default_: 'en', validate: (v) => v in languages }, - { - key: 'version', - default_: 'free-pro-team', - validate: (v) => { - if (prefixVersionAliases[v] || allVersions[v]) return true - if (Object.values(prefixVersionAliases).includes(v)) return true - const valid = [ - ...Object.keys(prefixVersionAliases), - ...Object.values(prefixVersionAliases), - ...Object.keys(allVersions), - ] - throw new ValidationError(`'${v}' not in ${valid.join(', ')}`) - }, - }, - { - key: 'size', - default_: DEFAULT_AUTOCOMPLETE_SIZE, - cast: (v) => parseInt(v, 10), - validate: (v) => v >= 0 && v <= MAX_AUTOCOMPLETE_SIZE, - }, -] -export function getAutocompleteSearchFromRequest(req, force = {}) { - const { search, validationErrors } = getSearchFromRequest(req, {}, AUTOCOMPLETE_PARAMS) - if (validationErrors.length === 0) { - const version = prefixVersionAliases[search.version] || allVersions[search.version].shortName - search.indexName = `${getIndexPrefix()}github-autocomplete-${search.language}-${version}` - } - return { search, validationErrors } -} - -export function getSearchFromRequest(req, force = {}, params = PARAMS) { - const search = {} - const validationErrors = [] - - for (const { key, default_, cast, validate, multiple } of params) { - // This is necessary because when the version or language comes from - // the pathname, we don't want pick these up from the query string. - // This function gets used by /$locale/$version/search - // *and* /api/search/v1?language=$locale&version=$version - if (key in force) { - search[key] = force[key] - continue - } - - let value = req.query[key] - if (!value || (typeof value === 'string' && !value.trim())) { - if (default_ === undefined) { - // no value and no default, bad! - validationErrors.push({ error: `No truthy value for key '${key}'`, key }) - continue - } - value = default_ - } - if (cast) { - value = cast(value) - } - try { - if (validate && !validate(value)) { - validationErrors.push({ - error: format('Not a valid value (%O) for key %O', value, key), - key, - }) - } - } catch (err) { - if (err instanceof ValidationError) { - validationErrors.push({ error: err.toString(), field: key }) - } else { - throw err - } - } - if (!multiple && Array.isArray(value)) { - validationErrors.push({ - error: format('Cannot have multiple values (%O) for key %O', value, key), - key, - }) - } - - search[key] = value - } - - if (!validationErrors.length) { - const version = - prefixVersionAliases[search.version] || - versionAliases[search.version] || - allVersions[search.version].miscVersionName - search.indexName = `${getIndexPrefix()}github-docs-${version}-${search.language}` // github-docs-ghes-3.5-en - } - - return { search, validationErrors } -} - -function toBoolean(value) { - if (value === 'true' || value === '1') return true - return false -} - -function toArray(value) { - return Array.isArray(value) ? value : [value] -} diff --git a/src/search/middleware/search-routes.ts b/src/search/middleware/search-routes.ts new file mode 100644 index 000000000000..db84b46639f3 --- /dev/null +++ b/src/search/middleware/search-routes.ts @@ -0,0 +1,150 @@ +/* + This file and the routes included are for the /search endpoint of our API + + For general search (client searches on docs.github.com) we use the middleware in ./general-search-middleware to get the search results +*/ +import express, { Request, Response } from 'express' + +import FailBot from '@/observability/lib/failbot.js' +import { searchCacheControl } from '@/frame/middleware/cache-control.js' +import catchMiddlewareError from '@/observability/middleware/catch-middleware-error.js' +import { + setFastlySurrogateKey, + SURROGATE_ENUMS, +} from '@/frame/middleware/set-fastly-surrogate-key.js' +import { getAutocompleteSearchResults } from '@/search/lib/get-elasticsearch-results/general-autocomplete' +import { getAISearchAutocompleteResults } from '@/search/lib/get-elasticsearch-results/ai-search-autocomplete' +import { getSearchFromRequestParams } from '@/search/lib/search-request-params/get-search-from-request-params' +import { getGeneralSearchResults } from '@/search/lib/get-elasticsearch-results/general-search' + +const router = express.Router() + +router.get('/legacy', (req: Request, res: Response) => { + res.status(410).send('Use /api/search/v1 instead.') +}) + +router.get( + '/v1', + catchMiddlewareError(async (req: Request, res: Response) => { + const { indexName, searchParams, validationErrors } = getSearchFromRequestParams( + req, + 'generalSearch', + ) + if (validationErrors.length) { + // We only send the first validation error to the user + return res.status(400).json(validationErrors[0]) + } + + const getResultOptions = { + indexName, + searchParams, + } + try { + const { meta, hits, aggregations } = await getGeneralSearchResults(getResultOptions) + + if (process.env.NODE_ENV !== 'development') { + searchCacheControl(res) + // We can cache this without purging it after every deploy + // because the API search is only used as a proxy for local + // and preview environments. + setFastlySurrogateKey(res, SURROGATE_ENUMS.MANUAL) + } + + res.status(200).json({ meta, hits, aggregations }) + } catch (error) { + await handleGetSearchResultsError(req, res, error, getResultOptions) + } + }), +) + +router.get( + '/autocomplete/v1', + catchMiddlewareError(async (req: Request, res: Response) => { + const { + indexName, + validationErrors, + searchParams: { query, size }, + } = getSearchFromRequestParams(req, 'generalAutocomplete') + if (validationErrors.length) { + return res.status(400).json(validationErrors[0]) + } + + const options = { + indexName, + query, + size, + } + try { + const { meta, hits } = await getAutocompleteSearchResults(options) + + if (process.env.NODE_ENV !== 'development') { + searchCacheControl(res) + setFastlySurrogateKey(res, SURROGATE_ENUMS.MANUAL) + } + + res.status(200).json({ meta, hits }) + } catch (error) { + await handleGetSearchResultsError(req, res, error, options) + } + }), +) + +router.get( + '/ai-search-autocomplete/v1', + catchMiddlewareError(async (req: Request, res: Response) => { + const { + indexName, + validationErrors, + searchParams: { query, size }, + } = getSearchFromRequestParams(req, 'aiSearchAutocomplete') + if (validationErrors.length) { + return res.status(400).json(validationErrors[0]) + } + + const getResultOptions = { + indexName, + query, + size, + } + try { + const { meta, hits } = await getAISearchAutocompleteResults(getResultOptions) + + if (process.env.NODE_ENV !== 'development') { + searchCacheControl(res) + setFastlySurrogateKey(res, SURROGATE_ENUMS.MANUAL) + } + + res.status(200).json({ meta, hits }) + } catch (error) { + await handleGetSearchResultsError(req, res, error, getResultOptions) + } + }), +) + +async function handleGetSearchResultsError(req: Request, res: Response, error: any, options: any) { + if (process.env.NODE_ENV === 'development') { + console.error(`Error calling getSearchResults(${options})`, error) + } else { + const reports = FailBot.report(error, { url: req.url, ...options }) + if (reports) await Promise.all(reports) + } + res.status(500).json({ error: error.message }) +} + +// Redirects for latest versions +router.get('/', (req: Request, res: Response) => { + res.redirect(307, req.originalUrl.replace('/search', '/search/v1')) +}) + +router.get('/autocomplete', (req: Request, res: Response) => { + res.redirect(307, req.originalUrl.replace('/search/autocomplete', '/search/autocomplete/v1')) +}) + +router.get('/ai-search-autocomplete', (req: Request, res: Response) => { + res.redirect( + 307, + req.originalUrl.replace('/search/ai-search-autocomplete', '/search/ai-search-autocomplete/v1'), + ) +}) + +export default router diff --git a/src/search/middleware/search.js b/src/search/middleware/search.js deleted file mode 100644 index 8650b68acccc..000000000000 --- a/src/search/middleware/search.js +++ /dev/null @@ -1,160 +0,0 @@ -import express from 'express' - -import FailBot from '#src/observability/lib/failbot.js' -import { searchCacheControl } from '#src/frame/middleware/cache-control.js' -import catchMiddlewareError from '#src/observability/middleware/catch-middleware-error.js' -import { - setFastlySurrogateKey, - SURROGATE_ENUMS, -} from '#src/frame/middleware/set-fastly-surrogate-key.js' -import { getAutocompleteSearchResults, getSearchResults } from './es-search.js' -import { getAutocompleteSearchFromRequest, getSearchFromRequest } from './get-search-request.js' - -const router = express.Router() - -router.get('/legacy', (req, res) => { - res.status(410).send('Use /api/search/v1 instead.') -}) - -export const validationMiddleware = (req, res, next) => { - const { search, validationErrors } = getSearchFromRequest(req) - if (validationErrors.length) { - // There might be multiple things bad about the query parameters, - // but we send a 400 on the first possible one in the API. - return res.status(400).json(validationErrors[0]) - } - - req.search = search - return next() -} - -router.get( - '/v1', - validationMiddleware, - catchMiddlewareError(async function search(req, res) { - const { - indexName, - query, - autocomplete, - page, - size, - debug, - sort, - highlights, - include, - toplevel, - aggregate, - } = req.search - - const options = { - indexName, - query, - page, - size, - debug, - sort, - highlights, - usePrefixSearch: autocomplete, - include, - toplevel, - aggregate, - } - try { - const { meta, hits, aggregations } = await getSearchResults(options) - - if (process.env.NODE_ENV !== 'development') { - searchCacheControl(res) - // We can cache this without purging it after every deploy - // because the API search is only used as a proxy for local - // and preview environments. - setFastlySurrogateKey(res, SURROGATE_ENUMS.MANUAL) - } - - // The v1 version of the output matches perfectly what comes out - // of the getSearchResults() function. - res.status(200).json({ meta, hits, aggregations }) - } catch (error) { - // If getSearchResult() throws an error that might be 404 inside - // elasticsearch, if we don't capture that here, it will propagate - // to the next middleware. - await handleGetSearchResultsError(req, res, error, options) - } - }), -) - -export const autocompleteValidationMiddleware = (req, res, next) => { - const { search, validationErrors } = getAutocompleteSearchFromRequest(req) - if (validationErrors.length) { - // There might be multiple things bad about the query parameters, - // but we send a 400 on the first possible one in the API. - return res.status(400).json(validationErrors[0]) - } - - req.search = search - return next() -} - -router.get( - '/autocomplete/v1', - autocompleteValidationMiddleware, - catchMiddlewareError(async (req, res) => { - const { indexName, query, size } = req.search - - const options = { - indexName, - query, - size, - } - try { - const { meta, hits } = await getAutocompleteSearchResults(options) - - if (process.env.NODE_ENV !== 'development') { - searchCacheControl(res) - // We can cache this without purging it after every deploy - // because the API search is only used as a proxy for local - // and preview environments. - setFastlySurrogateKey(res, SURROGATE_ENUMS.MANUAL) - } - - // The v1 version of the output matches perfectly what comes out - // of the getSearchResults() function. - res.status(200).json({ meta, hits }) - } catch (error) { - // If getSearchResult() throws an error that might be 404 inside - // elasticsearch, if we don't capture that here, it will propagate - // to the next middleware. - await handleGetSearchResultsError(req, res, error, options) - } - }), -) - -// We have more than one place where we do `try{...} catch error( THIS )` -// which is slightly different depending on the "sub-version" (e.g. /legacy) -// This function is a single place to take care of all of these error handlings -async function handleGetSearchResultsError(req, res, error, options) { - if (process.env.NODE_ENV === 'development') { - console.error(`Error calling getSearchResults(${options})`, error) - } else { - const reports = FailBot.report(error, Object.assign({ url: req.url }, options)) - // It might be `undefined` if no backends are configured which - // is likely when using production NODE_ENV on your laptop - // where you might not have a HAYSTACK_URL configured. - if (reports) await Promise.all(reports) - } - res.status(500).json({ error: error.message }) -} - -// Alias for the latest version -router.get('/', (req, res) => { - // At the time of writing, the latest version is v1. (July 2022) - // Use `req.originalUrl` because this router is "self contained" - // which means that `req.url` will be `/` in this context. - res.redirect(307, req.originalUrl.replace('/search', '/search/v1')) -}) - -// Alias for the latest autocomplete version -router.get('/autocomplete', (req, res) => { - res.redirect(307, req.originalUrl.replace('/search/autocomplete', '/search/autocomplete/v1')) -}) - -export default router diff --git a/src/search/pages/search.tsx b/src/search/pages/search.tsx index 42965548ae69..55c0333a2e25 100644 --- a/src/search/pages/search.tsx +++ b/src/search/pages/search.tsx @@ -7,9 +7,10 @@ import { addUINamespaces, } from 'src/frame/components/context/MainContext' import { DefaultLayout } from 'src/frame/components/DefaultLayout' -import type { SearchT } from 'src/search/components/types' -import { SearchContext, SearchContextT } from 'src/search/components/context/SearchContext' +import { SearchContext } from 'src/search/components/context/SearchContext' import { Search } from 'src/search/components/index' +import { SearchOnReqObject } from 'src/search/types' +import type { SearchContextT } from 'src/search/components/types' type Props = { mainContext: MainContextT @@ -40,6 +41,8 @@ export const getServerSideProps: GetServerSideProps = async (context) => throw new Error('Expected req.context to be populated with .search') } + const searchObject = req.context.search as SearchOnReqObject<'generalSearch'> + // The `req.context.search` is similar to what's needed to React // render the search result page. // But it contains information (from the contextualizing) that is @@ -48,24 +51,24 @@ export const getServerSideProps: GetServerSideProps = async (context) => // `page` and `indexName` which was useful when it made the actual // Elasticsearch query. But it's not needed to render the results. // We explicitly pick out the parts that are needed, only. - const search: SearchT = { - search: { - query: req.context.search.search.query, - debug: req.context.search.search.debug, + const search: SearchContextT['search'] = { + searchParams: { + query: searchObject.searchParams.query, + debug: searchObject.searchParams.debug, }, - validationErrors: req.context.search.validationErrors, + validationErrors: searchObject.validationErrors, } // If there are no results (e.g. /en/search?query=) from the // contextualizing, then `req.context.search.results` will // be `undefined` which can't be serialized as a prop, using JSON.stringify. - if (req.context.search.results) { + if (searchObject.results) { search.results = { - meta: req.context.search.results.meta, - hits: req.context.search.results.hits, + meta: searchObject.results.meta, + hits: searchObject.results.hits, // Use `null` instead of `undefined` for JSON serialization. // The only reason it would ever not be truthy is if the aggregates // functionality is not enabled for this version. - aggregations: req.context.search.results.aggregations || null, + aggregations: searchObject.results.aggregations || null, } } diff --git a/src/search/scripts/analyze-text.js b/src/search/scripts/analyze-text.ts similarity index 61% rename from src/search/scripts/analyze-text.js rename to src/search/scripts/analyze-text.ts index b314a2bf2439..668836734c5c 100755 --- a/src/search/scripts/analyze-text.js +++ b/src/search/scripts/analyze-text.ts @@ -1,24 +1,19 @@ -#!/usr/bin/env node - -// [start-readme] -// -// See how a piece of text gets turned into tokens by the different -// analyzers. +// See how a piece of text gets turned into tokens by the different analyzers. // Requires that the index exists in Elasticsearch. // // Example: // -// npm run analyze-text "my words" to tokenize -// -// [end-readme] +// npm run analyze-text -- -V dotcom -l en "The name of the wind" import { Client } from '@elastic/elasticsearch' -import { program, Option } from 'commander' +import { Command, Option } from 'commander' import chalk from 'chalk' import dotenv from 'dotenv' -import { languageKeys } from '#src/languages/lib/languages.js' -import { allVersions } from '#src/versions/lib/all-versions.js' +import { languageKeys } from '@/languages/lib/languages.js' +import { allVersions } from '@/versions/lib/all-versions.js' + +import type { IndicesAnalyzeAnalyzeToken } from '@elastic/elasticsearch/lib/api/types' // Now you can optionally have set the ELASTICSEARCH_URL in your .env file. dotenv.config() @@ -38,16 +33,28 @@ dotenv.config() // // We need this later to be able to map CLI arguments to what the // records are called when found on disk. -const shortNames = Object.fromEntries( - Object.values(allVersions).map((info) => { - const shortName = info.hasNumberedReleases - ? info.miscBaseName + info.currentRelease - : info.miscBaseName - return [shortName, info] - }), -) +const shortNames: Record = + Object.fromEntries( + Object.values(allVersions).map((info) => { + const shortName = info.hasNumberedReleases + ? `${info.miscBaseName}${info.currentRelease}` + : info.miscBaseName + return [shortName, info] + }), + ) -const allVersionKeys = Object.keys(shortNames) +const allVersionKeys: string[] = Object.keys(shortNames) + +interface Options { + verbose?: boolean + version?: string + language?: string + notLanguage?: string + elasticsearchUrl?: string + indexPrefix?: string +} + +const program = new Command() program .description('Analyze text into tokens') @@ -56,21 +63,29 @@ program .addOption( new Option('-l, --language ', 'Which language to focus on').choices(languageKeys), ) + .option('--not-language ', 'Exclude a specific language') .option('-u, --elasticsearch-url ', 'If different from $ELASTICSEARCH_URL') + .option('--index-prefix ', 'Prefix for the index name') .argument('', 'text to tokenize') .parse(process.argv) -main(program.opts(), program.args) +const options = program.opts() +const args: string[] = program.args + +main(options, args).catch((err) => { + console.error(chalk.red('Error:'), err) + process.exit(1) +}) -async function main(opts, args) { +async function main(opts: Options, args: string[]): Promise { const texts = [args.join(' ')] if (!opts.elasticsearchUrl && !process.env.ELASTICSEARCH_URL) { throw new Error( - 'Must passed the elasticsearch URL option or ' + + 'Must pass the elasticsearch URL option or ' + 'set the environment variable ELASTICSEARCH_URL', ) } - let node = opts.elasticsearchUrl || process.env.ELASTICSEARCH_URL + let node = opts.elasticsearchUrl || process.env.ELASTICSEARCH_URL! // Allow the user to lazily set it to `localhost:9200` for example. if (!node.startsWith('http') && !node.startsWith('://') && node.split(':').length === 2) { @@ -79,15 +94,15 @@ async function main(opts, args) { try { const parsed = new URL(node) - if (!parsed.hostname) throw new Error('no valid hostname') + if (!parsed.hostname) throw new Error('No valid hostname') } catch (err) { - console.error(chalk.bold('URL for Elasticsearch not a valid URL', err)) + console.error(chalk.bold('URL for Elasticsearch not a valid URL'), err) return } const { verbose, language, notLanguage } = opts - // The notLanguage is useful you want to, for example, index all languages + // The notLanguage is useful if you want to, for example, index all languages // *except* English. if (language && notLanguage) { throw new Error("Can't combine --language and --not-language") @@ -116,29 +131,32 @@ async function main(opts, args) { const indexName = `${prefix}github-docs-${versionKey}-${languageKey}` console.log(chalk.yellow(`Analyzing in ${chalk.bold(indexName)}`)) - await analyzeVersion(client, texts, indexName, verbose) + await analyzeVersion(client, texts, indexName) } -function safeUrlDisplay(url) { +function safeUrlDisplay(url: string): string { const parsed = new URL(url) if (parsed.password) { parsed.password = '***' } if (parsed.username) { - parsed.username = parsed.username.slice(0, 4) + '***' + parsed.username = `${parsed.username.slice(0, 4)}***` } return parsed.toString() } -async function analyzeVersion(client, texts, indexName, verbose = false) { + +async function analyzeVersion(client: Client, texts: string[], indexName: string): Promise { for (const text of texts) { console.log(`RAW TEXT: 〝${chalk.italic(text)}〞`) for (const analyzer of ['text_analyzer_explicit', 'text_analyzer', 'standard']) { console.log('ANALYZER:', chalk.bold(analyzer)) - const { tokens } = await client.indices.analyze({ + const response = await client.indices.analyze({ index: indexName, body: { analyzer, text }, }) - const tokenWords = tokens.map((token) => token.token) + + const tokens: IndicesAnalyzeAnalyzeToken[] | undefined = response.tokens + const tokenWords: string[] = tokens?.map((token) => token.token) || [] console.log(tokenWords) } } diff --git a/src/search/scripts/index-elasticsearch.js b/src/search/scripts/index-elasticsearch.js deleted file mode 100755 index ac78d312bd7a..000000000000 --- a/src/search/scripts/index-elasticsearch.js +++ /dev/null @@ -1,575 +0,0 @@ -#!/usr/bin/env node - -// [start-readme] -// -// Creates Elasticsearch index, populates from records, -// moves the index alias, deletes old indexes. -// -// [end-readme] - -import fs from 'fs/promises' -import path from 'path' - -import { Client, errors } from '@elastic/elasticsearch' -import { program, Option, InvalidArgumentError } from 'commander' -import chalk from 'chalk' -import dotenv from 'dotenv' - -import { retryOnErrorTest } from './retry-on-error-test.js' -import { languageKeys } from '#src/languages/lib/languages.js' -import { allVersions } from '#src/versions/lib/all-versions.js' - -// Now you can optionally have set the ELASTICSEARCH_URL in your .env file. -dotenv.config() - -// Create an object that maps the "short name" of a version to -// all information about it. E.g. -// -// { -// 'ghes-3.5': { -// hasNumberedReleases: true, -// currentRelease: '3.5', -// version: 'enterprise-server@3.5', -// miscBaseName: 'ghes-' -// ... -// }, -// ... -// -// We need this later to be able to map CLI arguments to what the -// records are called when found on disk. -const shortNames = Object.fromEntries( - Object.values(allVersions).map((info) => { - const shortName = info.hasNumberedReleases - ? info.miscBaseName + info.currentRelease - : info.miscBaseName - return [shortName, info] - }), -) - -const allVersionKeys = Object.keys(shortNames) - -const DEFAULT_SLEEPTIME_SECONDS = 30 - -program - .description('Creates Elasticsearch index from records') - .option('-v, --verbose', 'Verbose outputs') - .addOption(new Option('-V, --version [VERSION...]', 'Specific versions').choices(allVersionKeys)) - .addOption( - new Option('-l, --language ', 'Which languages to focus on').choices(languageKeys), - ) - .addOption( - new Option('--not-language ', 'Specific language to omit').choices(languageKeys), - ) - .option('-u, --elasticsearch-url ', 'If different from $ELASTICSEARCH_URL') - .option('-p, --index-prefix ', 'Index string to put before index name') - .option( - '-s, --stagger-seconds ', - 'Number of seconds to sleep between each bulk operation', - (value) => { - const parsed = parseInt(value, 10) - if (isNaN(parsed)) { - throw new InvalidArgumentError('Not a number.') - } - return parsed - }, - ) - .option( - '-r, --retries ', - 'Number of retry attempts on recoverable network errors', - (value) => { - const parsed = parseInt(value, 10) - if (isNaN(parsed)) { - throw new InvalidArgumentError('Not a number.') - } - return parsed - }, - ) - .option( - '--sleep-time ', - `Number of seconds to sleep between each retry attempt (defaults to ${DEFAULT_SLEEPTIME_SECONDS})`, - (value) => { - const parsed = parseInt(value, 10) - if (isNaN(parsed)) { - throw new InvalidArgumentError('Not a number.') - } - return parsed - }, - ) - .argument('', 'where the indexable files are') - .parse(process.argv) - -main(program.opts(), program.args) - -async function main(opts, args) { - if (!args.length) { - throw new Error('Must pass the source as the first argument') - } - - const { verbose, language, notLanguage, elasticsearchUrl } = opts - - if (!elasticsearchUrl && !process.env.ELASTICSEARCH_URL) { - throw new Error( - 'Must passed the elasticsearch URL option or ' + - 'set the environment variable ELASTICSEARCH_URL', - ) - } - let node = elasticsearchUrl || process.env.ELASTICSEARCH_URL - - // Allow the user to lazily set it to `localhost:9200` for example. - if (!node.startsWith('http') && !node.startsWith('://') && node.split(':').length === 2) { - node = `http://${node}` - } - - try { - const parsed = new URL(node) - if (!parsed.hostname) throw new Error('no valid hostname') - } catch (err) { - console.error(chalk.bold('URL for Elasticsearch not a valid URL', err)) - throw err - } - - // The notLanguage is useful you want to, for example, index all languages - // *except* English. - if (language && notLanguage) { - throw new Error("Can't combine --language and --not-language") - } - - if (verbose) { - console.log(`Connecting to ${chalk.bold(safeUrlDisplay(node))}`) - } - const sourceDirectory = args[0] - try { - await fs.stat(sourceDirectory) - } catch (error) { - if (error.code === 'ENOENT') { - throw new Error(`The specified directory '${sourceDirectory}' does not exist.`) - } - throw error - } - - try { - await indexAll(node, sourceDirectory, opts) - } catch (error) { - // If any error is thrown from within the SDK, that error object will - // contain a `Connection` object which, when printed, can reveal the - // username/password or the base64 Basic auth credentials. - // So we want to carefully re-throw it so it only contains the minimal - // information for debugging without exposing the Connection credentials - // in Actions logs. - if (error instanceof errors.ElasticsearchClientError) { - // All ElasticsearchClientError error subclasses have a `name` and - // `message` but only some have a `meta`. - if (error.meta) { - console.error('Error meta: %O', error.meta) - } - throw new Error(error.message) - } - // If any other error happens that isn't from the elasticsearch SDK, - // let it bubble up. - throw error - } -} - -const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)) - -async function indexAll(node, sourceDirectory, opts) { - const client = new Client({ node }) - - const { language, verbose, notLanguage, indexPrefix, staggerSeconds } = opts - - let version - if ('version' in opts) { - version = opts.version - if (process.env.VERSION) { - console.warn( - `'version' specified as argument ('${version}') AND environment variable ('${process.env.VERSION}')`, - ) - } - } else { - if (process.env.VERSION && process.env.VERSION !== 'all') { - version = process.env.VERSION - if (!allVersionKeys.includes(version)) { - throw new Error( - `Environment variable 'VERSION' (${version}) is not recognized. Must be one of ${allVersionKeys}`, - ) - } - } - } - let versionKeys = allVersionKeys - // If it came from the `--version` argument parsing, it might be a string - // or an array of strings because it uses `--version [VERSION...]`. - if (version) { - if (Array.isArray(version)) { - versionKeys = version - } else { - versionKeys = [version] - } - } - - // This will throw if it can't ping - await client.ping() - - const languages = - language || languageKeys.filter((lang) => !notLanguage || !notLanguage.includes(lang)) - if (verbose) { - console.log(`Indexing on languages ${chalk.bold(languages.join(', '))}`) - } - - const prefix = indexPrefix ? `${indexPrefix}_` : '' - - for (const language of languages) { - let count = 0 - for (const versionKey of versionKeys) { - console.log(chalk.yellow(`Indexing ${chalk.bold(versionKey)} in ${chalk.bold(language)}`)) - const indexName = `${prefix}github-docs-${versionKey}-${language}` - - const t0 = new Date() - await indexVersion(client, indexName, versionKey, language, sourceDirectory, opts) - const t1 = new Date() - console.log(chalk.green(`Finished indexing ${indexName}. Took ${formatTime(t1 - t0)}`)) - if (verbose) { - console.log(`To view index: ${safeUrlDisplay(node + `/${indexName}`)}`) - console.log(`To search index: ${safeUrlDisplay(node + `/${indexName}/_search`)}`) - } - count++ - // console.log({ count, versionKeysLength: versionKeys.length }) - if (staggerSeconds && count < versionKeys.length - 1) { - console.log(`Sleeping for ${staggerSeconds} seconds...`) - await sleep(1000 * staggerSeconds) - } - // A bit of visual separation betweeen each version - console.log('') - } - } -} - -function safeUrlDisplay(url) { - const parsed = new URL(url) - if (parsed.password) { - parsed.password = '***' - } - if (parsed.username) { - parsed.username = parsed.username.slice(0, 4) + '***' - } - return parsed.toString() -} - -// Return '20220719012012' if the current date is -// 2022-07-19T01:20:12.172Z. Note how the 6th month (July) becomes -// '07'. All numbers become 2 character zero-padding strings individually. -function utcTimestamp() { - const d = new Date() - - return ( - [ - `${d.getUTCFullYear()}`, - d.getUTCMonth() + 1, - d.getUTCDate(), - d.getUTCHours(), - d.getUTCMinutes(), - d.getUTCSeconds(), - ] - // If it's a number make it a zero-padding 2 character string - .map((x) => (typeof x === 'number' ? ('0' + x).slice(-2) : x)) - .join('') - ) -} - -// Consider moving this to lib -async function indexVersion(client, indexName, version, language, sourceDirectory, opts) { - const { verbose } = opts - - // Note, it's a bit "weird" that numbered releases versions are - // called the number but that's the convention the previous - // search backend used - const indexVersionName = shortNames[version].hasNumberedReleases - ? shortNames[version].currentRelease - : shortNames[version].miscBaseName - const recordsName = `github-docs-${indexVersionName}-${language}` - - const records = await loadRecords(recordsName, sourceDirectory) - - const thisAlias = `${indexName}__${utcTimestamp()}` - - // CREATE INDEX - const settings = { - analysis: { - char_filter: { - // This will turn `runs-on` into `runs_on` so that it can't be - // tokenized to `runs` because `on` is a stop word. - // It also means that prose terms, in English, like `opt-in` - // not be matched if someone searches for `opt in`. But this - // is why we have multiple different analyzers. So it becomes - // `opt_in` in the `text_analyzer_explicit` analyzer, but is - // left as `opt` in the `text_analyzer` analyzer. - hyphenation_filter: { - type: 'mapping', - mappings: ['- => _'], - }, - }, - analyzer: { - // We defined to analyzers. Both based on a "common core" with the - // `standard` tokenizer. But the second one adds Snowball filter. - // That means the tokenization of "Dependency naming" becomes - // `[dependency, naming]` in the explicit one and `[depend, name]` - // in the Snowball one. - // We do this to give a chance to boost the more exact spelling a - // bit higher with the assumption that if the user knew exactly - // what it was called, we should show that higher. - // A great use-case of this when users search for keywords that are - // code words like `dependency-name`. - text_analyzer_explicit: { - char_filter: ['hyphenation_filter'], - filter: ['lowercase', 'stop', 'asciifolding'], - tokenizer: 'standard', - type: 'custom', - }, - text_analyzer: { - filter: ['lowercase', 'stop', 'asciifolding'], - tokenizer: 'standard', - type: 'custom', - }, - }, - filter: { - // Will later, conditionally, put the snowball configuration here. - }, - }, - } - const snowballLanguage = getSnowballLanguage(language) - if (snowballLanguage) { - settings.analysis.analyzer.text_analyzer.filter.push('languaged_snowball') - settings.analysis.filter.languaged_snowball = { - type: 'snowball', - language: snowballLanguage, - } - } else { - if (verbose) { - console.warn(`No snowball language for '${language}'`) - } - } - - await client.indices.create({ - index: thisAlias, - mappings: { - properties: { - url: { type: 'keyword' }, - title: { - type: 'text', - analyzer: 'text_analyzer', - norms: false, - // This is used for fast highlighting. Uses more space but makes - // the searches faster. - term_vector: 'with_positions_offsets', - }, - title_explicit: { type: 'text', analyzer: 'text_analyzer_explicit', norms: false }, - content: { - type: 'text', - analyzer: 'text_analyzer', - // This is used for fast highlighting. Uses more space but makes - // the searches faster. - term_vector: 'with_positions_offsets', - }, - content_explicit: { - type: 'text', - analyzer: 'text_analyzer_explicit', - // This is used for fast highlighting. Uses more space but makes - // the searches faster. - term_vector: 'with_positions_offsets', - }, - headings: { type: 'text', analyzer: 'text_analyzer', norms: false }, - headings_explicit: { type: 'text', analyzer: 'text_analyzer_explicit', norms: false }, - breadcrumbs: { type: 'text' }, - popularity: { type: 'float' }, - intro: { type: 'text' }, - // Use 'keyword' because it's faster to index and (more importantly) - // faster to search on. It would be different if it was something - // users could type in into a text input. - toplevel: { type: 'keyword' }, - }, - }, - settings, - }) - - // POPULATE - const allRecords = Object.values(records).sort((a, b) => b.popularity - a.popularity) - const operations = allRecords.flatMap((doc) => { - const { title, objectID, content, breadcrumbs, headings, intro, toplevel } = doc - const contentEscaped = escapeHTML(content) - const headingsEscaped = escapeHTML(headings) - const record = { - url: objectID, - title, - title_explicit: title, - content: contentEscaped, - content_explicit: contentEscaped, - breadcrumbs, - headings: headingsEscaped, - headings_explicit: headingsEscaped, - // This makes sure the popularities are always greater than 1. - // Generally the 'popularity' is a ratio where the most popular - // one of all is 1.0. - // By making it >=1.0 when we multiply a relevance score, - // you never get a product of 0.0. - popularity: doc.popularity + 1, - intro, - toplevel, - } - return [{ index: { _index: thisAlias } }, record] - }) - - const bulkOptions = { - // Default is 'false'. - // It means that the index is NOT refreshed as documents are inserted. - // Which makes sense in our case because we do not intend to search on - // this index until after we've pointed the alias to this new index. - refresh: false, - // Default is '1m' but we have no reason *not* to be patient. It's run - // by a bot on a schedeule (GitHub Actions). - timeout: '5m', - } - - const attempts = opts.retries || 0 - const sleepTime = (opts.sleepTime || DEFAULT_SLEEPTIME_SECONDS) * 1000 - - console.log(`About to bulk index ${allRecords.length.toLocaleString()} records with retry %O`, { - attempts, - sleepTime, - }) - const t0 = new Date() - const bulkResponse = await retryOnErrorTest( - (error) => { - // Rate limiting can happen when you're indexing too much at - // same time. - return error instanceof errors.ResponseError && error.meta.statusCode === 429 - }, - () => client.bulk({ operations, ...bulkOptions }), - { - attempts, - sleepTime, - onError: (_, attempts, sleepTime) => { - console.warn( - chalk.yellow( - `Failed to bulk index ${indexName}. Will attempt ${attempts} more times (after ${ - sleepTime / 1000 - }s sleep).`, - ), - ) - }, - }, - ) - - if (bulkResponse.errors) { - // Some day, when we're more confident how and why this might happen - // we can rewrite this code to "massage" the errors better. - // For now, if it fails, it's "OK". It means we won't be proceeding, - // an error is thrown in Actions and we don't have to worry about - // an incompletion index. - console.error(`Bulk response errors: ${bulkResponse.errors}`) - throw new Error('Bulk errors happened.') - } - const t1 = new Date() - console.log(`Bulk indexed ${thisAlias}. Took ${formatTime(t1 - t0)}`) - - // The counting of documents in the index is async and can take a while - // to reflect. So send count requests until we get the right number. - let documentsInIndex = 0 - let countAttempts = 3 - while (documentsInIndex < allRecords.length) { - const { count } = await client.count({ index: thisAlias }) - documentsInIndex = count - if (documentsInIndex >= allRecords.length) break - countAttempts-- - if (!countAttempts) { - console.log(`After ${countAttempts} attempts still haven't matched the expected number.`) - break - } - await sleep(1000) - } - - console.log( - `Documents now in ${chalk.bold(thisAlias)}: ${chalk.bold(documentsInIndex.toLocaleString())}`, - ) - - // To perform an atomic operation that creates the new alias and removes - // the old indexes, we can use the updateAliases API with a body that - // includes an "actions" array. The array includes the added alias - // and the removed indexes. If any of the actions fail, none of the operations - // are performed. - // https://www.elastic.co/guide/en/elasticsearch/reference/master/indices-aliases.html - const aliasUpdates = [ - { - add: { - index: thisAlias, - alias: indexName, - }, - }, - ] - console.log(`Alias ${indexName} -> ${thisAlias}`) - - console.log('About to get indices with retry %O', { attempts, sleepTime }) - const indices = await retryOnErrorTest( - (error) => { - // 404 can happen when you're trying to get an index that - // doesn't exist. ...yet! - return error instanceof errors.ResponseError && error.meta.statusCode === 404 - }, - () => client.cat.indices({ format: 'json' }), - { - attempts, - sleepTime, - onError: (error, attempts, sleepTime) => { - console.warn( - chalk.yellow( - `Failed to get index ${indexName} (${ - error.message || error.toString() - }). Will attempt ${attempts} more times (after ${formatTime(sleepTime)}s sleep).`, - ), - ) - }, - }, - ) - for (const index of indices) { - if (index.index !== thisAlias && index.index.startsWith(indexName)) { - aliasUpdates.push({ remove_index: { index: index.index } }) - console.log('Deleting index', index.index) - } - } - if (verbose) console.log('Updating alias actions:', aliasUpdates) - await client.indices.updateAliases({ body: { actions: aliasUpdates } }) -} - -function escapeHTML(content) { - return content.replace(//g, '>').replace(/"/g, '"') -} - -async function loadRecords(indexName, sourceDirectory) { - const filePath = path.join(sourceDirectory, `${indexName}-records.json`) - const payload = await fs.readFile(filePath) - return JSON.parse(payload) -} - -function getSnowballLanguage(language) { - // Based on https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-snowball-tokenfilter.html - // Note, not all languages are supported. So this function might return - // undefined. That implies that you can't use snowballing. - return { - en: 'English', - fr: 'French', - es: 'Spanish', - ru: 'Russian', - it: 'Italian', - de: 'German', - pt: 'Portuguese', - }[language] -} - -function formatTime(ms) { - if (ms < 1000) { - return `${ms.toFixed(1)}ms` - } - const seconds = ms / 1000 - if (seconds > 60) { - return `${Math.round(seconds / 60)}m${Math.round(seconds % 60)}s` - } - return `${seconds.toFixed(1)}s` -} diff --git a/src/search/scripts/index-test-fixtures.sh b/src/search/scripts/index-test-fixtures.sh index 235eb915d28f..3091b5e84774 100755 --- a/src/search/scripts/index-test-fixtures.sh +++ b/src/search/scripts/index-test-fixtures.sh @@ -6,7 +6,10 @@ set -e # For general site-search -npm run index-elasticsearch -- -l en -l ja -V ghec -V dotcom --index-prefix tests -- src/search/tests/fixtures/search-indexes +npm run index-general-search -- src/search/tests/fixtures/search-indexes -l en -l ja -V ghec -V fpt --index-prefix tests -# For autocomplete search -npm run index -- autocomplete src/search/tests/fixtures/data -l en -l ja -v fpt -v ghec --index-prefix tests +# For general autocomplete search +npm run index-general-autocomplete -- src/search/tests/fixtures/data -l en -l ja -v fpt -v ghec --index-prefix tests + +# For AI search autocomplete +npm run index-ai-search-autocomplete -- src/search/tests/fixtures/data -l en -v fpt -v ghec --index-prefix tests diff --git a/src/search/scripts/index/README.md b/src/search/scripts/index/README.md new file mode 100644 index 000000000000..a9f9432c0b0f --- /dev/null +++ b/src/search/scripts/index/README.md @@ -0,0 +1,24 @@ +# Elastic Search Indexing + +Elasticsearch uses indexes to store the data that is used to determine search results. + +We use this scripts in this directory to index our Elasticsearch instances. + +In production, the indexing happens in the GitHub workflows: `index-autocomplete-search.yml` and `index-general-search.yml` + +## CLI Script + +Before running the indexing for **general search** you run the [scrape](../scrape/README.md) script to scrape page data into files. + +Before running the indexing for **general autocomplete** and **AI search autocomplete** you need to clone [docs-internal-data](https://github.com/github/docs-internal-data) to the root of this directory. + +There is a separate run command for indexing each type of search data: +1. **general search**: `npm run index-general-search -- ` +2. **general autocomplete**: `npm run index-general-autocomplete -- docs-internal-data` (if `docs-internal-data` is cloned to root directory) +3. **AI search autocomplete**: `npm run index-ai-search-autocomplete -- docs-internal-data` (if `docs-internal-data` is cloned to root directory) + +To see the arguments accepted by any script, pass the `--help` argument, for example + +```bash +npm run index-general-autocomplete -- --help +``` \ No newline at end of file diff --git a/src/search/scripts/index/index-autocomplete.ts b/src/search/scripts/index/index-autocomplete.ts deleted file mode 100644 index 2c88c3a50d29..000000000000 --- a/src/search/scripts/index/index-autocomplete.ts +++ /dev/null @@ -1,167 +0,0 @@ -import fs from 'node:fs' -import path from 'node:path' - -import { Client, estypes } from '@elastic/elasticsearch' - -import { getClient } from './lib/get-client' -import { utcTimestamp } from './lib/utils' -import { populate } from './lib/populate' - -import { type Version, Records } from './types' - -export const shortVersionNames = { - 'enterprise-server': 'ghes', - 'enterprise-cloud': 'ghec', - 'free-pro-team': 'fpt', -} as const - -const DEFAULT_SLEEPTIME_SECONDS = 30 - -type Options = { - dataRepoRoot: string - languages: string[] - versions: Version[] - retries?: number - sleepTime?: number - verbose?: boolean - indexPrefix?: string -} - -export async function indexAutocomplete(options: Options) { - // The data repo has a predictable structure of - // `hydro/rollups/user-searches/$language/$version/rollup.json` - // But note that the "version" might be a prefix, like enterprise-server. - // const { verbose } = options - - const client = getClient() - - const { dataRepoRoot, versions, languages } = options - for (const language of languages) { - for (const version of versions) { - const records = loadRecords({ version, language, dataRepoRoot }) - const { alias, name } = await createIndex( - client, - language, - version, - options.indexPrefix || '', - ) - await populate(client, records, { - alias, - name, - retries: options.retries || 0, - sleepTime: options.sleepTime || DEFAULT_SLEEPTIME_SECONDS, - }) - } - } -} - -type LoadOptions = { - dataRepoRoot: string - language: string - version: string -} - -function loadRecords(options: LoadOptions): Records { - // First load the rollup records for user-searches - const filePath = path.join( - options.dataRepoRoot, - 'hydro/rollups/user-searches', - options.language, - options.version, - 'rollup.json', - ) - const terms: Records = {} - - const userSearchTerms: Records = JSON.parse(fs.readFileSync(filePath, 'utf8')) - let highestValue = Math.max(...Object.values(userSearchTerms)) - if (highestValue === 0) { - throw new Error(`No records found for ${options.language} ${options.version}`) - } - for (const [term, value] of Object.entries(userSearchTerms)) { - // Why +1? - // Because we want these user-searches to alway be higher than all the - // terms generated from titles. - // For example, a common user-search term that users use - // is "log forwarding". But there might not be a deconstructed term, - // from the document titles, however there might be one called - // "log proxy". So when our users search for "log" we want to suggest, - // in the autocomplete UI "log forwarding" before "log proxy". - terms[term] = value / highestValue + 1 - } - - const documentTermsFilePath = path.join( - options.dataRepoRoot, - 'all-documents/terms', - options.language, - options.version, - 'terms.json', - ) - const documentTerms: Records = JSON.parse(fs.readFileSync(documentTermsFilePath, 'utf8')) - highestValue = Math.max(...Object.values(documentTerms)) - if (highestValue === 0) { - throw new Error(`No document title records found for ${options.language} ${options.version}`) - } - for (const [term, value] of Object.entries(documentTerms)) { - if (!(term in terms)) { - terms[term] = value / highestValue + 1 - } - } - - return terms -} - -type IndexInfo = { - alias: string - name: string -} - -async function createIndex( - client: Client, - language: string, - version: Version, - indexPrefix: string, -): Promise { - const settings: estypes.IndicesIndexSettings = { - analysis: { - analyzer: { - text_analyzer: { - filter: ['lowercase'], - tokenizer: 'standard', - type: 'custom', - }, - }, - }, - // filter: { - // // Will later, conditionally, put the snowball configuration here. - // }, - // XXX SNOWBALL? - } - - if (indexPrefix && !indexPrefix.endsWith('_')) { - indexPrefix += '_' - } - - const indexName = `${indexPrefix}github-autocomplete-${language}-${shortVersionNames[version] || version}` - const thisAlias = `${indexName}__${utcTimestamp()}` - - const mappings: estypes.MappingTypeMapping = { - properties: { - term: { - type: 'text', - analyzer: 'text_analyzer', - // This is used for fast highlighting. Uses more space but makes - // the searches faster. - term_vector: 'with_positions_offsets', - }, - popularity: { type: 'float' }, - }, - } - - await client.indices.create({ - index: thisAlias, - mappings, - settings, - }) - - return { alias: thisAlias, name: indexName } -} diff --git a/src/search/scripts/index/index-cli.ts b/src/search/scripts/index/index-cli.ts new file mode 100644 index 000000000000..5ec770e8ef4a --- /dev/null +++ b/src/search/scripts/index/index-cli.ts @@ -0,0 +1,158 @@ +import { program, Option, Command, InvalidArgumentError } from 'commander' +import { errors } from '@elastic/elasticsearch' +import dotenv from 'dotenv' + +import { languageKeys } from '@/languages/lib/languages.js' +import { indexGeneralAutocomplete } from './lib/index-general-autocomplete' +import { indexGeneralSearch } from './lib/index-general-search' +import { + allIndexVersionKeys, + allIndexVersionOptions, + supportedAutocompletePlanVersions, +} from '@/search/lib/elasticsearch-versions' +import { indexAISearchAutocomplete } from './lib/index-ai-search-autocomplete' + +// If you optionally have ELASTICSEARCH_URL set in your .env file. +dotenv.config() + +program.name('index').description('CLI scripts for indexing Docs data into Elasticsearch') + +const generalAutoCompleteCommand = new Command('general-autocomplete') + .description('Index for general search autocomplete') + .addOption( + new Option('-l, --language ', 'Specific languages(s)').choices(languageKeys), + ) + .addOption( + new Option('-v, --version ', 'Specific versions').choices(allIndexVersionKeys), + ) + .option('--verbose', 'Verbose output') + .option('--index-prefix ', 'Prefix for the index names', '') + .argument('', 'path to the docs-internal-data repo') + .action(async (dataRepoRoot: string, options) => { + const languages = options.language ? options.language : languageKeys + const indexPrefix = options.indexPrefix || '' + try { + await indexGeneralAutocomplete({ + dataRepoRoot, + languages, + versions: options.version || supportedAutocompletePlanVersions, + indexPrefix, + }) + } catch (error: any) { + if (error instanceof errors.ElasticsearchClientError) { + if ((error as any)?.meta) { + console.error('Error meta: %O', (error as any).meta) + } + } + console.error('general-autocomplete indexing error:', error.message) + process.exit(1) + } + }) + +const generalSearchCommand = new Command('general-search') + .description( + 'Indexes records for general search. Records should be pre-scraped by the scrape script.', + ) + .option('-v, --verbose', 'Verbose outputs') + .addOption( + new Option('-V, --version [VERSION...]', 'Specific versions').choices(allIndexVersionOptions), + ) + .addOption( + new Option('-l, --language ', 'Which languages to focus on').choices(languageKeys), + ) + .addOption( + new Option('--not-language ', 'Specific language to omit').choices(languageKeys), + ) + .option('-u, --elasticsearch-url ', 'If different from $ELASTICSEARCH_URL') + .option('-p, --index-prefix ', 'Index string to put before index name') + .option( + '-s, --stagger-seconds ', + 'Number of seconds to sleep between each bulk operation', + (value) => { + const parsed = parseInt(value, 10) + if (isNaN(parsed)) { + throw new InvalidArgumentError('Not a number.') + } + return parsed + }, + ) + .option( + '-r, --retries ', + 'Number of retry attempts on recoverable network errors', + (value) => { + const parsed = parseInt(value, 10) + if (isNaN(parsed)) { + throw new InvalidArgumentError('Not a number.') + } + return parsed + }, + ) + .option( + '--sleep-time ', + `Number of seconds to sleep between each retry attempt (defaults to 30)`, + (value) => { + const parsed = parseInt(value, 10) + if (isNaN(parsed)) { + throw new InvalidArgumentError('Not a number.') + } + return parsed + }, + 30, + ) + .argument('', 'where the indexable files are') + .action(async (sourceDirectory, options) => { + try { + await indexGeneralSearch(sourceDirectory, options) + } catch (error: any) { + if (error instanceof errors.ElasticsearchClientError) { + if ((error as any)?.meta) { + console.error('Error meta: %O', (error as any).meta) + } + } + console.error('general-search indexing error:', error.message) + process.exit(1) + } + }) + +const aiSearchAutocompleteCommand = new Command('ai-search-autocomplete') + .description('Index for AI search autocomplete') + .addOption( + new Option( + '-l, --language ', + 'Specific languages(s). (NOTE: Only english, "en" is currently supported', + ).choices(['en']), + ) + .addOption( + new Option('-v, --version ', 'Specific versions').choices(allIndexVersionKeys), + ) + .option('--verbose', 'Verbose output') + .option('--index-prefix ', 'Prefix for the index names', '') + .argument('', 'path to the docs-internal-data repo') + .action(async (dataRepoRoot: string, options) => { + // In the future, we may want to support multiple languages + // Currently (since this is an experiment), we only support english + const languages = ['en'] + const indexPrefix = options.indexPrefix || '' + try { + await indexAISearchAutocomplete({ + dataRepoRoot, + languages, + versions: options.version || supportedAutocompletePlanVersions, + indexPrefix, + }) + } catch (error: any) { + if (error instanceof errors.ElasticsearchClientError) { + if ((error as any)?.meta) { + console.error('Error meta: %O', (error as any).meta) + } + } + console.error('ai-search-autocomplete indexing error:', error.message) + process.exit(1) + } + }) + +program.addCommand(generalAutoCompleteCommand) +program.addCommand(generalSearchCommand) +program.addCommand(aiSearchAutocompleteCommand) + +program.parse(process.argv) diff --git a/src/search/scripts/index/index.ts b/src/search/scripts/index/index.ts deleted file mode 100644 index 4b46ae05c1f6..000000000000 --- a/src/search/scripts/index/index.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { program, Option } from 'commander' - -import { languageKeys } from '@/languages/lib/languages.js' -import { indexAutocomplete } from './index-autocomplete' -import { type Version } from './types' - -const defaultVersions: Version[] = ['free-pro-team', 'enterprise-server', 'enterprise-cloud'] -const shortAlias = new Map() -shortAlias.set('ghes', 'enterprise-server') -shortAlias.set('fpt', 'free-pro-team') -shortAlias.set('ghec', 'enterprise-cloud') - -program.name('index').description('CLI scripts for indexing to Elasticsearch') - -program - .command('autocomplete') - .description('Index for autocomplete') - .addOption( - new Option('-l, --language ', 'Specific languages(s)').choices(languageKeys), - ) - .addOption( - new Option('-v, --version ', 'Specific version prefix(es)').choices([ - ...defaultVersions, - ...shortAlias.keys(), - ]), - ) - .option('--verbose', 'Verbose output') - .option('--index-prefix ', 'Prefix for the index names', '') - .argument('', 'path to the docs-internal-data repo') - .action((root: string, options) => { - const languages = options.language ? options.language : languageKeys - const versions: Version[] = [] - for (const v of options.version || defaultVersions) { - if (shortAlias.has(v)) { - versions.push(shortAlias.get(v)!) - } else { - versions.push(v) - } - } - const indexPrefix = options.indexPrefix || '' - return indexAutocomplete({ dataRepoRoot: root, languages, versions, indexPrefix }) - }) - -program.parse(process.argv) diff --git a/src/search/scripts/index/lib/get-client.ts b/src/search/scripts/index/lib/get-client.ts deleted file mode 100644 index 4f9b79034430..000000000000 --- a/src/search/scripts/index/lib/get-client.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Client } from '@elastic/elasticsearch' - -export function getClient(): Client { - const node = getElasticsearchURL() - const client = new Client({ node }) - return client -} - -function getElasticsearchURL() { - if (!process.env.ELASTICSEARCH_URL) { - throw new Error( - 'Must passed the elasticsearch URL option or ' + - 'set the environment variable ELASTICSEARCH_URL', - ) - } - let node = process.env.ELASTICSEARCH_URL - - // Allow the user to lazily set it to `localhost:9200` for example. - if (!node.startsWith('http') && !node.startsWith('://') && node.split(':').length === 2) { - node = `http://${node}` - } - - const parsed = new URL(node) - if (!parsed.hostname) throw new Error('no valid hostname') - - return node -} diff --git a/src/search/scripts/index/lib/index-ai-search-autocomplete.ts b/src/search/scripts/index/lib/index-ai-search-autocomplete.ts new file mode 100644 index 000000000000..8bde681a1afe --- /dev/null +++ b/src/search/scripts/index/lib/index-ai-search-autocomplete.ts @@ -0,0 +1,112 @@ +import fs from 'node:fs' +import path from 'node:path' + +import { getElasticsearchClient } from '@/search/lib/helpers/get-client' +import { getElasticSearchIndex } from '@/search/lib/elasticsearch-indexes' +import { + createIndex, + populateIndex, + printSuccess, + updateAlias, +} from '@/search/scripts/index/utils/indexing-elasticsearch-utils' +import { getAISearchAutocompleteSettings } from '@/search/scripts/index/utils/settings' +import { aiSearchAutocompleteMappings } from '@/search/scripts/index/utils/mappings' +import { getPlanVersionFromIndexVersion } from '@/search/lib/elasticsearch-versions' + +import type { TermsWithFrequency } from '@/search/scripts/index/types' + +type Options = { + dataRepoRoot: string + languages: string[] + versions: string[] + retries?: number + sleepTime?: number + verbose?: boolean + indexPrefix?: string +} + +export async function indexAISearchAutocomplete(options: Options) { + const client = getElasticsearchClient(undefined, options.verbose) + await client.ping() // Will throw if not available + + const { dataRepoRoot, languages, versions } = options + for (const language of languages) { + for (const version of versions) { + const startTime = new Date() + + const records = loadQueriesWithPriority({ dataRepoRoot, language, version }) + const { indexName, indexAlias } = getElasticSearchIndex( + 'aiSearchAutocomplete', + version, + language, + options.indexPrefix || '', + ) + + const settings = getAISearchAutocompleteSettings(language, options.verbose) + + await createIndex(client, indexAlias, settings, aiSearchAutocompleteMappings) + + const recordsArray = Object.entries(records).map(([term, popularity]) => ({ + term, + popularity, + })) + + await populateIndex(client, indexAlias, indexName, recordsArray, { + retries: options.retries, + sleepTime: options.sleepTime, + verbose: options.verbose, + }) + + await updateAlias(client, indexName, indexAlias, options) + + printSuccess(indexName, startTime, options.verbose) + } + } +} + +type LoadOptions = { + dataRepoRoot: string + language: string + version: string +} + +function loadQueriesWithPriority(options: LoadOptions): TermsWithFrequency { + // The {version} in the paths uses the version's 'plan' name, e.g. `free-pro-team` instead of `fpt` + const internalDataVersion = getPlanVersionFromIndexVersion(options.version) + + if (!internalDataVersion) { + throw new Error(`No rollup version found for version ${options.version}`) + } + + const queriesFilePath = path.join( + options.dataRepoRoot, + 'ai/search/queries', + options.language, + internalDataVersion, + 'queries.json', + ) + + const queriesFile = JSON.parse(fs.readFileSync(queriesFilePath, 'utf8')) + const { topQueries, allQueries } = queriesFile + + const terms: TermsWithFrequency = {} + + let popularity = topQueries.length + allQueries.length + + // Assign higher popularity to topQueries + for (const term of topQueries) { + terms[term] = popularity + popularity -= 1 + } + + // Assign remaining popularity to allQueries using the order they have in the JSON + for (const term of allQueries) { + // Don't read in the topQueries again (duplicates) + if (!(term in terms)) { + terms[term] = popularity + popularity -= 1 + } + } + + return terms +} diff --git a/src/search/scripts/index/lib/index-general-autocomplete.ts b/src/search/scripts/index/lib/index-general-autocomplete.ts new file mode 100644 index 000000000000..436417256ecf --- /dev/null +++ b/src/search/scripts/index/lib/index-general-autocomplete.ts @@ -0,0 +1,134 @@ +import fs from 'node:fs' +import path from 'node:path' + +import { getElasticsearchClient } from '@/search/lib/helpers/get-client' +import { getElasticSearchIndex } from '@/search/lib/elasticsearch-indexes' +import { + createIndex, + populateIndex, + printSuccess, + updateAlias, +} from '@/search/scripts/index/utils/indexing-elasticsearch-utils' +import { getGeneralAutocompleteSettings } from '@/search/scripts/index/utils/settings' +import { generalAutocompleteMappings } from '@/search/scripts/index/utils/mappings' +import { getPlanVersionFromIndexVersion } from '@/search/lib/elasticsearch-versions' + +import type { TermsWithFrequency } from '@/search/scripts/index/types' + +type Options = { + dataRepoRoot: string + languages: string[] + versions: string[] + retries?: number + sleepTime?: number + verbose?: boolean + indexPrefix?: string +} + +export async function indexGeneralAutocomplete(options: Options) { + const client = getElasticsearchClient(undefined, options.verbose) + await client.ping() // Will throw if not available + + const { dataRepoRoot, versions, languages } = options + for (const language of languages) { + for (const version of versions) { + const startTime = new Date() + + const records = loadTermsWithFrequency({ version, language, dataRepoRoot }) + const { indexName, indexAlias } = getElasticSearchIndex( + 'generalAutocomplete', + version, + language, + options.indexPrefix || '', + ) + + const settings = getGeneralAutocompleteSettings(language, options.verbose) + + await createIndex(client, indexAlias, settings, generalAutocompleteMappings) + + const recordsArray = Object.entries(records).map(([term, popularity]) => ({ + term, + popularity, + })) + + await populateIndex(client, indexAlias, indexName, recordsArray, { + retries: options.retries, + sleepTime: options.sleepTime, + verbose: options.verbose, + }) + + await updateAlias(client, indexName, indexAlias, options) + + printSuccess(indexName, startTime, options.verbose) + } + } +} + +type LoadOptions = { + dataRepoRoot: string + language: string + version: string +} + +/* + * Terms are one-word search terms that a user might enter into a search toolbar + * We have two sources of "terms": + * - Previous user searches (searchTerms) + * - Terms auto-generated taking each word from each title of all of our articles (documentTerms) + * + * Each of the files live in our docs-internal-data repo that should be cloned before running this script. + * The paths to these files for each type of term are: + * - searchTerms: hydro/rollups/user-searches/{langauge}/{version}/rollup.json + * - documentTerms: hydro/rollups/user-searches/{langauge}/{version}/rollup.json + */ +function loadTermsWithFrequency(options: LoadOptions): TermsWithFrequency { + // The {version} in the paths uses the version's 'plan' name, e.g. `free-pro-team` instead of `fpt` + const internalDataVersion = getPlanVersionFromIndexVersion(options.version) + + if (!internalDataVersion) { + throw new Error(`No rollup version found for version ${options.version}`) + } + + const filePath = path.join( + options.dataRepoRoot, + 'hydro/rollups/user-searches', + options.language, + internalDataVersion, + 'rollup.json', + ) + const terms: TermsWithFrequency = {} + + const userSearchTerms: TermsWithFrequency = JSON.parse(fs.readFileSync(filePath, 'utf8')) + let maxFrequency = Math.max(...Object.values(userSearchTerms)) + if (maxFrequency === 0) { + throw new Error(`No records found for ${options.language} ${options.version}`) + } + for (const [term, frequency] of Object.entries(userSearchTerms)) { + // Normalize the frequency which will turn into "popularity" in ElasticSearch + // We include +1 here because "userSearchTerms" should have higher priority than "articleTitleTerms" + terms[term] = frequency / maxFrequency + 1 + } + + const articleTitleTermsFilePath = path.join( + options.dataRepoRoot, + 'all-documents/terms', + options.language, + internalDataVersion, + 'terms.json', + ) + const articleTitleTerms: TermsWithFrequency = JSON.parse( + fs.readFileSync(articleTitleTermsFilePath, 'utf8'), + ) + maxFrequency = Math.max(...Object.values(articleTitleTerms)) + if (maxFrequency === 0) { + throw new Error(`No document title records found for ${options.language} ${options.version}`) + } + for (const [articleTitleTerm, frequency] of Object.entries(articleTitleTerms)) { + if (!(articleTitleTerm in terms)) { + // Notice that we don't + 1 here because we want to give more priority to data from user searches + terms[articleTitleTerm] = frequency / maxFrequency + } + } + + return terms +} diff --git a/src/search/scripts/index/lib/index-general-search.ts b/src/search/scripts/index/lib/index-general-search.ts new file mode 100644 index 000000000000..7a6596a096e9 --- /dev/null +++ b/src/search/scripts/index/lib/index-general-search.ts @@ -0,0 +1,145 @@ +import { Client } from '@elastic/elasticsearch' +import chalk from 'chalk' + +import { languageKeys } from '#src/languages/lib/languages.js' +import { allVersions } from '#src/versions/lib/all-versions.js' +import { getElasticSearchIndex } from '@/search/lib/elasticsearch-indexes' +import { getElasticsearchClient } from '@/search/lib/helpers/get-client' +import { + createIndex, + escapeHTML, + loadIndexRecords, + populateIndex, + printSuccess, + updateAlias, +} from '@/search/scripts/index/utils/indexing-elasticsearch-utils' +import { sleep } from '@/search/lib/helpers/time' +import { getGeneralSearchSettings } from '@/search/scripts/index/utils/settings' +import { generalSearchMappings } from '@/search/scripts/index/utils/mappings' + +import type { AllVersionInfo } from '@/search/scripts/index/types' + +interface Options { + verbose?: boolean + version?: string[] | string + language?: string[] + notLanguage?: string[] + elasticsearchUrl?: string + indexPrefix?: string + staggerSeconds?: number + retries?: number + sleepTime: number +} + +const shortNames: { [key: string]: AllVersionInfo } = Object.fromEntries( + Object.values(allVersions).map((info: AllVersionInfo) => { + const shortName = info.hasNumberedReleases + ? info.miscBaseName + info.currentRelease + : info.miscBaseName + return [shortName, info] + }), +) + +const allVersionKeys = Object.keys(shortNames) + +export async function indexGeneralSearch(sourceDirectory: string, opts: Options) { + if (!sourceDirectory) { + throw new Error('Must pass the source directory as the first argument') + } + + const { language, notLanguage } = opts + + if (language && notLanguage) { + throw new Error("Can't combine --language and --not-language") + } + + const client = getElasticsearchClient(opts.elasticsearchUrl, opts.verbose) + await client.ping() // Will throw if not available + + let version: string | string[] | undefined = opts.version + if (!version && process.env.VERSION && process.env.VERSION !== 'all') { + version = process.env.VERSION + if (!allVersionKeys.includes(version)) { + throw new Error( + `Environment variable 'VERSION' (${version}) is not recognized. Must be one of ${allVersionKeys}`, + ) + } + } + let versionKeys = allVersionKeys + if (version) { + versionKeys = Array.isArray(version) ? version : [version] + } + + const languages = + language || languageKeys.filter((lang) => !notLanguage || !notLanguage.includes(lang)) + if (opts.verbose) { + console.log(`Indexing on languages ${chalk.bold(languages.join(', '))}`) + } + + for (const language of languages) { + let count = 0 + for (const versionKey of versionKeys) { + const startTime = new Date() + + const { indexName, indexAlias } = getElasticSearchIndex( + 'generalSearch', + versionKey, + language, + opts.indexPrefix || '', + ) + + await indexVersion(client, indexName, indexAlias, language, sourceDirectory, opts) + + count++ + if (opts.staggerSeconds && count < versionKeys.length - 1) { + console.log(`Sleeping for ${opts.staggerSeconds} seconds...`) + await sleep(1000 * opts.staggerSeconds) + } + + printSuccess(indexName, startTime, opts.verbose) + } + } +} + +async function indexVersion( + client: Client, + indexName: string, + indexAlias: string, + language: string, + sourceDirectory: string, + opts: Options, +) { + const recordsData = await loadIndexRecords(indexName, sourceDirectory) + const allRecords = Object.values(recordsData).sort((a, b) => b.popularity - a.popularity) + const records = allRecords.map((doc) => { + const { title, objectID, content, breadcrumbs, headings, intro, toplevel } = doc + const contentEscaped = escapeHTML(content) + const headingsEscaped = escapeHTML(headings) + return { + url: objectID, + title, + title_explicit: title, + content: contentEscaped, + content_explicit: contentEscaped, + breadcrumbs, + headings: headingsEscaped, + headings_explicit: headingsEscaped, + popularity: doc.popularity + 1, + intro, + toplevel, + } + }) + + const settings = getGeneralSearchSettings(language, opts.verbose || false) + const mappings = generalSearchMappings + + await createIndex(client, indexAlias, settings, mappings) + + await populateIndex(client, indexAlias, indexName, records, { + retries: opts.retries, + sleepTime: opts.sleepTime * 1000, + verbose: opts.verbose, + }) + + await updateAlias(client, indexName, indexAlias, opts) +} diff --git a/src/search/scripts/index/lib/populate.ts b/src/search/scripts/index/lib/populate.ts deleted file mode 100644 index 252b90cefbcd..000000000000 --- a/src/search/scripts/index/lib/populate.ts +++ /dev/null @@ -1,107 +0,0 @@ -import chalk from 'chalk' -import { Client, errors } from '@elastic/elasticsearch' - -import type { Records, RetryConfig } from '../types' -import { retryOnErrorTest } from './retry-on-error-test' -import { repointAlias } from './repoint-alias' -import { formatTime, sleep } from './utils' - -type PopulateOptions = RetryConfig & { - verbose?: boolean - alias: string - name: string -} - -export async function populate(client: Client, records: Records, options: PopulateOptions) { - const { alias, name } = options - - const allRecords = Object.entries(records).sort((a, b) => b[1] - a[1]) - const operations = allRecords.flatMap(([term, count]) => { - const popularity = count / allRecords[0][1] // Normalize to 1.0 for the highest count - return [ - { index: { _index: alias } }, - { - term, - popularity, - }, - ] - }) - - const bulkOptions = { - // Default is 'false'. - // It means that the index is NOT refreshed as documents are inserted. - // Which makes sense in our case because we do not intend to search on - // this index until after we've pointed the alias to this new index. - refresh: false, - // Default is '1m' but we have no reason *not* to be patient. It's run - // by a bot on a schedeule (GitHub Actions). - timeout: '5m', - } - - const attempts = options.retries - const sleepTime = options.sleepTime * 1000 - - console.log(`About to bulk index ${allRecords.length.toLocaleString()} records with retry %O`, { - attempts, - sleepTime, - }) - const t0 = new Date() - const bulkResponse = await retryOnErrorTest( - (error: Error) => { - // Rate limiting can happen when you're indexing too much at - // same time. - return error instanceof errors.ResponseError && error.meta.statusCode === 429 - }, - () => client.bulk({ operations, ...bulkOptions }), - { - attempts, - sleepTime, - onError: (_, attempts, sleepTime) => { - console.warn( - chalk.yellow( - `Failed to bulk index ${name}. Will attempt ${attempts} more times (after ${ - sleepTime / 1000 - }s sleep).`, - ), - ) - }, - }, - ) - - if (bulkResponse.errors) { - // Some day, when we're more confident how and why this might happen - // we can rewrite this code to "massage" the errors better. - // For now, if it fails, it's "OK". It means we won't be proceeding, - // an error is thrown in Actions and we don't have to worry about - // an incompletion index. - console.error(`Bulk response errors: ${bulkResponse.errors}`) - throw new Error('Bulk errors happened.') - } - const t1 = new Date() - console.log(`Bulk indexed ${alias}. Took ${formatTime(t1.getTime() - t0.getTime())}`) - - // The counting of documents in the index is async and can take a while - // to reflect. So send count requests until we get the right number. - let documentsInIndex = 0 - let countAttempts = 3 - while (documentsInIndex < allRecords.length) { - const { count } = await client.count({ index: alias }) - documentsInIndex = count - if (documentsInIndex >= allRecords.length) break - countAttempts-- - if (!countAttempts) { - console.log(`After ${countAttempts} attempts still haven't matched the expected number.`) - break - } - await sleep(1000) - } - console.log( - `Documents now in ${chalk.bold(alias)}: ${chalk.bold(documentsInIndex.toLocaleString())}`, - ) - - await repointAlias(client, alias, name, { - attempts, - sleepTime, - verbose: Boolean(options.verbose), - }) -} diff --git a/src/search/scripts/index/lib/repoint-alias.ts b/src/search/scripts/index/lib/repoint-alias.ts deleted file mode 100644 index 36af59d2609e..000000000000 --- a/src/search/scripts/index/lib/repoint-alias.ts +++ /dev/null @@ -1,77 +0,0 @@ -import chalk from 'chalk' -import { Client, errors } from '@elastic/elasticsearch' - -import { retryOnErrorTest } from './retry-on-error-test' -import { formatTime } from './utils' - -export async function repointAlias( - client: Client, - alias: string, - name: string, - options: { - attempts: number - sleepTime: number - verbose: boolean - }, -) { - const { attempts, sleepTime, verbose } = options - // To perform an atomic operation that creates the new alias and removes - // the old indexes, we can use the updateAliases API with a body that - // includes an "actions" array. The array includes the added alias - // and the removed indexes. If any of the actions fail, none of the operations - // are performed. - // https://www.elastic.co/guide/en/elasticsearch/reference/master/indices-aliases.html - - type Update = - | { - add: { - index: string - alias: string - } - } - | { - remove_index: { - index: string - } - } - const aliasUpdates: Update[] = [ - { - add: { - index: alias, - alias: name, - }, - }, - ] - console.log(`Alias ${name} -> ${alias}`) - - console.log('About to get indices with retry %O', { attempts, sleepTime }) - const indices = await retryOnErrorTest( - (error: any) => { - // 404 can happen when you're trying to get an index that - // doesn't exist. ...yet! - return error instanceof errors.ResponseError && error.meta.statusCode === 404 - }, - () => client.cat.indices({ format: 'json' }), - { - attempts, - sleepTime, - onError: (error, attempts, sleepTime) => { - console.warn( - chalk.yellow( - `Failed to get index ${name} (${ - error.message || error.toString() - }). Will attempt ${attempts} more times (after ${formatTime(sleepTime)}s sleep).`, - ), - ) - }, - }, - ) - for (const index of indices) { - if (index.index !== alias && index.index.startsWith(name)) { - aliasUpdates.push({ remove_index: { index: index.index } }) - console.log('Deleting index', index.index) - } - } - if (verbose) console.log('Updating alias actions:', aliasUpdates) - await client.indices.updateAliases({ body: { actions: aliasUpdates } }) -} diff --git a/src/search/scripts/index/types.ts b/src/search/scripts/index/types.ts index 533fb79d045f..bb4fd8f876fe 100644 --- a/src/search/scripts/index/types.ts +++ b/src/search/scripts/index/types.ts @@ -1,10 +1,55 @@ -export type Version = 'free-pro-team' | 'enterprise-server' | 'enterprise-cloud' - -export type Records = { - [key: string]: number -} - export type RetryConfig = { retries: number sleepTime: number } + +export interface AllVersionInfo { + hasNumberedReleases: boolean + miscBaseName: string + currentRelease: string + version: string + plan: string +} + +export interface AllVersions { + [key: string]: AllVersionInfo +} + +export interface Options { + language?: string + notLanguage?: string + version?: string + docsInternalData?: string + markers?: boolean + filter?: string +} + +export type Args = string[] + +export interface Page { + relativePath: string + redirect_from?: string[] +} + +export interface Config { + noMarkers: boolean + filter?: string + docsInternalDataPath?: string +} + +export type TermsWithFrequency = { [term: string]: number } + +export interface Records { + [objectID: string]: Record // Here objectId will be identical to the record's objectId +} + +export interface Record { + objectID: string // e.g. "/en/enterprise-cloud@latest/get-started" + breadcrumbs: string // e.g. "Get started / Using GitHub" + title: string // e.g. "Get started with GitHub documentation" + headings: string + content: string + intro: string + toplevel: string + popularity: number +} diff --git a/src/search/scripts/index/utils/constants.ts b/src/search/scripts/index/utils/constants.ts new file mode 100644 index 000000000000..6b833259d0c8 --- /dev/null +++ b/src/search/scripts/index/utils/constants.ts @@ -0,0 +1,11 @@ +export const SNOWBALL_LANGUAGES: { [key: string]: string } = { + en: 'English', + fr: 'French', + es: 'Spanish', + ru: 'Russian', + it: 'Italian', + de: 'German', + pt: 'Portuguese', +} + +export const DEFAULT_SLEEPTIME_SECONDS = 30 diff --git a/src/search/scripts/index/utils/indexing-elasticsearch-utils.ts b/src/search/scripts/index/utils/indexing-elasticsearch-utils.ts new file mode 100644 index 000000000000..8bde91c54685 --- /dev/null +++ b/src/search/scripts/index/utils/indexing-elasticsearch-utils.ts @@ -0,0 +1,178 @@ +import chalk from 'chalk' +import { Client, estypes, errors } from '@elastic/elasticsearch' +import fs from 'fs/promises' +import path from 'path' + +import { readableTimeMinAndSec, sleep } from '@/search/lib/helpers/time' +import { retryOnErrorTest } from '@/search/scripts/index/utils/retry-on-error-test' +import { + DEFAULT_SLEEPTIME_SECONDS, + SNOWBALL_LANGUAGES, +} from '@/search/scripts/index/utils/constants' +import { safeUrlDisplay } from '@/search/lib/helpers/strings' + +import type { Records } from '@/search/scripts/index/types' + +type Options = { + retries?: number + sleepTime?: number + verbose?: boolean +} + +export async function createIndex( + client: Client, + indexAlias: string, + settings: estypes.IndicesIndexSettings, + mappings: estypes.MappingTypeMapping, +) { + await client.indices.create({ + index: indexAlias, + mappings, + settings, + }) +} + +export async function populateIndex( + client: Client, + indexAlias: string, + indexName: string, + records: any[], + options: Options, +) { + console.log(chalk.yellow(`\nIndexing ${chalk.bold(indexName)}`)) + const bulkOperations = records.flatMap((doc) => [{ index: { _index: indexAlias } }, doc]) + + const bulkOptions = { + refresh: false, + timeout: '5m', + } + + const attempts = options.retries || 0 + const sleepTime = options.sleepTime || DEFAULT_SLEEPTIME_SECONDS * 1000 + console.log(`About to bulk index ${records.length.toLocaleString()} records with retry %O`, { + attempts, + sleepTimeMS: sleepTime, + }) + + const t0 = new Date() + const bulkResponse = await retryOnErrorTest( + (error) => error instanceof errors.ResponseError && error.meta.statusCode === 429, + () => client.bulk({ operations: bulkOperations, ...bulkOptions }), + { + attempts, + sleepTime, + onError: (_, attempts, sleepTime) => { + console.warn( + chalk.yellow( + `Failed to bulk index ${indexName}. Will attempt ${attempts} more times (after ${ + sleepTime / 1000 + }s sleep).`, + ), + ) + }, + }, + ) + + if (bulkResponse.errors) { + console.error(`Bulk response errors: ${bulkResponse.errors}`) + throw new Error('Bulk errors happened.') + } + const t1 = new Date() + console.log( + `Bulk indexed ${indexAlias}. Took ${readableTimeMinAndSec(t1.getTime() - t0.getTime())}`, + ) + + let documentsInIndex = 0 + let countAttempts = 3 + while (documentsInIndex < records.length) { + const { count } = await client.count({ index: indexAlias }) + documentsInIndex = count + if (documentsInIndex >= records.length) break + countAttempts-- + if (!countAttempts) { + console.log(`After ${countAttempts} attempts still haven't matched the expected number.`) + break + } + await sleep(1000) + } + + console.log(`Documents now in ${indexAlias}: ${documentsInIndex.toLocaleString()}`) +} + +export async function updateAlias( + client: Client, + indexName: string, + indexAlias: string, + options: Options, +) { + const aliasUpdates: estypes.IndicesUpdateAliasesAction[] = [ + { + add: { + index: indexAlias, + alias: indexName, + }, + }, + ] + + const indices = await retryOnErrorTest( + (error) => { + // 404 can happen when you're trying to get an index that + // doesn't exist. ...yet! + return error instanceof errors.ResponseError && error.meta.statusCode === 404 + }, + () => client.cat.indices({ format: 'json' }), + { + attempts: options.retries || 0, + sleepTime: (options.sleepTime || DEFAULT_SLEEPTIME_SECONDS) * 1000, + onError: (error, attempts, sleepTime) => { + console.warn( + chalk.yellow( + `Failed to get index ${indexName} (${ + error.message || error.toString() + }). Will attempt ${attempts} more times (after ${readableTimeMinAndSec(sleepTime)}s sleep).`, + ), + ) + }, + }, + ) + + for (const index of indices) { + if (index.index !== indexAlias && index.index.startsWith(indexName)) { + aliasUpdates.push({ remove_index: { index: index.index } }) + console.log('Deleting old index', index.index) + } + } + if (options.verbose) console.log('Updating alias actions:', aliasUpdates) + await client.indices.updateAliases({ body: { actions: aliasUpdates } }) +} + +export function printSuccess(indexName: string, startTime: Date, verbose = false) { + const endTime = new Date() + console.log( + chalk.green( + `Finished indexing ${indexName}. Took ${readableTimeMinAndSec(endTime.getTime() - startTime.getTime())}`, + ), + ) + + if (verbose) { + console.log(`To view index: ${safeUrlDisplay(`/${indexName}`)}`) + console.log(`To search index: ${safeUrlDisplay(`/${indexName}/_search`)}`) + } +} + +export async function loadIndexRecords( + indexName: string, + sourceDirectory: string, +): Promise { + const filePath = path.join(sourceDirectory, `${indexName}-records.json`) + const payload = await fs.readFile(filePath, 'utf8') + return JSON.parse(payload) +} + +export function escapeHTML(content: string): string { + return content.replace(//g, '>').replace(/"/g, '"') +} + +export function getSnowballLanguage(language: string): string | undefined { + return SNOWBALL_LANGUAGES[language] +} diff --git a/src/search/scripts/index/utils/mappings.ts b/src/search/scripts/index/utils/mappings.ts new file mode 100644 index 000000000000..1bacf528ee21 --- /dev/null +++ b/src/search/scripts/index/utils/mappings.ts @@ -0,0 +1,52 @@ +import type { estypes } from '@elastic/elasticsearch' + +export const generalSearchMappings: estypes.MappingTypeMapping = { + properties: { + url: { type: 'keyword' }, + title: { + type: 'text', + analyzer: 'text_analyzer', + norms: false, + term_vector: 'with_positions_offsets', + }, + title_explicit: { type: 'text', analyzer: 'text_analyzer_explicit', norms: false }, + content: { + type: 'text', + analyzer: 'text_analyzer', + term_vector: 'with_positions_offsets', + }, + content_explicit: { + type: 'text', + analyzer: 'text_analyzer_explicit', + term_vector: 'with_positions_offsets', + }, + headings: { type: 'text', analyzer: 'text_analyzer', norms: false }, + headings_explicit: { type: 'text', analyzer: 'text_analyzer_explicit', norms: false }, + breadcrumbs: { type: 'text' }, + popularity: { type: 'float' }, + intro: { type: 'text' }, + toplevel: { type: 'keyword' }, + }, +} + +export const generalAutocompleteMappings: estypes.MappingTypeMapping = { + properties: { + term: { + type: 'text', + analyzer: 'text_analyzer', + term_vector: 'with_positions_offsets', + }, + popularity: { type: 'float' }, + }, +} + +export const aiSearchAutocompleteMappings: estypes.MappingTypeMapping = { + properties: { + term: { + type: 'text', + analyzer: 'text_analyzer', + term_vector: 'with_positions_offsets', + }, + popularity: { type: 'float' }, + }, +} diff --git a/src/search/scripts/index/lib/retry-on-error-test.ts b/src/search/scripts/index/utils/retry-on-error-test.ts similarity index 97% rename from src/search/scripts/index/lib/retry-on-error-test.ts rename to src/search/scripts/index/utils/retry-on-error-test.ts index b2c88420a4ef..bed48738f492 100644 --- a/src/search/scripts/index/lib/retry-on-error-test.ts +++ b/src/search/scripts/index/utils/retry-on-error-test.ts @@ -1,5 +1,3 @@ -// [start-readme] -// // Return a function that you can use to run any code within and if it // throws you get a chance to say whether to sleep + retry. // Example: @@ -20,10 +18,8 @@ // Note that, by default, the sleep time is "exponential" by a factor of // 1.5. So the first sleep will, in the above example, // be 800ms. Then 1,200ms, Then 1,800ms. etc. -// -// [end-readme] -import { sleep } from './utils' +import { sleep } from '@/search/lib/helpers/time' export async function retryOnErrorTest( errorTest: (error: any) => boolean, diff --git a/src/search/scripts/index/utils/settings.ts b/src/search/scripts/index/utils/settings.ts new file mode 100644 index 000000000000..a2d65ca29ffe --- /dev/null +++ b/src/search/scripts/index/utils/settings.ts @@ -0,0 +1,118 @@ +import { SNOWBALL_LANGUAGES } from '@/search/scripts/index/utils/constants' + +import type { estypes } from '@elastic/elasticsearch' +import type { + AnalysisSnowballLanguage, + AnalysisCustomAnalyzer, +} from '@elastic/elasticsearch/lib/api/types' + +export function getGeneralSearchSettings( + language: string, + verbose: boolean, +): estypes.IndicesIndexSettings { + const settings: estypes.IndicesIndexSettings = { + analysis: { + char_filter: { + hyphenation_filter: { + type: 'mapping', + mappings: ['- => _'], + }, + }, + analyzer: { + text_analyzer_explicit: { + char_filter: ['hyphenation_filter'], + filter: ['lowercase', 'stop', 'asciifolding'], + tokenizer: 'standard', + type: 'custom', + } as AnalysisCustomAnalyzer, + text_analyzer: { + filter: ['lowercase', 'stop', 'asciifolding'], + tokenizer: 'standard', + type: 'custom', + } as AnalysisCustomAnalyzer, + }, + filter: {}, + }, + } + + const snowballLanguage = SNOWBALL_LANGUAGES[language] + if (snowballLanguage) { + const textAnalyzer = settings.analysis!.analyzer!.text_analyzer as AnalysisCustomAnalyzer + textAnalyzer.filter!.push('languaged_snowball') + + settings.analysis!.filter!['languaged_snowball'] = { + type: 'snowball', + language: snowballLanguage as AnalysisSnowballLanguage, + } + } else if (verbose) { + console.warn(`No snowball language for '${language}'`) + } + + return settings +} + +export function getGeneralAutocompleteSettings( + language: string, + verbose = false, +): estypes.IndicesIndexSettings { + const settings: estypes.IndicesIndexSettings = { + analysis: { + analyzer: { + text_analyzer: { + filter: ['lowercase'], + tokenizer: 'standard', + type: 'custom', + } as AnalysisCustomAnalyzer, + }, + filter: {}, + }, + } + + const snowballLanguage = SNOWBALL_LANGUAGES[language] + if (snowballLanguage) { + const textAnalyzer = settings.analysis!.analyzer!.text_analyzer as AnalysisCustomAnalyzer + textAnalyzer.filter!.push('languaged_snowball') + + settings.analysis!.filter!['languaged_snowball'] = { + type: 'snowball', + language: snowballLanguage as AnalysisSnowballLanguage, + } + } else if (verbose) { + console.warn(`No snowball language for '${language}'`) + } + + return settings +} + +export function getAISearchAutocompleteSettings( + language: string, + verbose = false, +): estypes.IndicesIndexSettings { + const settings: estypes.IndicesIndexSettings = { + analysis: { + analyzer: { + text_analyzer: { + filter: ['lowercase'], + tokenizer: 'standard', + type: 'custom', + } as AnalysisCustomAnalyzer, + }, + filter: {}, + }, + } + + const snowballLanguage = SNOWBALL_LANGUAGES[language] + if (snowballLanguage) { + const textAnalyzer = settings.analysis!.analyzer!.text_analyzer as AnalysisCustomAnalyzer + textAnalyzer.filter!.push('languaged_snowball') + + settings.analysis!.filter!['languaged_snowball'] = { + type: 'snowball', + language: snowballLanguage as AnalysisSnowballLanguage, + } + } else if (verbose) { + console.warn(`No snowball language for '${language}'`) + } + + return settings +} diff --git a/src/search/scripts/retry-on-error-test.js b/src/search/scripts/retry-on-error-test.js deleted file mode 100644 index c41b222b47d0..000000000000 --- a/src/search/scripts/retry-on-error-test.js +++ /dev/null @@ -1,76 +0,0 @@ -// [start-readme] -// -// Return a function that you can use to run any code within and if it -// throws you get a chance to say whether to sleep + retry. -// Example: -// -// async function mainFunction() { -// if (Math.random() > 0.9) throw new Error('too large') -// return 'OK' -// } -// -// const errorTest = (err) => err instanceof Error && err.message.includes('too large') -// const config = { // all optional -// attempts: 3, -// sleepTime: 800, -// onError: (err, attempts) => console.warn(`Failed ${attempts} attempts`) -// } -// const ok = await retry(errorTest, mainFunction, config) -// -// Note that, by default, the sleep time is "exponential" by a factor of -// 1.5. So the first sleep will, in the above example, -// be 800ms. Then 1,200ms, Then 1,800ms. etc. -// -// [end-readme] - -const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)) - -export async function retryOnErrorTest( - errorTest, - callback, - { - attempts = 4, - sleepTime = 1000, - exponential = 1.5, - jitterPercent = 25, - onError = () => {}, - } = {}, -) { - while (true) { - try { - return await callback() - } catch (error) { - if (error instanceof Error && attempts > 0 && errorTest(error)) { - if (onError) onError(error, attempts, sleepTime) - attempts-- - // The reason for the jitter is to avoid a thundering herd problem. - // Suppose two independent processes/threads start at the same time. - // They both fail, perhaps due to rate limiting. Now, if they both - // sleep for 30 seconds in the first retry attempt, it'll just - // clash again 30 seconds later. But if you add a bit of jitter, at - // the next attempt these independent processes/threads will now - // start at slightly different times. - - // According to the Oxford English dictionary, they define "jitter" as: - // - // slight irregular movement, variation, or unsteadiness, - // especially in an electrical signal or electronic device. - // - await sleep(addJitter(sleepTime, jitterPercent)) - if (exponential) { - sleepTime *= 2 - } - } else { - throw error - } - } - } -} - -function addJitter(num, percent) { - // Return the number plus between 0 and $percent of that number. - // For example, for 1,000 with a 20% jitter you might get 1133.4 - // because you start with 1,000 and 13.4% is a random number between - // 0 and 20%. - return num + Math.random() * percent * 0.01 * num -} diff --git a/src/search/scripts/scrape/README.md b/src/search/scripts/scrape/README.md new file mode 100644 index 000000000000..538052f51b96 --- /dev/null +++ b/src/search/scripts/scrape/README.md @@ -0,0 +1,40 @@ +# Scraping for General Search + +We need to scrape each page on the Docs site and use the data we scrape to index Elasticsearch. + +We currently only scrape for **general search** results. + +Autocomplete search data is generated from analytics events and GPT queries. + +## CLI Script + +Before running the scraping script ensure that the server is running in another terminal with `npm run general-search-scrape-server` + +Run the script with `npm run general-search-scrape -- ` + +After a successful run it will generate a series of JSON files with the page data of every page of the Docs site into the passed directory. + +The `index-general-search.yml` workflow will scrape the records into `/tmp/records` then proceed to run the [general-search indexing script](../index/README.md) + +To see the arguments accepted by the script, pass the `--help` argument, for example + +```bash +npm run general-search-scrape -- --help +``` + +## Records (scraped pages) + +In the context of an Elasticsearch index, a record represents a page. Each record has `breadcrumbs`, `title`, `headings`, `content` (the article content in text, not HTML), `intro` (if one exists in the frontmatter), and a unique `objectID` that is currently just the permalink of the article. Here's an example: + +```json +{ + "objectID":"/en/actions/creating-actions/about-custom-actions", + "breadcrumbs":"GitHub Actions / Creating actions", + "title":"About custom actions", + "headings":"About custom actions\nTypes of actions\n[...]", + "content":"Actions are individual tasks that you can combine to create jobs and customize your workflow. You can create your own 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 GitHub community.", + "toplevel":"GitHub Actions", + "popularity":0 +} +``` diff --git a/src/search/scripts/build-records.js b/src/search/scripts/scrape/lib/build-records.ts similarity index 75% rename from src/search/scripts/build-records.js rename to src/search/scripts/scrape/lib/build-records.ts index 42313c4ee149..329771487a85 100644 --- a/src/search/scripts/build-records.js +++ b/src/search/scripts/scrape/lib/build-records.ts @@ -1,14 +1,16 @@ -#!/usr/bin/env node import eventToPromise from 'event-to-promise' import chalk from 'chalk' import dotenv from 'dotenv' import boxen from 'boxen' import { HTTPError } from 'got' -import parsePageSectionsIntoRecords from './parse-page-sections-into-records.js' -import getPopularPages from './popular-pages.js' -import languages from '#src/languages/lib/languages.js' -import domwaiter from './domwaiter.js' +import languages from '@/languages/lib/languages.js' +import parsePageSectionsIntoRecords from '@/search/scripts/scrape/lib/parse-page-sections-into-records' +import getPopularPages from '@/search/scripts/scrape/lib/popular-pages' +import domwaiter from '@/search/scripts/scrape/lib/domwaiter' +import { getAllVersionsKeyFromIndexVersion } from '@/search/lib/elasticsearch-versions' + +import type { Page, Permalink, Record, Config, Redirects } from '@/search/scripts/scrape/types' const pageMarker = chalk.green('|') const recordMarker = chalk.grey('.') @@ -31,16 +33,19 @@ const MIN_TIME = parseInt(process.env.BUILD_RECORDS_MIN_TIME || '5', 10) const FORCE_0_POPULARITY_PRODUCTS = new Set(['contributing']) export default async function buildRecords( - indexName, - indexablePages, - pageVersion, - languageCode, - redirects, - config = {}, -) { + indexName: string, + indexablePages: Page[], + indexVersion: string, + languageCode: string, + redirects: Redirects, + config: Config = {} as Config, +): Promise { + // Determine the page version from the index version + const pageVersion = getAllVersionsKeyFromIndexVersion(indexVersion) + const { noMarkers, docsInternalDataPath } = config console.log(`\n\nBuilding records for index '${indexName}' (${languages[languageCode].name})`) - const records = [] + const records: Record[] = [] const pages = indexablePages // exclude pages that are not in the current language .filter((page) => page.languageCode === languageCode) @@ -55,12 +60,15 @@ export default async function buildRecords( }) }) .map((permalink) => { - permalink.url = `http://localhost:${port}${permalink.href}` + if (permalink) { + permalink.url = `http://localhost:${port}${permalink.href}` + } return permalink }) + .filter((permalink): permalink is Permalink => permalink !== undefined) const popularPages = docsInternalDataPath - ? await getPopularPages(docsInternalDataPath, redirects, pageVersion, languageCode) + ? await getPopularPages(docsInternalDataPath, redirects, indexVersion, languageCode) : {} console.log('indexable pages', indexablePages.length) @@ -93,7 +101,7 @@ export default async function buildRecords( if (err instanceof HTTPError && !err.response.ok) { console.log( '\n' + - boxen(chalk.bold(err.request.requestUrl.pathname), { + boxen(chalk.bold(err.request.requestUrl?.pathname), { title: chalk.red('The URL it failed on was'), padding: 1, borderColor: 'red', diff --git a/src/search/scripts/domwaiter.js b/src/search/scripts/scrape/lib/domwaiter.ts similarity index 50% rename from src/search/scripts/domwaiter.js rename to src/search/scripts/scrape/lib/domwaiter.ts index fccfc4aaae7c..fe70a1d9fedd 100644 --- a/src/search/scripts/domwaiter.js +++ b/src/search/scripts/scrape/lib/domwaiter.ts @@ -1,9 +1,18 @@ -import { EventEmitter } from 'node:events' +import { EventEmitter } from 'events' import Bottleneck from 'bottleneck' import got from 'got' import cheerio from 'cheerio' -export default function domwaiter(pages, opts = {}) { +import type { Permalink } from '@/search/scripts/scrape/types' + +interface DomWaiterOptions { + parseDOM?: boolean + json?: boolean + maxConcurrent?: number + minTime?: number +} + +export default function domwaiter(pages: Permalink[], opts: DomWaiterOptions = {}): EventEmitter { const emitter = new EventEmitter() const defaults = { @@ -17,26 +26,26 @@ export default function domwaiter(pages, opts = {}) { const limiter = new Bottleneck(opts) pages.forEach((page) => { - limiter.schedule(getPage, page, emitter, opts) + limiter.schedule(() => getPage(page, emitter, opts)) }) - limiter - .on('idle', () => { - emitter.emit('done') - }) - .on('error', (err) => { - emitter.emit('error', err) - }) + limiter.on('idle', () => { + emitter.emit('done') + }) + + limiter.on('error', (err) => { + emitter.emit('error', err) + }) return emitter } -async function getPage(page, emitter, opts) { +async function getPage(page: Permalink, emitter: EventEmitter, opts: DomWaiterOptions) { emitter.emit('beforePageLoad', page) if (opts.json) { try { - const json = await got(page.url).json() + const json = await got(page.url!).json() const pageCopy = Object.assign({}, page, { json }) emitter.emit('page', pageCopy) } catch (err) { @@ -44,9 +53,9 @@ async function getPage(page, emitter, opts) { } } else { try { - const body = (await got(page.url)).body + const body = (await got(page.url!)).body const pageCopy = Object.assign({}, page, { body }) - if (opts.parseDOM) pageCopy.$ = cheerio.load(body) + if (opts.parseDOM) (pageCopy as any).$ = cheerio.load(body) emitter.emit('page', pageCopy) } catch (err) { emitter.emit('error', err) diff --git a/src/search/scripts/find-indexable-pages.js b/src/search/scripts/scrape/lib/find-indexable-pages.ts similarity index 70% rename from src/search/scripts/find-indexable-pages.js rename to src/search/scripts/scrape/lib/find-indexable-pages.ts index 9e05abfec99d..ed37cd196c2b 100644 --- a/src/search/scripts/find-indexable-pages.js +++ b/src/search/scripts/scrape/lib/find-indexable-pages.ts @@ -1,8 +1,9 @@ -#!/usr/bin/env node -import { loadPages } from '#src/frame/lib/page-data.js' +import { loadPages } from '@/frame/lib/page-data.js' -export default async function findIndexablePages(match = '') { - const allPages = await loadPages() +import type { Page } from '@/search/scripts/scrape/types' + +export default async function findIndexablePages(match = ''): Promise { + const allPages: Page[] = await loadPages() const indexablePages = allPages // exclude hidden pages .filter((page) => !page.hidden) diff --git a/src/search/scripts/parse-page-sections-into-records.js b/src/search/scripts/scrape/lib/parse-page-sections-into-records.ts similarity index 91% rename from src/search/scripts/parse-page-sections-into-records.js rename to src/search/scripts/scrape/lib/parse-page-sections-into-records.ts index 0897b7c289a5..8bee4c2a237b 100644 --- a/src/search/scripts/parse-page-sections-into-records.js +++ b/src/search/scripts/scrape/lib/parse-page-sections-into-records.ts @@ -1,17 +1,18 @@ -#!/usr/bin/env node import { render } from 'cheerio-to-text' +import type { Record } from '@/search/scripts/scrape/types' + // This module takes cheerio page object and divides it into sections // using H1,H2 heading elements as section delimiters. The text // that follows each heading becomes the content of the search record. const ignoredHeadingSlugs = ['in-this-article', 'further-reading', 'prerequisites'] -export default function parsePageSectionsIntoRecords(page) { +export default function parsePageSectionsIntoRecords(page: any): Record { const { href, $ } = page const title = $('h1').first().text().trim() const breadcrumbsArray = $('[data-search=breadcrumbs] nav.breadcrumbs a') - .map((i, el) => { + .map((i: number, el: any) => { return $(el).text().trim().replace('/', '').replace(/\s+/g, ' ') }) .get() @@ -21,8 +22,7 @@ export default function parsePageSectionsIntoRecords(page) { // page that don't make much sense to find in a site search. $('[data-search=hide]').remove() - // Only slice off the last one if the length of the array is greater - // that 1. + // Only slice off the last one if the length of the array is greater than 1 // On an article page, we the breadcrumbs array will be something // like: // @@ -51,12 +51,12 @@ export default function parsePageSectionsIntoRecords(page) { const $sections = $('h2', $root) .filter('[id]') - .filter((i, el) => { + .filter((i: number, el: any) => { return !ignoredHeadingSlugs.includes($(el).attr('id')) }) const headings = $sections - .map((i, el) => $(el).text()) + .map((i: number, el: any) => $(el).text()) .get() .join('\n') .trim() diff --git a/src/search/scripts/popular-pages.js b/src/search/scripts/scrape/lib/popular-pages.ts similarity index 61% rename from src/search/scripts/popular-pages.js rename to src/search/scripts/scrape/lib/popular-pages.ts index 11dac8186206..a6e42053441b 100644 --- a/src/search/scripts/popular-pages.js +++ b/src/search/scripts/scrape/lib/popular-pages.ts @@ -2,28 +2,31 @@ import { join } from 'path' import { existsSync } from 'fs' import fs from 'fs/promises' -export default async function getPopularPages(dirPath, redirects, version, language) { - // The dirPath is the path to the github/docs-internal-data repo. - // We make assumptions about the structure of the repo. In particular, - // the pageviews rollups live in - // `hydro/rollups/pageviews/$language/$versionprefix/rollup.json` - // For example - // `hydro/rollups/pageviews/en/enterprise-cloud/rollup.json` - const versionPrefix = version.split('@')[0] - let filePath = join(dirPath, 'hydro/rollups/pageviews', language, versionPrefix, 'rollup.json') +import { getPlanVersionFromIndexVersion } from '@/search/lib/elasticsearch-versions.js' + +import type { Redirects, PopularPages } from '@/search/scripts/scrape/types' + +export default async function getPopularPages( + dirPath: string, + redirects: Redirects, + indexVersion: string, + language: string, +): Promise { + const planVersion = getPlanVersionFromIndexVersion(indexVersion) + let filePath = join(dirPath, 'hydro/rollups/pageviews', language, planVersion, 'rollup.json') if (!existsSync(filePath) && language !== 'en') { console.warn("Trying the rollup for 'en'") language = 'en' - filePath = join(dirPath, 'hydro/rollups/pageviews', language, versionPrefix, 'rollup.json') + filePath = join(dirPath, 'hydro/rollups/pageviews', language, planVersion, 'rollup.json') } if (!existsSync(filePath)) { - throw new Error(`No rollup found for version '${versionPrefix}'. Tried ${filePath}`) + throw new Error(`No rollup found for version '${planVersion}'. Tried ${filePath}`) } const rollupRaw = await fs.readFile(filePath, 'utf-8') - // Firt iterate through the array of objects, not making an assumption + // First iterate through the array of objects, not making an assumption // that the first one is the biggest one. - const all = {} + const all: { [key: string]: number } = {} for (const [path, count] of Object.entries(JSON.parse(rollupRaw))) { if (!path) { // Can happen if the SQL query is, for some unknown reason, finding @@ -41,11 +44,11 @@ export default async function getPopularPages(dirPath, redirects, version, langu // We never index these anyway so their popularity is never relevant. continue } - all[path] = count + all[path] = count as number } const biggestCount = Math.max(...Object.values(all)) - const popularPages = {} + const popularPages: PopularPages = {} for (const [path, count] of Object.entries(all)) { // Don't bother writing massively long floating point numbers // because reducing it makes the JSON records smaller and we don't @@ -55,11 +58,6 @@ export default async function getPopularPages(dirPath, redirects, version, langu // The reason we're heeding redirects is because it's possible // that the JSON file is older/"staler" than the // content itself. - // Imaging our analytics recorded that `/en/foo` had 1,234 pageviews, - // and someone goes and... `git mv content/foo content/bar` plus - // adding `redirect_from: - /foo` into the front-matter. - // Then, by using the redirects first, we can maintain that popularity - // by now "pretending" that it's `/en/bar` that has 1,234 pageviews. popularPages[redirects[path] || path] = ratio } diff --git a/src/search/scripts/sync.js b/src/search/scripts/scrape/lib/scrape-into-index-json.ts similarity index 64% rename from src/search/scripts/sync.js rename to src/search/scripts/scrape/lib/scrape-into-index-json.ts index 98feec1b81e6..56cbe264d4e6 100644 --- a/src/search/scripts/sync.js +++ b/src/search/scripts/scrape/lib/scrape-into-index-json.ts @@ -1,22 +1,22 @@ -#!/usr/bin/env node import chalk from 'chalk' -import languages from '#src/languages/lib/languages.js' -import buildRecords from './build-records.js' -import findIndexablePages from './find-indexable-pages.js' -import { allVersions } from '#src/versions/lib/all-versions.js' -import { namePrefix } from '#src/search/lib/config.js' -import { writeIndexRecords } from './search-index-records.js' +import languages from '@/languages/lib/languages.js' +import buildRecords from '@/search/scripts/scrape/lib/build-records' +import findIndexablePages from '@/search/scripts/scrape/lib/find-indexable-pages' +import { writeIndexRecords } from '@/search/scripts/scrape/lib/search-index-records' +import { getElasticSearchIndex } from '@/search/lib/elasticsearch-indexes' + +import type { Options, Config, Page, Redirects } from '@/search/scripts/scrape/types' // Build a search data file for every combination of product version and language // e.g. `github-docs-dotcom-en.json` and `github-docs-2.14-ja.json` -export default async function syncSearchIndexes({ +export default async function scrapeIntoIndexJson({ language, notLanguage, outDirectory, versionsToBuild, - config = {}, -}) { + config = {} as Config, +}: Options): Promise { const t0 = new Date() // build indices for a specific language if provided; otherwise build indices for all languages @@ -25,14 +25,14 @@ export default async function syncSearchIndexes({ ) console.log( - `Building indices for ${chalk.yellow(language || 'all languages')} and ${chalk.yellow( + `Building indices for language: ${chalk.yellow(language || 'all languages')} and version: ${chalk.yellow( versionsToBuild.length === 1 ? versionsToBuild[0] : 'all versions', )}.\n`, ) // Exclude WIP pages, hidden pages, index pages, etc - const indexablePages = await findIndexablePages(config.filter) - const redirects = {} + const indexablePages: Page[] = await findIndexablePages(config.filter) + const redirects: Redirects = {} indexablePages.forEach((page) => { const href = page.relativePath.replace('index.md', '').replace('.md', '') for (let redirectFrom of page.redirect_from || []) { @@ -47,22 +47,14 @@ export default async function syncSearchIndexes({ let countRecordsTotal = 0 // Build and validate all indices for (const languageCode of languagesToBuild) { - for (const pageVersion of versionsToBuild) { - // if GHES, resolves to the release number like 2.21, 2.22, etc. - // if FPT, resolves to 'dotcom' - const indexVersion = - allVersions[pageVersion].plan === 'enterprise-server' - ? allVersions[pageVersion].currentRelease - : allVersions[pageVersion].miscBaseName - - // github-docs-dotcom-en, github-docs-2.22-en - const indexName = `${namePrefix}-${indexVersion}-${languageCode}` + for (const indexVersion of versionsToBuild) { + const { indexName } = getElasticSearchIndex('generalSearch', indexVersion, languageCode) // The page version will be the new version, e.g., free-pro-team@latest, enterprise-server@3.7 const records = await buildRecords( indexName, indexablePages, - pageVersion, + indexVersion, languageCode, redirects, config, @@ -81,6 +73,6 @@ export default async function syncSearchIndexes({ console.log(`Rate ~${chalk.bold(rate)} pages per second.`) } -function formatSeconds(seconds) { +function formatSeconds(seconds: number): string { return new Date(seconds * 1000).toISOString().substr(11, 8) } diff --git a/src/search/scripts/validate-records.js b/src/search/scripts/scrape/lib/search-index-records.ts similarity index 61% rename from src/search/scripts/validate-records.js rename to src/search/scripts/scrape/lib/search-index-records.ts index 1adb43217c5f..c4459ccdb88a 100644 --- a/src/search/scripts/validate-records.js +++ b/src/search/scripts/scrape/lib/search-index-records.ts @@ -1,16 +1,27 @@ -#!/usr/bin/env node +import path from 'path' +import fs from 'fs/promises' import assert from 'assert' import { isArray, isString } from 'lodash-es' -function countArrayValues(arr) { - const counter = new Map() - arr.forEach((value) => counter.set(value, (counter.get(value) || 0) + 1)) - return [...counter.entries()].map(([value, count]) => { - return { value, count } - }) +import type { Record } from '@/search/scripts/scrape/types' + +export async function writeIndexRecords( + name: string, + records: Record[], + outDirectory: string, +): Promise { + validateRecords(name, records) + + const recordsObject = Object.fromEntries(records.map((record) => [record.objectID, record])) + const content = JSON.stringify(recordsObject, undefined, 0) + + const filePath = path.join(outDirectory, `${name}-records.json`) + await fs.writeFile(filePath, content) + + return filePath } -export default function validateRecords(name, records) { +function validateRecords(name: string, records: Record[]): true { assert(isString(name) && name.length, '`name` is required') assert(isArray(records) && records.length, '`records` must be a non-empty array') @@ -35,3 +46,11 @@ export default function validateRecords(name, records) { return true } + +function countArrayValues(arr: string[]) { + const counter = new Map() + arr.forEach((value) => counter.set(value, (counter.get(value) || 0) + 1)) + return [...counter.entries()].map(([value, count]) => { + return { value, count } + }) +} diff --git a/src/search/scripts/sync-search-indices.js b/src/search/scripts/scrape/scrape-cli.ts old mode 100755 new mode 100644 similarity index 64% rename from src/search/scripts/sync-search-indices.js rename to src/search/scripts/scrape/scrape-cli.ts index 17e3e13fbc25..db8c89e4a0a0 --- a/src/search/scripts/sync-search-indices.js +++ b/src/search/scripts/scrape/scrape-cli.ts @@ -1,36 +1,25 @@ -#!/usr/bin/env node - -// [start-readme] -// // This script is run automatically via GitHub Actions on every push to `main` to generate searchable data. -// It can also be run manually. For more info see [contributing/search.md](contributing/search.md) -// -// [end-readme] +// It can also be run manually. import { existsSync, statSync, readdirSync } from 'fs' - -import assert from 'assert' import { program, Option } from 'commander' -import { languageKeys } from '#src/languages/lib/languages.js' -import { allVersions } from '#src/versions/lib/all-versions.js' -import searchSync from './sync.js' +import { languageKeys } from '@/languages/lib/languages' +import scrapeIntoIndexJson from '@/search/scripts/scrape/lib/scrape-into-index-json' +import { + allIndexVersionKeys, + allIndexVersionOptions, + versionToIndexVersionMap, +} from '@/search/lib/elasticsearch-versions' -const shortNames = Object.fromEntries( - Object.values(allVersions).map((info) => { - const shortName = info.hasNumberedReleases - ? info.miscBaseName + info.currentRelease - : info.miscBaseName - return [shortName, info] - }), -) - -const allVersionKeys = [...Object.keys(shortNames), ...Object.keys(allVersions)] +import type { Config, Options, ProgramOptions } from '@/search/scripts/scrape/types' program - .description('Creates search records by scraping') + .description('Creates search index JSONs by scraping a running docs site') .option('-v, --verbose', 'Verbose outputs') - .addOption(new Option('-V, --version ', 'Specific versions').choices(allVersionKeys)) + .addOption( + new Option('-V, --version ', 'Specific versions').choices(allIndexVersionOptions), + ) .addOption( new Option('-l, --language ', 'Which languages to focus on').choices(languageKeys), ) @@ -48,8 +37,8 @@ program main(program.opts(), program.args) -async function main(opts, args) { - let language +async function main(opts: ProgramOptions, args: string[]) { + let language: string | undefined if ('language' in opts) { language = opts.language if (process.env.LANGUAGE) { @@ -72,7 +61,7 @@ async function main(opts, args) { throw new Error("Can't specify --language *and* --not-language") } - let version + let version: string | undefined if ('version' in opts) { version = opts.version if (process.env.VERSION) { @@ -83,15 +72,15 @@ async function main(opts, args) { } else { if (process.env.VERSION && process.env.VERSION !== 'all') { version = process.env.VERSION - if (!allVersionKeys.includes(version)) { + if (!allIndexVersionOptions.includes(version)) { throw new Error( - `Environment variable 'VERSION' (${version}) is not recognized. Must be one of ${allVersionKeys}`, + `Environment variable 'VERSION' (${version}) is not recognized. Must be one of ${allIndexVersionOptions}`, ) } } } - let docsInternalDataPath + let docsInternalDataPath: string | undefined const { docsInternalData } = opts const { DOCS_INTERNAL_DATA } = process.env @@ -120,39 +109,30 @@ async function main(opts, args) { throw new Error(`'${docsInternalDataPath}' must contain a 'hydro' directory`) } - // A `--version` or `process.env.VERSION` was specified, we need to convert - // it to the long name. I.e. `free-pro-team@latest`. Not `dotcom`. - // But it could also have beeb specified as `all` which means that `version` - // here ill be `undefined` which is also OK. - // const indexVersion = shortNames[version].hasNumberedReleases - // ? shortNames[version].currentRelease - // : shortNames[version].miscBaseName - - let indexVersion + let indexVersion: string | undefined if (version && version !== 'all') { - // If it has been specified, it needs to be in the "long-form". - // I.e. `enterprise-server@3.5` not `ghes-3.5`. - indexVersion = version in shortNames ? shortNames[version].version : version + indexVersion = versionToIndexVersionMap[version] + } + if (!indexVersion && !allIndexVersionOptions.includes(indexVersion || '')) { + throw new Error( + `Input error. Version must be not passed or one of ${allIndexVersionOptions}. Got: ${indexVersion}`, + ) } - assert( - !indexVersion || indexVersion in allVersions, - `version must be undefined or one of ${Object.keys(allVersions)}`, - ) const [outDirectory] = args - const config = { + const config: Config = { noMarkers: !opts.markers, filter: opts.filter, docsInternalDataPath, } - const options = { + const options: Options = { language, notLanguage, outDirectory, config, - versionsToBuild: indexVersion ? [indexVersion] : Object.keys(allVersions), + versionsToBuild: indexVersion ? [indexVersion] : Object.keys(allIndexVersionKeys), } - await searchSync(options) + await scrapeIntoIndexJson(options) } diff --git a/src/search/scripts/scrape/types.ts b/src/search/scripts/scrape/types.ts new file mode 100644 index 000000000000..20db4d78b968 --- /dev/null +++ b/src/search/scripts/scrape/types.ts @@ -0,0 +1,70 @@ +export interface Config { + noMarkers: boolean + filter?: string + docsInternalDataPath?: string +} + +export interface Options { + language?: string + notLanguage?: string + outDirectory: string + config: Config + versionsToBuild: string[] +} + +export interface ProgramOptions { + verbose?: boolean + version?: string + language?: string + notLanguage?: string + markers?: boolean + filter?: string + docsInternalData?: string +} + +export interface Page { + relativePath: string + languageCode: string + permalinks: Permalink[] + redirect_from?: string[] + hidden?: boolean + parentProduct?: { + wip?: boolean + hidden?: boolean + } +} + +export interface Permalink { + pageVersion: string + href: string + languageCode: string + relativePath: string + url?: string + '?'?: string +} + +export interface Record { + objectID: string + breadcrumbs: string + title: string + headings: string + content: string + intro: string + toplevel: string + popularity?: number +} + +export interface Language { + name: string + code: string +} + +export type Languages = { [key: string]: Language } + +export interface Redirects { + [key: string]: string +} + +export interface PopularPages { + [key: string]: number +} diff --git a/src/search/scripts/search-index-records.js b/src/search/scripts/search-index-records.js deleted file mode 100644 index 19684a77bf97..000000000000 --- a/src/search/scripts/search-index-records.js +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env node -import path from 'path' -import fs from 'fs/promises' - -import validateRecords from './validate-records.js' - -export async function writeIndexRecords(name, records, outDirectory) { - validateRecords(name, records) - - const recordsObject = Object.fromEntries(records.map((record) => [record.objectID, record])) - const content = JSON.stringify(recordsObject, undefined, 0) - - const filePath = path.join(outDirectory, `${name}-records.json`) - await fs.writeFile(filePath, content) - - return filePath -} diff --git a/src/search/tests/api-ai-search-autocomplete.ts b/src/search/tests/api-ai-search-autocomplete.ts new file mode 100644 index 000000000000..191f1854a287 --- /dev/null +++ b/src/search/tests/api-ai-search-autocomplete.ts @@ -0,0 +1,164 @@ +/** + * To be able to run these tests you need to index the fixtures! + * And you need to have an Elasticsearch URL to connect to for the server. + * + * To index the fixtures, run: + * + * ELASTICSEARCH_URL=http://localhost:9200 npm run index-test-fixtures + * + * This will replace any "real" Elasticsearch indexes you might have so + * once you're done working on vitest tests you need to index real + * content again. + */ + +import { expect, test, vi } from 'vitest' + +import { describeIfElasticsearchURL } from '@/tests/helpers/conditional-runs.js' +import { get } from '@/tests/helpers/e2etest-ts' + +import type { AutocompleteSearchResponse } from '@/search/types' + +if (!process.env.ELASTICSEARCH_URL) { + console.warn( + 'None of the API search middleware tests are run because ' + + "the environment variable 'ELASTICSEARCH_URL' is currently not set.", + ) +} + +const aiSearchEndpoint = '/api/search/ai-search-autocomplete/v1' +const getSearchEndpointWithParams = (searchParams: URLSearchParams) => + `${aiSearchEndpoint}?${searchParams}` + +// This suite only runs if $ELASTICSEARCH_URL is set. +describeIfElasticsearchURL('search/ai-search-autocomplete v1 middleware', () => { + vi.setConfig({ testTimeout: 60 * 1000 }) + + test('basic search', async () => { + const sp = new URLSearchParams() + // To see why this will work, + // see src/search/tests/fixtures/data/ai/* + sp.set('query', 'how do I') + const res = await get(getSearchEndpointWithParams(sp)) + expect(res.statusCode).toBe(200) + const results = JSON.parse(res.body) as AutocompleteSearchResponse + + expect(results.meta).toBeTruthy() + expect(results.meta.found.value).toBeGreaterThanOrEqual(1) + expect(results.meta.found.relation).toBeTruthy() + + expect(results.hits).toBeTruthy() + + const hit = results.hits[0] + expect(hit.term).toBe('How do I clone a repository?') + expect(hit.highlights).toBeTruthy() + expect(hit.highlights[0]).toBe('How do I clone a repository?') + + // Check that it can be cached at the CDN + expect(res.headers['set-cookie']).toBeUndefined() + expect(res.headers['cache-control']).toContain('public') + expect(res.headers['cache-control']).toMatch(/max-age=[1-9]/) + expect(res.headers['surrogate-control']).toContain('public') + expect(res.headers['surrogate-control']).toMatch(/max-age=[1-9]/) + expect(res.headers['surrogate-key']).toBe('manual-purge') + }) + + test('invalid version', async () => { + const sp = new URLSearchParams() + sp.set('query', 'fo') + sp.set('version', 'never-heard-of') + const res = await get(`${aiSearchEndpoint}?{sp}`) + expect(res.statusCode).toBe(400) + expect(JSON.parse(res.body).error).toBeTruthy() + }) + + test('variations on version name', async () => { + const sp = new URLSearchParams() + sp.set('query', 'fo') + sp.set('version', 'enterprise-cloud') + { + const res = await get(getSearchEndpointWithParams(sp)) + expect(res.statusCode).toBe(200) + } + sp.set('version', 'ghec') + { + const res = await get(getSearchEndpointWithParams(sp)) + expect(res.statusCode).toBe(200) + } + sp.set('version', 'fpt') + { + const res = await get(getSearchEndpointWithParams(sp)) + expect(res.statusCode).toBe(200) + } + sp.set('version', 'free-pro-team@latest') + { + const res = await get(getSearchEndpointWithParams(sp)) + expect(res.statusCode).toBe(200) + } + }) + + test('invalid language', async () => { + const sp = new URLSearchParams() + sp.set('query', 'fo') + sp.set('language', 'xx') + const res = await get(getSearchEndpointWithParams(sp)) + expect(res.statusCode).toBe(400) + expect(JSON.parse(res.body).error).toBeTruthy() + }) + + test('only english supported', async () => { + const sp = new URLSearchParams() + sp.set('query', 'fo') + sp.set('language', 'ja') + const res = await get(getSearchEndpointWithParams(sp)) + expect(res.statusCode).toBe(400) + expect(JSON.parse(res.body).error).toBeTruthy() + }) + + test('fuzzy autocomplete search', async () => { + const sp = new URLSearchParams() + sp.set('query', 'cl') // Short for "clone" + const res = await get(getSearchEndpointWithParams(sp)) + expect(res.statusCode).toBe(200) + const results = JSON.parse(res.body) as AutocompleteSearchResponse + // 'cl" matches "How do I clone a repository?" + const hit = results.hits[0] + expect(hit.term).toBe('How do I clone a repository?') + // Highlighting behavior will highlight the matching "term" which is an entire word + // In this case that word is "clone" when the query is "cl" + expect(hit.highlights[0]).toBe('How do I clone a repository?') + }) + + test('autocomplete term search', async () => { + const sp = new URLSearchParams() + sp.set('query', 'clone') + const res = await get(getSearchEndpointWithParams(sp)) + expect(res.statusCode).toBe(200) + const results = JSON.parse(res.body) as AutocompleteSearchResponse + console.log(JSON.stringify(results, null, 2)) + const hit = results.hits[0] + expect(hit.term).toBe('How do I clone a repository?') + expect(hit.highlights).toBeTruthy() + expect(hit.highlights[0]).toBe('How do I clone a repository?') + }) + + test('invalid query', async () => { + const sp = new URLSearchParams() + // No query at all + { + const res = await get(getSearchEndpointWithParams(sp)) + expect(res.statusCode).toBe(400) + } + // Empty query + { + sp.set('query', '') + const res = await get(getSearchEndpointWithParams(sp)) + expect(res.statusCode).toBe(400) + } + // Empty when trimmed + { + sp.set('query', ' ') + const res = await get(getSearchEndpointWithParams(sp)) + expect(res.statusCode).toBe(400) + } + }) +}) diff --git a/src/search/tests/api-autocomplete-search.js b/src/search/tests/api-general-autocomplete-search.ts similarity index 62% rename from src/search/tests/api-autocomplete-search.js rename to src/search/tests/api-general-autocomplete-search.ts index 2d9448aa51a5..f3e7120a0beb 100644 --- a/src/search/tests/api-autocomplete-search.js +++ b/src/search/tests/api-general-autocomplete-search.ts @@ -13,8 +13,9 @@ import { expect, test, vi } from 'vitest' -import { describeIfElasticsearchURL } from '#src/tests/helpers/conditional-runs.js' -import { get } from '#src/tests/helpers/e2etest.js' +import { describeIfElasticsearchURL } from '@/tests/helpers/conditional-runs.js' +import { get } from '@/tests/helpers/e2etest-ts' +import type { AutocompleteSearchResponse, SearchValidationErrorEntry } from '@/search/types' if (!process.env.ELASTICSEARCH_URL) { console.warn( @@ -28,13 +29,13 @@ describeIfElasticsearchURL('search/autocomplete v1 middleware', () => { vi.setConfig({ testTimeout: 60 * 1000 }) test('basic search', async () => { - const sp = new URLSearchParams() + const sp: URLSearchParams = new URLSearchParams() // To see why this will work, // see src/search/tests/fixtures/data sp.set('query', 'fo') - const res = await get('/api/search/autocomplete/v1?' + sp) + const res = await get('/api/search/autocomplete/v1?' + sp.toString()) expect(res.statusCode).toBe(200) - const results = JSON.parse(res.body) + const results: AutocompleteSearchResponse = JSON.parse(res.body) expect(results.meta).toBeTruthy() expect(results.meta.found.value).toBeGreaterThanOrEqual(1) @@ -58,55 +59,65 @@ describeIfElasticsearchURL('search/autocomplete v1 middleware', () => { }) test('invalid version', async () => { - const sp = new URLSearchParams() + const sp: URLSearchParams = new URLSearchParams() sp.set('query', 'fo') sp.set('version', 'never-heard-of') - const res = await get('/api/search/autocomplete/v1?' + sp) + const res = await get('/api/search/autocomplete/v1?' + sp.toString()) expect(res.statusCode).toBe(400) - expect(JSON.parse(res.body).error).toBeTruthy() + const errorResponse: SearchValidationErrorEntry = JSON.parse(res.body).error + expect(errorResponse).toBeTruthy() }) test('variations on version name', async () => { - const sp = new URLSearchParams() + const sp: URLSearchParams = new URLSearchParams() sp.set('query', 'fo') + + // Test with 'enterprise-cloud' version sp.set('version', 'enterprise-cloud') { - const res = await get('/api/search/autocomplete/v1?' + sp) + const res = await get('/api/search/autocomplete/v1?' + sp.toString()) expect(res.statusCode).toBe(200) } + + // Test with 'ghec' version sp.set('version', 'ghec') { - const res = await get('/api/search/autocomplete/v1?' + sp) + const res = await get('/api/search/autocomplete/v1?' + sp.toString()) expect(res.statusCode).toBe(200) } + + // Test with 'fpt' version sp.set('version', 'fpt') { - const res = await get('/api/search/autocomplete/v1?' + sp) + const res = await get('/api/search/autocomplete/v1?' + sp.toString()) expect(res.statusCode).toBe(200) } + + // Test with 'free-pro-team@latest' version sp.set('version', 'free-pro-team@latest') { - const res = await get('/api/search/autocomplete/v1?' + sp) + const res = await get('/api/search/autocomplete/v1?' + sp.toString()) expect(res.statusCode).toBe(200) } }) test('invalid language', async () => { - const sp = new URLSearchParams() + const sp: URLSearchParams = new URLSearchParams() sp.set('query', 'fo') sp.set('language', 'xx') - const res = await get('/api/search/autocomplete/v1?' + sp) + const res = await get('/api/search/autocomplete/v1?' + sp.toString()) expect(res.statusCode).toBe(400) - expect(JSON.parse(res.body).error).toBeTruthy() + const errorResponse: SearchValidationErrorEntry = JSON.parse(res.body).error + expect(errorResponse).toBeTruthy() }) test('fuzzy autocomplete search', async () => { - const sp = new URLSearchParams() + const sp: URLSearchParams = new URLSearchParams() sp.set('query', 'forc') - const res = await get('/api/search/autocomplete/v1?' + sp) + const res = await get('/api/search/autocomplete/v1?' + sp.toString()) expect(res.statusCode).toBe(200) - const results = JSON.parse(res.body) - // The work "fork" matches "fo" + const results: AutocompleteSearchResponse = JSON.parse(res.body) + // The work "fork" matches "forc" const hit = results.hits[0] expect(hit.term).toBe('fork') expect(hit.highlights).toBeTruthy() @@ -114,22 +125,22 @@ describeIfElasticsearchURL('search/autocomplete v1 middleware', () => { }) test('invalid query', async () => { - const sp = new URLSearchParams() + const sp: URLSearchParams = new URLSearchParams() // No query at all { - const res = await get('/api/search/autocomplete/v1?' + sp) + const res = await get('/api/search/autocomplete/v1?' + sp.toString()) expect(res.statusCode).toBe(400) } // Empty query { sp.set('query', '') - const res = await get('/api/search/autocomplete/v1?' + sp) + const res = await get('/api/search/autocomplete/v1?' + sp.toString()) expect(res.statusCode).toBe(400) } // Empty when trimmed { sp.set('query', ' ') - const res = await get('/api/search/autocomplete/v1?' + sp) + const res = await get('/api/search/autocomplete/v1?' + sp.toString()) expect(res.statusCode).toBe(400) } }) diff --git a/src/search/tests/api-search.js b/src/search/tests/api-search.ts similarity index 66% rename from src/search/tests/api-search.js rename to src/search/tests/api-search.ts index 167e7622d1d0..e1b2ee7c089d 100644 --- a/src/search/tests/api-search.js +++ b/src/search/tests/api-search.ts @@ -12,9 +12,9 @@ */ import { expect, test, vi } from 'vitest' - -import { describeIfElasticsearchURL } from '#src/tests/helpers/conditional-runs.js' -import { get } from '#src/tests/helpers/e2etest.js' +import { describeIfElasticsearchURL } from '@/tests/helpers/conditional-runs.js' +import { get } from '@/tests/helpers/e2etest-ts' +import { GeneralSearchResponse, SearchResultAggregations, GeneralSearchHit } from '@/search/types' if (!process.env.ELASTICSEARCH_URL) { console.warn( @@ -33,9 +33,9 @@ describeIfElasticsearchURL('search v1 middleware', () => { // see src/search/tests/fixtures/search-indexes/github-docs-dotcom-en-records.json // which clearly has a record with the title "Foo" sp.set('query', 'foo') - const res = await get('/api/search/v1?' + sp) + const res = await get('/api/search/v1?' + sp.toString()) expect(res.statusCode).toBe(200) - const results = JSON.parse(res.body) + const results: GeneralSearchResponse = JSON.parse(res.body) expect(results.meta).toBeTruthy() expect(results.meta.found.value).toBeGreaterThanOrEqual(1) @@ -51,7 +51,7 @@ describeIfElasticsearchURL('search v1 middleware', () => { expect(results.hits.length).toBeGreaterThanOrEqual(1) // ...but only one has the word "foo" in its title so we can // be certain it comes first. - const hit = results.hits[0] + const hit: GeneralSearchHit = results.hits[0] // This specifically checks what we expect of version v1 expect(hit.url).toBe('/en/foo') expect(hit.title).toBe('Foo') @@ -75,11 +75,11 @@ describeIfElasticsearchURL('search v1 middleware', () => { const sp = new URLSearchParams() sp.set('query', 'foo') sp.set('debug', '1') // Note! - const res = await get('/api/search/v1?' + sp) + const res = await get('/api/search/v1?' + sp.toString()) expect(res.statusCode).toBe(200) - const results = JSON.parse(res.body) + const results: GeneralSearchResponse = JSON.parse(res.body) // safe because we know exactly the fixtures - const hit = results.hits[0] + const hit: GeneralSearchHit = results.hits[0] expect(hit.popularity).toBeTruthy() expect(hit.score).toBeTruthy() expect(hit.es_url).toBeTruthy() @@ -90,9 +90,9 @@ describeIfElasticsearchURL('search v1 middleware', () => { { const sp = new URLSearchParams() sp.set('query', 'sill') - const res = await get('/api/search/v1?' + sp) + const res = await get('/api/search/v1?' + sp.toString()) expect(res.statusCode).toBe(200) - const results = JSON.parse(res.body) + const results: GeneralSearchResponse = JSON.parse(res.body) // Fixtures contains no word called 'sill'. It does contain the term // 'silly' which, in English, becomes 'silli` when stemmed. // Because we don't use `&autocomplete=true` this time, we expect @@ -105,22 +105,23 @@ describeIfElasticsearchURL('search v1 middleware', () => { const sp = new URLSearchParams() sp.set('query', 'sill') sp.set('autocomplete', 'true') - const res = await get('/api/search/v1?' + sp) + const res = await get('/api/search/v1?' + sp.toString()) expect(res.statusCode).toBe(200) - const results = JSON.parse(res.body) + const results: GeneralSearchResponse = JSON.parse(res.body) expect(results.meta.found.value).toBeGreaterThanOrEqual(1) - const hit = results.hits[0] - const contentHighlights = hit.highlights.content - expect(contentHighlights[0]).toMatch('silly') + const hit: GeneralSearchHit = results.hits[0] + const contentHighlights: string[] | undefined = hit.highlights.content + expect(contentHighlights).toBeTruthy() + expect(contentHighlights![0]).toMatch('silly') } }) test('find nothing', async () => { const sp = new URLSearchParams() sp.set('query', 'xojixjoiwejhfoiuwehjfioweufhj') - const res = await get('/api/search/v1?' + sp) + const res = await get('/api/search/v1?' + sp.toString()) expect(res.statusCode).toBe(200) - const results = JSON.parse(res.body) + const results: GeneralSearchResponse = JSON.parse(res.body) expect(results.hits.length).toBe(0) expect(results.meta.found.value).toBe(0) }) @@ -129,9 +130,9 @@ describeIfElasticsearchURL('search v1 middleware', () => { const sp = new URLSearchParams() sp.set('query', 'introduction heading') sp.append('highlights', 'content') - const res = await get('/api/search/v1?' + sp) + const res = await get('/api/search/v1?' + sp.toString()) expect(res.statusCode).toBe(200) - const results = JSON.parse(res.body) + const results: GeneralSearchResponse = JSON.parse(res.body) expect(results.meta.found.value).toBeGreaterThanOrEqual(1) for (const hit of results.hits) { expect(hit.highlights.title).toBeFalsy() @@ -144,9 +145,9 @@ describeIfElasticsearchURL('search v1 middleware', () => { // This will match because it's in the 'content' but not in 'headings' sp.set('query', 'Fact of life') sp.set('highlights', 'title') - const res = await get('/api/search/v1?' + sp) + const res = await get('/api/search/v1?' + sp.toString()) expect(res.statusCode).toBe(200) - const results = JSON.parse(res.body) + const results: GeneralSearchResponse = JSON.parse(res.body) expect(results.meta.found.value).toBeGreaterThanOrEqual(1) for (const hit of results.hits) { expect(hit.highlights.title).toBeTruthy() @@ -158,14 +159,14 @@ describeIfElasticsearchURL('search v1 middleware', () => { const sp = new URLSearchParams() sp.set('query', 'foo') sp.set('version', 'dotcom') - const res1 = await get('/api/search/v1?' + sp) + const res1 = await get('/api/search/v1?' + sp.toString()) expect(res1.statusCode).toBe(200) - const results1 = JSON.parse(res1.body) + const results1: GeneralSearchResponse = JSON.parse(res1.body) sp.set('version', 'free-pro-team@latest') - const res2 = await get('/api/search/v1?' + sp) + const res2 = await get('/api/search/v1?' + sp.toString()) expect(res2.statusCode).toBe(200) - const results2 = JSON.parse(res2.body) + const results2: GeneralSearchResponse = JSON.parse(res2.body) expect(results1.hits[0].id).toBe(results2.hits[0].id) }) @@ -174,90 +175,126 @@ describeIfElasticsearchURL('search v1 middleware', () => { { const res = await get('/api/search/v1') expect(res.statusCode).toBe(400) - expect(JSON.parse(res.body).error).toBeTruthy() + const errorResponse = JSON.parse(res.body) as { + error: string + field?: string + } + expect(errorResponse.error).toBeTruthy() } // query is just whitespace { const sp = new URLSearchParams() sp.set('query', ' ') - const res = await get('/api/search/v1?' + sp) + const res = await get('/api/search/v1?' + sp.toString()) expect(res.statusCode).toBe(400) - expect(JSON.parse(res.body).error).toBeTruthy() + const errorResponse = JSON.parse(res.body) as { + error: string + field?: string + } + expect(errorResponse.error).toBeTruthy() } // unrecognized language { const sp = new URLSearchParams() sp.set('query', 'test') sp.set('language', 'xxx') - const res = await get('/api/search/v1?' + sp) + const res = await get('/api/search/v1?' + sp.toString()) expect(res.statusCode).toBe(400) - expect(JSON.parse(res.body).error).toMatch('language') + const errorResponse = JSON.parse(res.body) as { + error: string + field?: string + } + expect(errorResponse.error).toMatch('language') } // unrecognized page { const sp = new URLSearchParams() sp.set('query', 'test') sp.set('page', '9999') - const res = await get('/api/search/v1?' + sp) + const res = await get('/api/search/v1?' + sp.toString()) expect(res.statusCode).toBe(400) - expect(JSON.parse(res.body).error).toMatch('page') + const errorResponse = JSON.parse(res.body) as { + error: string + field?: string + } + expect(errorResponse.error).toMatch('page') } // unrecognized version { const sp = new URLSearchParams() sp.set('query', 'test') sp.set('version', 'xxxxx') - const res = await get('/api/search/v1?' + sp) + const res = await get('/api/search/v1?' + sp.toString()) expect(res.statusCode).toBe(400) - expect(JSON.parse(res.body).error).toMatch("'xxxxx'") - expect(JSON.parse(res.body).field).toMatch('version') + const errorResponse = JSON.parse(res.body) as { + error: string + field?: string + } + expect(errorResponse.error).toMatch("'xxxxx'") + expect(errorResponse.field).toMatch('version') } // unrecognized size { const sp = new URLSearchParams() sp.set('query', 'test') sp.set('size', 'not a number') - const res = await get('/api/search/v1?' + sp) + const res = await get('/api/search/v1?' + sp.toString()) expect(res.statusCode).toBe(400) - expect(JSON.parse(res.body).error).toMatch('size') + const errorResponse = JSON.parse(res.body) as { + error: string + field?: string + } + expect(errorResponse.error).toMatch('size') } // unrecognized sort { const sp = new URLSearchParams() sp.set('query', 'test') sp.set('sort', 'neverheardof') - const res = await get('/api/search/v1?' + sp) + const res = await get('/api/search/v1?' + sp.toString()) expect(res.statusCode).toBe(400) - expect(JSON.parse(res.body).error).toMatch('sort') + const errorResponse = JSON.parse(res.body) as { + error: string + field?: string + } + expect(errorResponse.error).toMatch('sort') } // unrecognized highlights { const sp = new URLSearchParams() sp.set('query', 'test') sp.set('highlights', 'neverheardof') - const res = await get('/api/search/v1?' + sp) + const res = await get('/api/search/v1?' + sp.toString()) expect(res.statusCode).toBe(400) - expect(JSON.parse(res.body).error).toMatch('neverheardof') + const errorResponse = JSON.parse(res.body) as { + error: string + field?: string + } + expect(errorResponse.error).toMatch('neverheardof') } // multiple 'query' keys { const sp = new URLSearchParams() sp.append('query', 'test1') sp.append('query', 'test2') - const res = await get('/api/search/v1?' + sp) + const res = await get('/api/search/v1?' + sp.toString()) expect(res.statusCode).toBe(400) - expect(JSON.parse(res.body).error).toMatch('Cannot have multiple values') + const errorResponse = JSON.parse(res.body) as { + error: string + field?: string + } + expect(errorResponse.error).toMatch('Cannot have multiple values') } }) test('breadcrumbless records should always return a string', async () => { const sp = new URLSearchParams() sp.set('query', 'breadcrumbs') - const res = await get('/api/search/v1?' + sp) + const res = await get('/api/search/v1?' + sp.toString()) expect(res.statusCode).toBe(200) - const results = JSON.parse(res.body) + const results: GeneralSearchResponse = JSON.parse(res.body) // safe because we know exactly the fixtures - const hit = results.hits[0] + const hit: GeneralSearchHit = results.hits[0] expect(hit.breadcrumbs).toBe('') }) }) @@ -268,9 +305,9 @@ describeIfElasticsearchURL("additional fields with 'include'", () => { test("'intro' and 'headings' are omitted by default", async () => { const sp = new URLSearchParams() sp.set('query', 'foo') - const res = await get('/api/search/v1?' + sp) + const res = await get('/api/search/v1?' + sp.toString()) expect(res.statusCode).toBe(200) - const results = JSON.parse(res.body) + const results: GeneralSearchResponse = JSON.parse(res.body) const firstKeys = Object.keys(results.hits[0]) expect(firstKeys.includes('intro')).toBeFalsy() expect(firstKeys.includes('headings')).toBeFalsy() @@ -280,9 +317,9 @@ describeIfElasticsearchURL("additional fields with 'include'", () => { const sp = new URLSearchParams() sp.set('query', 'foo') sp.set('include', 'intro') - const res = await get('/api/search/v1?' + sp) + const res = await get('/api/search/v1?' + sp.toString()) expect(res.statusCode).toBe(200) - const results = JSON.parse(res.body) + const results: GeneralSearchResponse = JSON.parse(res.body) const firstKeys = Object.keys(results.hits[0]) expect(firstKeys.includes('intro')).toBeTruthy() expect(firstKeys.includes('headings')).toBeFalsy() @@ -293,9 +330,9 @@ describeIfElasticsearchURL("additional fields with 'include'", () => { sp.set('query', 'foo') sp.append('include', 'intro') sp.append('include', 'headings') - const res = await get('/api/search/v1?' + sp) + const res = await get('/api/search/v1?' + sp.toString()) expect(res.statusCode).toBe(200) - const results = JSON.parse(res.body) + const results: GeneralSearchResponse = JSON.parse(res.body) const firstKeys = Object.keys(results.hits[0]) expect(firstKeys.includes('intro')).toBeTruthy() expect(firstKeys.includes('headings')).toBeTruthy() @@ -305,9 +342,12 @@ describeIfElasticsearchURL("additional fields with 'include'", () => { const sp = new URLSearchParams() sp.set('query', 'foo') sp.set('include', 'xxxxx') - const res = await get('/api/search/v1?' + sp) + const res = await get('/api/search/v1?' + sp.toString()) expect(res.statusCode).toBe(400) - const results = JSON.parse(res.body) + const results = JSON.parse(res.body) as { + error: string + field?: string + } expect(results.error).toMatch(`Not a valid value ([ 'xxxxx' ]) for key 'include'`) }) }) @@ -319,9 +359,9 @@ describeIfElasticsearchURL('filter by toplevel', () => { const sp = new URLSearchParams() sp.set('query', 'foo') sp.set('include', 'toplevel') - const res = await get('/api/search/v1?' + sp) + const res = await get('/api/search/v1?' + sp.toString()) expect(res.statusCode).toBe(200) - const results = JSON.parse(res.body) + const results: GeneralSearchResponse = JSON.parse(res.body) // In the fixtures, there are two distinct `toplevel` that // matches to this search. const toplevels = new Set(results.hits.map((hit) => hit.toplevel)) @@ -333,9 +373,9 @@ describeIfElasticsearchURL('filter by toplevel', () => { sp.set('query', 'foo') sp.set('include', 'toplevel') sp.set('toplevel', 'Baring') - const res = await get('/api/search/v1?' + sp) + const res = await get('/api/search/v1?' + sp.toString()) expect(res.statusCode).toBe(200) - const results = JSON.parse(res.body) + const results: GeneralSearchResponse = JSON.parse(res.body) const toplevels = new Set(results.hits.map((hit) => hit.toplevel)) expect(toplevels).toEqual(new Set(['Baring'])) }) @@ -346,9 +386,9 @@ describeIfElasticsearchURL('filter by toplevel', () => { sp.set('include', 'toplevel') sp.append('toplevel', 'Baring') sp.append('toplevel', 'Fooing') - const res = await get('/api/search/v1?' + sp) + const res = await get('/api/search/v1?' + sp.toString()) expect(res.statusCode).toBe(200) - const results = JSON.parse(res.body) + const results: GeneralSearchResponse = JSON.parse(res.body) const toplevels = new Set(results.hits.map((hit) => hit.toplevel)) expect(toplevels).toEqual(new Set(['Fooing', 'Baring'])) }) @@ -358,9 +398,9 @@ describeIfElasticsearchURL('filter by toplevel', () => { sp.set('query', 'foo') sp.set('include', 'toplevel') sp.set('toplevel', 'Never heard of') - const res = await get('/api/search/v1?' + sp) + const res = await get('/api/search/v1?' + sp.toString()) expect(res.statusCode).toBe(200) - const results = JSON.parse(res.body) + const results: GeneralSearchResponse = JSON.parse(res.body) expect(results.meta.found.value).toBe(0) }) }) @@ -372,12 +412,14 @@ describeIfElasticsearchURL('aggregate', () => { const sp = new URLSearchParams() sp.set('query', 'foo') sp.set('aggregate', 'toplevel') - const res = await get('/api/search/v1?' + sp) + const res = await get('/api/search/v1?' + sp.toString()) expect(res.statusCode).toBe(200) - const results = JSON.parse(res.body) + const results: GeneralSearchResponse & { aggregations?: SearchResultAggregations } = JSON.parse( + res.body, + ) expect(results.aggregations).toBeTruthy() - expect(results.aggregations.toplevel).toBeTruthy() - const firstAgg = results.aggregations.toplevel[0] + expect(results.aggregations!.toplevel).toBeTruthy() + const firstAgg = results.aggregations!.toplevel[0] expect(firstAgg.key).toBeTruthy() expect(firstAgg.count).toBeTruthy() }) @@ -386,8 +428,12 @@ describeIfElasticsearchURL('aggregate', () => { const sp = new URLSearchParams() sp.set('query', 'foo') sp.set('aggregate', 'unrecognizedxxx') - const res = await get('/api/search/v1?' + sp) + const res = await get('/api/search/v1?' + sp.toString()) expect(res.statusCode).toBe(400) - expect(JSON.parse(res.body).error).toMatch('aggregate') + const results = JSON.parse(res.body) as { + error: string + field?: string + } + expect(results.error).toMatch('aggregate') }) }) diff --git a/src/search/tests/fixtures/data/ai/search/queries/en/enterprise-cloud/queries.json b/src/search/tests/fixtures/data/ai/search/queries/en/enterprise-cloud/queries.json new file mode 100644 index 000000000000..73aa1eb48443 --- /dev/null +++ b/src/search/tests/fixtures/data/ai/search/queries/en/enterprise-cloud/queries.json @@ -0,0 +1,52 @@ +{ + "topQueries": [ + "How do I authenticate with SAML SSO?", + "What is GitHub Copilot?", + "How do I manage billing for GitHub Copilot?", + "How do I view my GitHub Actions usage?", + "How do I add or edit a payment method?" + ], + "allQueries": [ + "How do I authenticate with SAML SSO?", + "What is GitHub Copilot?", + "How do I manage billing for GitHub Copilot?", + "How do I view my GitHub Actions usage?", + "How do I add or edit a payment method?", + "How do I manage my GitHub billing settings?", + "How do I create an enterprise account?", + "How do I manage licenses for Visual Studio subscriptions with GitHub Enterprise?", + "How do I view my payment history and receipts?", + "How do I manage billing for Git Large File Storage?", + "How do I authorize a personal access token for SAML SSO?", + "How do I manage billing for GitHub Advanced Security?", + "How do I set up a trial of GitHub Enterprise Cloud?", + "How do I manage my spending limit for GitHub Actions?", + "How do I prevent overspending on GitHub?", + "How do I estimate spending for my enterprise?", + "How do I authorize an SSH key for SAML SSO?", + "How do I view my subscriptions and billing date?", + "How do I manage security settings for my organization?", + "How do I close an issue?", + "How do I link a pull request to an issue?", + "How do I verify or approve a domain for my organization?", + "How do I manage billing for GitHub Codespaces?", + "How do I manage billing for GitHub Packages?", + "How do I change the visibility of my GitHub Pages site?", + "How do I manage custom repository roles for an organization?", + "How do I downgrade a sponsorship?", + "How do I upgrade a sponsorship?", + "How do I downgrade the billing plan for a GitHub Marketplace app?", + "How do I use projects and tasklists?", + "How do I transfer an issue to another repository?", + "How do I create an issue?", + "How do I delete an issue?", + "How do I manage billing for GitHub Marketplace?", + "How do I manage billing for GitHub Sponsors?", + "How do I troubleshoot a declined credit card charge?", + "How do I get code suggestions in my IDE with GitHub Copilot?", + "How do I manage my personal access tokens?", + "How do I unlock a locked account?", + "How do I manage custom properties for repositories in my organization?", + "How do I use advanced secret scanning features?" + ] +} diff --git a/src/search/tests/fixtures/data/ai/search/queries/en/free-pro-team/queries.json b/src/search/tests/fixtures/data/ai/search/queries/en/free-pro-team/queries.json new file mode 100644 index 000000000000..705c0222393b --- /dev/null +++ b/src/search/tests/fixtures/data/ai/search/queries/en/free-pro-team/queries.json @@ -0,0 +1,52 @@ +{ + "topQueries": [ + "What is GitHub and how do I get started?", + "What is GitHub Copilot and how do I get started?", + "How do I connect to GitHub with SSH?", + "How do I generate a personal access token?", + "How do I clone a repository?" + ], + "allQueries": [ + "How do I generate a new SSH key and add it to the SSH agent?", + "What are the GitHub terms of service?", + "How do I connect to GitHub with SSH?", + "How do I generate a personal access token?", + "How do I get code suggestions in my IDE with GitHub Copilot?", + "How do I clone a repository?", + "How do I create a new repository?", + "How do I change my primary email address on GitHub?", + "How do I set up Git?", + "What are GitHub's plans?", + "How do I propose changes with pull requests?", + "How do I manage billing on GitHub?", + "How do I configure a publishing source for my GitHub Pages site?", + "How do I add a new SSH key to my GitHub account?", + "How do I set up a GitHub Pages site?", + "How do I recover my account if I lose my 2FA credentials?", + "How do I personalize my GitHub profile?", + "How do I view my GitHub Actions usage?", + "How do I manage my spending limit for GitHub Actions?", + "How do I create an issue on GitHub?", + "How do I verify my email address on GitHub?", + "How do I ignore files in Git?", + "How do I install GitHub Desktop?", + "How do I test my SSH connection to GitHub?", + "How do I fork a repository?", + "How do I resolve 'Permission denied (publickey)' error?", + "How do I add a theme to my GitHub Pages site using Jekyll?", + "How do I manage a custom domain for my GitHub Pages site?", + "How do I manage Copilot policies as an individual subscriber?", + "How do I manage deploy keys on GitHub?", + "How do I manage my profile README on GitHub?", + "How do I create a tasklist on GitHub?", + "How do I delete a repository?", + "How do I view my Git Large File Storage usage?", + "How do I add an email address to my GitHub account?", + "How do I manage OAuth app access restrictions for my organization?", + "How do I view all of my issues and pull requests?", + "How do I manage billing for GitHub Codespaces?", + "How do I manage billing for Git Large File Storage?", + "How do I view my payment history and receipts on GitHub?", + "How do I unlock a locked account on GitHub?" + ] +} diff --git a/src/search/tests/fixtures/search-indexes/github-docs-dotcom-en-records.json b/src/search/tests/fixtures/search-indexes/tests_github-docs_general-search_fpt_en-records.json similarity index 100% rename from src/search/tests/fixtures/search-indexes/github-docs-dotcom-en-records.json rename to src/search/tests/fixtures/search-indexes/tests_github-docs_general-search_fpt_en-records.json diff --git a/src/search/tests/fixtures/search-indexes/github-docs-dotcom-ja-records.json b/src/search/tests/fixtures/search-indexes/tests_github-docs_general-search_fpt_ja-records.json similarity index 100% rename from src/search/tests/fixtures/search-indexes/github-docs-dotcom-ja-records.json rename to src/search/tests/fixtures/search-indexes/tests_github-docs_general-search_fpt_ja-records.json diff --git a/src/search/tests/fixtures/search-indexes/github-docs-ghec-en-records.json b/src/search/tests/fixtures/search-indexes/tests_github-docs_general-search_ghec_en-records.json similarity index 100% rename from src/search/tests/fixtures/search-indexes/github-docs-ghec-en-records.json rename to src/search/tests/fixtures/search-indexes/tests_github-docs_general-search_ghec_en-records.json diff --git a/src/search/tests/fixtures/search-indexes/github-docs-ghec-ja-records.json b/src/search/tests/fixtures/search-indexes/tests_github-docs_general-search_ghec_ja-records.json similarity index 100% rename from src/search/tests/fixtures/search-indexes/github-docs-ghec-ja-records.json rename to src/search/tests/fixtures/search-indexes/tests_github-docs_general-search_ghec_ja-records.json diff --git a/src/search/tests/parse-page-sections-into-records.js b/src/search/tests/parse-page-sections-into-records.ts similarity index 63% rename from src/search/tests/parse-page-sections-into-records.js rename to src/search/tests/parse-page-sections-into-records.ts index c6811f3e671f..7367ccf749f2 100644 --- a/src/search/tests/parse-page-sections-into-records.js +++ b/src/search/tests/parse-page-sections-into-records.ts @@ -5,10 +5,19 @@ import fs from 'fs/promises' import cheerio from 'cheerio' import { describe, expect, test } from 'vitest' -import parsePageSectionsIntoRecords from '../scripts/parse-page-sections-into-records' +import parsePageSectionsIntoRecords from '@/search/scripts/scrape/lib/parse-page-sections-into-records' +import type { Record } from '@/search/scripts/scrape/types' + const __dirname = path.dirname(fileURLToPath(import.meta.url)) -const fixtures = { +// Define the shape of fixtures with explicit keys and string values +const fixtures: { + pageWithSections: string + pageWithoutSections: string + pageWithoutBody: string + pageMultipleH1s: string + pageHeadingParagraphNoWhitespace: string +} = { pageWithSections: await fs.readFile( path.join(__dirname, 'fixtures/page-with-sections.html'), 'utf8', @@ -33,11 +42,11 @@ const fixtures = { describe('search parsePageSectionsIntoRecords module', () => { test('works for pages with sections', () => { - const html = fixtures.pageWithSections + const html: string = fixtures.pageWithSections const $ = cheerio.load(html) - const href = '/example/href' - const record = parsePageSectionsIntoRecords({ href, $, languageCode: 'en' }) - const expected = { + const href: string = '/example/href' + const record: Record = parsePageSectionsIntoRecords({ href, $, languageCode: 'en' }) + const expected: Record = { objectID: '/example/href', breadcrumbs: 'GitHub Actions / actions learning path', title: 'I am the page title', @@ -58,11 +67,11 @@ describe('search parsePageSectionsIntoRecords module', () => { }) test('works for pages without sections', () => { - const html = fixtures.pageWithoutSections + const html: string = fixtures.pageWithoutSections const $ = cheerio.load(html) - const href = '/example/href' - const record = parsePageSectionsIntoRecords({ href, $, languageCode: 'en' }) - const expected = { + const href: string = '/example/href' + const record: Record = parsePageSectionsIntoRecords({ href, $, languageCode: 'en' }) + const expected: Record = { objectID: '/example/href', breadcrumbs: 'Education / map topic', title: 'A page without sections', @@ -76,11 +85,11 @@ describe('search parsePageSectionsIntoRecords module', () => { }) test('works for pages without content', () => { - const html = fixtures.pageWithoutBody + const html: string = fixtures.pageWithoutBody const $ = cheerio.load(html) - const href = '/example/href' - const record = parsePageSectionsIntoRecords({ href, $, languageCode: 'en' }) - const expected = { + const href: string = '/example/href' + const record: Record = parsePageSectionsIntoRecords({ href, $, languageCode: 'en' }) + const expected: Record = { objectID: '/example/href', breadcrumbs: 'Education / map topic', title: 'A page without body', @@ -94,35 +103,29 @@ describe('search parsePageSectionsIntoRecords module', () => { }) test('only picks up the first h1 for the title', () => { - const html = fixtures.pageMultipleH1s + const html: string = fixtures.pageMultipleH1s const $ = cheerio.load(html) - const href = '/example/href' - const record = parsePageSectionsIntoRecords({ href, $, languageCode: 'en' }) + const href: string = '/example/href' + const record: Record = parsePageSectionsIntoRecords({ href, $, languageCode: 'en' }) + expect(record.title).toEqual('I am the page title') }) test("content doesn't lump headings with paragraphs together", () => { - const html = fixtures.pageHeadingParagraphNoWhitespace + const html: string = fixtures.pageHeadingParagraphNoWhitespace const $ = cheerio.load(html) - const href = '/example/href' - const record = parsePageSectionsIntoRecords({ href, $, languageCode: 'en' }) - - // This is a

inside the page but it should only appear once. - // We had a bug where the heading would be injected twice. - // E.g. - // - //

Heading

Text here

- // - // would become: - // - // Heading\nHeadingText here - // - // So now we make sure it only appears exactly once. - expect(record.content.match(/Changing your primary email address/g).length).toBe(1) - // But note also that it would also concatenate the text of the heading - // with the text of the paragraph without a whitespace in between. + const href: string = '/example/href' + const record: Record = parsePageSectionsIntoRecords({ href, $, languageCode: 'en' }) + + // Ensure the heading appears only once + const headingMatches = record.content.match(/Changing your primary email address/g) + expect(headingMatches).not.toBeNull() + expect(headingMatches!.length).toBe(1) + + // Ensure there's no concatenation without whitespace expect(record.content.includes('email addressYou can set')).toBeFalsy() - // Make sure that inline elements are still together. + + // Ensure inline elements remain intact expect(record.content).toMatch(/Paragraph\./) }) }) diff --git a/src/search/tests/rendering.js b/src/search/tests/rendering.ts similarity index 93% rename from src/search/tests/rendering.js rename to src/search/tests/rendering.ts index abd96a762e2a..660f9d553a45 100644 --- a/src/search/tests/rendering.js +++ b/src/search/tests/rendering.ts @@ -13,9 +13,9 @@ import { expect, test, vi } from 'vitest' -import { describeIfElasticsearchURL } from '#src/tests/helpers/conditional-runs.js' -import { get, getDOM } from '#src/tests/helpers/e2etest.js' -import { SURROGATE_ENUMS } from '#src/frame/middleware/set-fastly-surrogate-key.js' +import { describeIfElasticsearchURL } from '@/tests/helpers/conditional-runs.js' +import { get, getDOM } from '@/tests/helpers/e2etest-ts' +import { SURROGATE_ENUMS } from '@/frame/middleware/set-fastly-surrogate-key.js' if (!process.env.ELASTICSEARCH_URL) { console.warn( @@ -51,7 +51,7 @@ describeIfElasticsearchURL('search rendering page', () => { test('response headers', async () => { const res = await get('/en/search?query=foo') - // Check that it can be cached at the CDN + // Assuming `res` has a type with a `headers` property expect(res.headers['set-cookie']).toBeUndefined() expect(res.headers['cache-control']).toContain('public') expect(res.headers['cache-control']).toMatch(/max-age=[1-9]/) @@ -107,7 +107,7 @@ describeIfElasticsearchURL('search rendering page', () => { expect(results.length).toBeGreaterThan(0) // Each link should have enterprise-cloud@latest in the pathname const links = $('[data-testid="search-result"] a') - const hrefs = links.map((i, el) => $(el).attr('href')).get() + const hrefs: string[] = links.map((_, el) => $(el).attr('href') ?? '').get() for (const href of hrefs) { expect(href).toMatch('/en/enterprise-cloud@latest/') } @@ -133,7 +133,7 @@ describeIfElasticsearchURL('search rendering page', () => { expect(res.statusCode).toBe(200) }) - test('more that one search query', async () => { + test('more than one search query', async () => { const $ = await getDOM('/en/search?query=foo&query=bar') expect($('[data-testid="search-results"]').text()).toMatch('Cannot have multiple values') const results = $('[data-testid="search-result"]') diff --git a/src/search/tests/search.js b/src/search/tests/search.js deleted file mode 100644 index aa554c3d0507..000000000000 --- a/src/search/tests/search.js +++ /dev/null @@ -1,37 +0,0 @@ -import { describe, expect, test, vi } from 'vitest' - -import { get, getDOM } from '#src/tests/helpers/e2etest.js' - -describe('search results page', () => { - vi.setConfig({ testTimeout: 60 * 1000 }) - - test('says something if no query is provided', async () => { - const $ = await getDOM('/en/search') - const $container = $('[data-testid="search-results"]') - expect($container.text()).toMatch(/Enter a search term/) - // Default is the frontmatter title of the content/search/index.md - expect($('title').text()).toMatch('Search - GitHub Docs') - }) - - test('says something if query is empty', async () => { - const $ = await getDOM(`/en/search?${new URLSearchParams({ query: ' ' })}`) - const $container = $('[data-testid="search-results"]') - expect($container.text()).toMatch(/Enter a search term/) - }) - - test('mention search term in h1', async () => { - const $ = await getDOM(`/en/search?${new URLSearchParams({ query: 'peterbe' })}`) - const $container = $('[data-testid="search-results"]') - const h1Text = $container.find('h1').text() - expect(h1Text).toMatch(/Search results for/) - expect(h1Text).toMatch(/peterbe/) - expect($('title').text()).toMatch(/Search results for "peterbe"/) - }) - - test('invalid version prefix 404s', async () => { - const res = await get( - `/en/enterprise-stuff@3.10/search?${new URLSearchParams({ query: 'peterbe' })}`, - ) - expect(res.statusCode).toBe(404) - }) -}) diff --git a/src/search/tests/search.ts b/src/search/tests/search.ts new file mode 100644 index 000000000000..5fed615cd522 --- /dev/null +++ b/src/search/tests/search.ts @@ -0,0 +1,40 @@ +import { describe, expect, test, vi } from 'vitest' +import { get, getDOM } from '@/tests/helpers/e2etest-ts' + +describe('search results page', () => { + vi.setConfig({ testTimeout: 60 * 1000 }) + + test('says something if no query is provided', async (): Promise => { + const $ = await getDOM('/en/search') + const $container = $('[data-testid="search-results"]') + expect($container.text()).toMatch(/Enter a search term/) + // Default is the frontmatter title of the content/search/index.md + expect($('title').text()).toMatch('Search - GitHub Docs') + }) + + test('says something if query is empty', async (): Promise => { + const queryParams = new URLSearchParams({ query: ' ' }).toString() + const $ = await getDOM(`/en/search?${queryParams}`) + const $container = $('[data-testid="search-results"]') + expect($container.text()).toMatch(/Enter a search term/) + }) + + test('mentions search term in h1', async (): Promise => { + const searchTerm = 'peterbe' + const queryParams = new URLSearchParams({ query: searchTerm }).toString() + const $ = await getDOM(`/en/search?${queryParams}`) + const $container = $('[data-testid="search-results"]') + const h1Text: string = $container.find('h1').text() + + expect(h1Text).toMatch(/Search results for/) + expect(h1Text).toMatch(new RegExp(searchTerm)) + expect($('title').text()).toMatch(new RegExp(`Search results for "${searchTerm}"`)) + }) + + test('invalid version prefix 404s', async (): Promise => { + const queryParams = new URLSearchParams({ query: 'peterbe' }).toString() + const response = await get(`/en/enterprise-stuff@3.10/search?${queryParams}`) + + expect(response.statusCode).toBe(404) + }) +}) diff --git a/src/search/tests/topics.js b/src/search/tests/topics.js deleted file mode 100644 index 7efe8e8538e1..000000000000 --- a/src/search/tests/topics.js +++ /dev/null @@ -1,39 +0,0 @@ -import path from 'path' -import fs from 'fs' - -import { describe, expect, test } from 'vitest' -import walk from 'walk-sync' -import { difference } from 'lodash-es' - -import readFrontmatter from '#src/frame/lib/read-frontmatter.js' -import allowedTopics from '../../../data/allowed-topics.js' - -const contentDir = path.join(process.cwd(), 'content') -const topics = walk(contentDir, { includeBasePath: true }) - .filter((filename) => filename.endsWith('.md') && !filename.includes('README')) - .map((filename) => { - const fileContent = fs.readFileSync(filename, 'utf8') - const { data, errors } = readFrontmatter(fileContent) - if (errors.length > 0) { - console.warn(errors) - throw new Error(`More than 0 front-matter errors`) - } - return data.topics || [] - }) - .flat() - -const allUsedTopics = [...new Set(topics)].sort() - -describe('Check for allowed frontmatter topics', () => { - test('all used topics are allowed in /data/allowed-topics.js', () => { - expect(allUsedTopics.length).toBeGreaterThan(0) - const unusedTopics = difference(allUsedTopics, allowedTopics) - expect(unusedTopics).toEqual([]) - }) - - test('all allowed topics are used by at least one content file', () => { - expect(allowedTopics.length).toBeGreaterThan(0) - const disallowedTopics = difference(allowedTopics, allUsedTopics) - expect(disallowedTopics).toEqual([]) - }) -}) diff --git a/src/search/tests/topics.ts b/src/search/tests/topics.ts new file mode 100644 index 000000000000..c9cd13c9bfee --- /dev/null +++ b/src/search/tests/topics.ts @@ -0,0 +1,44 @@ +import path from 'path' +import fs from 'fs' + +import { describe, expect, test } from 'vitest' +import walk from 'walk-sync' +import { difference } from 'lodash-es' + +import readFrontmatter from '@/frame/lib/read-frontmatter' +import allowedTopics from '../../../data/allowed-topics' + +const contentDir: string = path.join(process.cwd(), 'content') + +const topics: string[] = walk(contentDir, { includeBasePath: true }) + .filter((filename: string) => filename.endsWith('.md') && !filename.includes('README')) + .map((filename: string) => { + const fileContent: string = fs.readFileSync(filename, 'utf8') + const { data, errors } = readFrontmatter(fileContent) + + if (errors.length > 0) { + console.warn(errors) + throw new Error(`More than 0 front-matter errors in file: ${filename}`) + } + + return (data as any).topics || [] + }) + .flat() + +const allUsedTopics: string[] = Array.from(new Set(topics)).sort() + +describe('Check for allowed frontmatter topics', () => { + test('all used topics are allowed in /data/allowed-topics.js', () => { + expect(allUsedTopics.length).toBeGreaterThan(0) + + const unusedTopics: string[] = difference(allUsedTopics, allowedTopics) + expect(unusedTopics).toEqual([]) + }) + + test('all allowed topics are used by at least one content file', () => { + expect(allowedTopics.length).toBeGreaterThan(0) + + const disallowedTopics: string[] = difference(allowedTopics, allUsedTopics) + expect(disallowedTopics).toEqual([]) + }) +}) diff --git a/src/search/types.ts b/src/search/types.ts new file mode 100644 index 000000000000..bab9fb97b734 --- /dev/null +++ b/src/search/types.ts @@ -0,0 +1,76 @@ +import type { SearchTotalHits } from '@elastic/elasticsearch/lib/api/types' +import type { + AdditionalIncludes, + ComputedSearchQueryParamsMap, +} from '@/search/lib/search-request-params/types' + +export type SearchTypes = 'generalSearch' | 'generalAutocomplete' | 'aiSearchAutocomplete' + +// Responses to API routes +export interface GeneralSearchResponse { + meta: SearchResultsMeta & { + page: number + } + hits: GeneralSearchHit[] + aggregations?: SearchResultAggregations | null +} + +export interface AutocompleteSearchResponse { + meta: SearchResultsMeta + hits: AutocompleteSearchHit[] +} + +// Response to middleware /search route +export interface SearchOnReqObject { + searchParams: ComputedSearchQueryParamsMap[Type] + validationErrors: SearchValidationErrorEntry[] + results?: GeneralSearchResponse +} + +export interface SearchValidationErrorEntry { + error: string + key?: string + field?: string +} + +// - - - Types for building the search responses - - - +export interface GeneralSearchHitWithoutIncludes { + id: string + url: string + title: string + breadcrumbs: string + topics?: string[] + score?: number + popularity?: number + es_url?: string + highlights: { + [key: string]: string[] + } +} + +export type GeneralSearchHit = GeneralSearchHitWithoutIncludes & { + [key in AdditionalIncludes]?: string +} + +interface AutocompleteSearchHit { + term?: string + highlights: string[] +} + +export type SearchAggregation = { + key: string + count: number +} + +export type SearchResultAggregations = { + [key: string]: SearchAggregation[] +} + +type SearchResultsMeta = { + found: SearchTotalHits + took: { + query_msec: number + total_msec: number + } + size: number +} diff --git a/src/tests/README.md b/src/tests/README.md index c0ae0e0f3074..d144226b2f2f 100644 --- a/src/tests/README.md +++ b/src/tests/README.md @@ -54,6 +54,10 @@ npm test -- vitest path/to/tests/directory ``` +## Allowing logging in tests + +If you set up a `console.log` in the code and want to see the output, simply append the `--silent false` flag to your test to see console output. + ## Failed Local Tests If the tests fail locally with an error like this: diff --git a/src/tests/helpers/e2etest-ts.ts b/src/tests/helpers/e2etest-ts.ts new file mode 100644 index 000000000000..9d489cdf28d0 --- /dev/null +++ b/src/tests/helpers/e2etest-ts.ts @@ -0,0 +1,181 @@ +import cheerio from 'cheerio' +import got, { Response, OptionsOfTextResponseBody, Method } from 'got' +import { omitBy, isUndefined } from 'lodash-es' + +type ResponseTypes = 'buffer' | 'json' | 'text' +type ResponseTypeMap = { + buffer: ArrayBuffer + json: any + text: string +} + +interface GetOptions { + method?: M + body?: any + followRedirects?: boolean + followAllRedirects?: boolean + headers?: Record + responseType?: ResponseType + retries?: number +} + +interface GetDOMOptions { + headers?: Record + allow500s?: boolean + allow404?: boolean + retries?: number +} + +interface ResponseWithHeaders extends Response { + headers: Record +} + +// Cache to store DOM objects +const getDOMCache = new Map() + +/** + * Makes an HTTP request using the specified method and options. + * + * @param route - The route to request. + * @param options - Configuration options for the request. + * @returns A promise that resolves to the HTTP response. + */ +export async function get( + route: string, + options: GetOptions = {}, +): Promise> { + const { + method = 'get', + body, + followRedirects = false, + followAllRedirects = false, + headers = {}, + responseType, + retries = 0, + } = options + + // Ensure the method is a valid function on `got` + const fn = got[method as 'get'] + if (!fn || typeof fn !== 'function') { + throw new Error(`No method function for '${method}'`) + } + + // Construct the options for the `got` request, omitting undefined values + const xopts: OptionsOfTextResponseBody = omitBy( + { + body, + headers, + retry: { limit: retries }, + throwHttpErrors: false, + followRedirect: followAllRedirects || followRedirects, + responseType: responseType || undefined, + }, + isUndefined, + ) + + // Perform the HTTP request + return (await fn(`http://localhost:4000${route}`, xopts)) as ResponseWithHeaders< + ResponseTypeMap[T] + > +} + +/** + * Makes a HEAD HTTP request to the specified route. + * + * @param route - The route to request. + * @param opts - Options for following redirects. + * @returns A promise that resolves to the HTTP response. + */ +export async function head( + route: string, + opts: { followRedirects?: boolean } = { followRedirects: false }, +): Promise> { + const res = await get(route, { method: 'head', followRedirects: opts.followRedirects }) + return res +} + +/** + * Makes a POST HTTP request to the specified route. + * + * @param route - The route to request. + * @param opts - Options for the request. + * @returns A promise that resolves to the HTTP response. + */ +export function post( + route: string, + opts: Omit = {}, +): Promise> { + return get(route, { ...opts, method: 'post' }) +} + +/** + * Retrieves a cached DOM object for the specified route and options. + * If the DOM is not cached, it fetches and caches it. + * + * @param route - The route to request. + * @param options - Options for fetching the DOM. + * @returns A promise that resolves to the cached DOM object. + */ +export async function getDOMCached( + route: string, + options: GetDOMOptions = {}, +): Promise { + const key = `${route}::${JSON.stringify(options)}` + if (!getDOMCache.has(key)) { + const dom = await getDOM(route, options) + getDOMCache.set(key, dom) + } + // The non-null assertion is safe here because we've just set the key if it didn't exist + return getDOMCache.get(key)! +} + +/** + * Fetches the DOM for the specified route and options. + * + * @param route - The route to request. + * @param options - Options for fetching the DOM. + * @returns A promise that resolves to the loaded DOM object. + */ +export async function getDOM(route: string, options: GetDOMOptions = {}): Promise { + const { headers, allow500s = false, allow404 = false, retries = 0 } = options + const res = await get(route, { followRedirects: true, headers, retries }) + + if (!allow500s && res.statusCode >= 500) { + throw new Error(`Server error (${res.statusCode}) on ${route}`) + } + + if (!allow404 && res.statusCode === 404) { + throw new Error(`Page not found on ${route} (${res.statusCode})`) + } + + const $ = cheerio.load(res.body || '', { xmlMode: true }) + + // Extend the Cheerio instance with the response object + ;($ as any).res = { ...res } + + return $ +} + +/** + * Fetches and parses JSON from the specified route. + * + * @param route - The route to request. + * @param opts - Options for the request. + * @returns A promise that resolves to the parsed JSON object. + */ +export async function getJSON( + route: string, + opts: Omit = {}, +): Promise { + const res = await get(route, { ...opts, followRedirects: true }) + + if (res.statusCode >= 500) { + throw new Error(`Server error (${res.statusCode}) on ${route}`) + } + + if (res.statusCode >= 400) { + console.warn(`${res.statusCode} on ${route} and the response might not be JSON`) + } + + return JSON.parse(res.body) +} diff --git a/tsconfig.json b/tsconfig.json index 96bc64921938..86975737dcc0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -27,6 +27,7 @@ }, "exclude": [ "node_modules", + "docs-internal-data", "src/code-scanning/scripts/generate-code-scanning-query-list.ts" ], "include": [ From a6d1ef254ca246ae595a9e6a60e9bf8f521d8c89 Mon Sep 17 00:00:00 2001 From: textractor Date: Thu, 7 Nov 2024 11:08:24 -0800 Subject: [PATCH 11/12] Update creating-a-github-pages-site-with-jekyll.md (#35092) Co-authored-by: Alex Nguyen <150945400+nguyenalex836@users.noreply.github.com> --- .../creating-a-github-pages-site-with-jekyll.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/pages/setting-up-a-github-pages-site-with-jekyll/creating-a-github-pages-site-with-jekyll.md b/content/pages/setting-up-a-github-pages-site-with-jekyll/creating-a-github-pages-site-with-jekyll.md index 18a79134730c..9ba19aad8296 100644 --- a/content/pages/setting-up-a-github-pages-site-with-jekyll/creating-a-github-pages-site-with-jekyll.md +++ b/content/pages/setting-up-a-github-pages-site-with-jekyll/creating-a-github-pages-site-with-jekyll.md @@ -82,7 +82,7 @@ Before you can use Jekyll to create a {% data variables.product.prodname_pages % # Removes the contents from your default branch from the working directory ``` -1. To create a new Jekyll site, use the `jekyll new` command: +1. To create a new Jekyll site, use the `jekyll new` command in your repository's root directory: ```shell $ jekyll new --skip-bundle . From 6855f297293bf39ea4bad483debbb8e49721f48d Mon Sep 17 00:00:00 2001 From: Artur Kordowski <9746197+akordowski@users.noreply.github.com> Date: Thu, 7 Nov 2024 20:46:37 +0100 Subject: [PATCH 12/12] Fix inconsistent alerts by using the markdown notation - part 1 (#35220) Co-authored-by: Alex Nguyen <150945400+nguyenalex836@users.noreply.github.com> --- .../managing-your-subscriptions.md | 7 +- .../viewing-your-subscriptions.md | 28 +- ...orkflow-for-triaging-your-notifications.md | 12 +- .../managing-notifications-from-your-inbox.md | 7 +- .../managing-your-profile-readme.md | 7 +- .../personalizing-your-profile.md | 46 +- .../setting-your-profile-to-private.md | 14 +- ...ributions-from-github-enterprise-server.md | 7 +- .../viewing-contributions-on-your-profile.md | 14 +- ...tributions-not-showing-up-on-my-profile.md | 12 +- ...an-email-address-to-your-github-account.md | 10 +- ...membering-your-github-username-or-email.md | 7 +- .../setting-your-commit-email-address.md | 14 +- .../verifying-your-email-address.md | 11 +- .../about-your-personal-dashboard.md | 14 +- .../changing-your-github-username.md | 21 +- ...evels-for-a-personal-account-repository.md | 14 +- .../about-organization-membership.md | 7 +- .../accessing-an-organization.md | 6 +- .../removing-yourself-from-an-organization.md | 11 +- ...iewing-peoples-roles-in-an-organization.md | 7 +- .../converting-a-user-into-an-organization.md | 33 +- .../deleting-your-personal-account.md | 14 +- .../managing-multiple-accounts.md | 7 +- .../merging-multiple-personal-accounts.md | 22 +- ...our-email-address-from-a-locked-account.md | 26 +- ...usage-limits-billing-and-administration.md | 18 +- .../about-actions-runner-controller.md | 14 +- ...t-support-for-actions-runner-controller.md | 11 +- .../authenticating-to-the-github-api.md | 17 +- ...ale-sets-with-actions-runner-controller.md | 157 ++---- ...uickstart-for-actions-runner-controller.md | 17 +- .../adding-self-hosted-runners.md | 18 +- .../autoscaling-with-self-hosted-runners.md | 7 +- ...-hosted-runner-application-as-a-service.md | 17 +- ...customizing-the-containers-used-by-jobs.md | 21 +- .../removing-self-hosted-runners.md | 38 +- .../running-scripts-before-or-after-a-job.md | 21 +- .../using-labels-with-self-hosted-runners.md | 24 +- .../managing-environments-for-deployment.md | 82 ++- .../reviewing-deployments.md | 18 +- .../disabling-and-enabling-a-workflow.md | 7 +- .../manually-running-a-workflow.md | 13 +- .../removing-workflow-artifacts.md | 7 +- .../skipping-workflow-runs.md | 14 +- ...tions-importer-with-custom-transformers.md | 21 +- ...ure-devops-with-github-actions-importer.md | 7 +- ...rom-bamboo-with-github-actions-importer.md | 7 +- ...om-jenkins-with-github-actions-importer.md | 7 +- .../adding-a-workflow-status-badge.md | 7 +- .../using-workflow-run-logs.md | 7 +- .../viewing-job-execution-time.md | 7 +- ...working-with-support-for-github-actions.md | 18 +- .../automatic-token-authentication.md | 26 +- .../security-hardening-for-github-actions.md | 21 +- ...es-to-secure-your-use-of-github-actions.md | 16 +- .../using-secrets-in-github-actions.md | 69 +-- ...-security-hardening-with-openid-connect.md | 21 +- ...g-openid-connect-in-amazon-web-services.md | 18 +- .../configuring-openid-connect-in-azure.md | 6 +- ...uring-openid-connect-in-hashicorp-vault.md | 25 +- .../configuring-openid-connect-in-pypi.md | 8 +- .../metadata-syntax-for-github-actions.md | 7 +- ...ublishing-actions-in-github-marketplace.md | 13 +- ...orkflow-templates-for-your-organization.md | 21 +- .../sharing-automations/reusing-workflows.md | 34 +- ...-workflows-from-your-private-repository.md | 10 +- ...ions-and-workflows-with-your-enterprise.md | 10 +- ...ns-and-workflows-with-your-organization.md | 10 +- .../building-and-testing-net.md | 7 +- .../building-and-testing-powershell.md | 21 +- .../deploying-docker-to-azure-app-service.md | 7 +- .../deploying-java-to-azure-app-service.md | 7 +- .../deploying-net-to-azure-app-service.md | 7 +- .../deploying-nodejs-to-azure-app-service.md | 7 +- .../deploying-php-to-azure-app-service.md | 7 +- .../deploying-python-to-azure-app-service.md | 7 +- ...ing-to-amazon-elastic-container-service.md | 7 +- .../deploying-to-azure-kubernetes-service.md | 7 +- .../deploying-to-azure-static-web-app.md | 7 +- .../deploying-to-google-kubernetes-engine.md | 14 +- .../deploying-with-github-actions.md | 7 +- ...-on-macos-runners-for-xcode-development.md | 7 +- .../closing-inactive-issues.md | 7 +- .../scheduling-issue-creation.md | 7 +- .../publishing-docker-images.md | 7 +- .../about-github-hosted-runners.md | 7 +- .../customizing-github-hosted-runners.md | 7 +- .../controlling-access-to-larger-runners.md | 14 +- .../running-jobs-on-larger-runners.md | 14 +- ...textual-information-about-workflow-runs.md | 7 +- ...hing-dependencies-to-speed-up-workflows.md | 20 +- ...te-expressions-in-workflows-and-actions.md | 26 +- .../store-information-in-variables.md | 42 +- ...toring-and-sharing-data-from-a-workflow.md | 7 +- ...ritten-building-blocks-in-your-workflow.md | 7 +- .../workflow-commands-for-github-actions.md | 100 ++-- .../events-that-trigger-workflows.md | 524 ++++++------------ ...ing-conditions-to-control-job-execution.md | 7 +- .../workflow-syntax-for-github-actions.md | 21 +- .../command-line-utilities.md | 57 +- .../configuring-backups-on-your-instance.md | 17 +- ...e-ecosystem-support-for-your-enterprise.md | 7 +- .../enabling-github-packages-with-aws.md | 12 +- ...github-packages-with-azure-blob-storage.md | 23 +- .../enabling-github-packages-with-minio.md | 13 +- .../about-github-connect.md | 7 +- ...enabling-dependabot-for-your-enterprise.md | 21 +- ...changing-the-hostname-for-your-instance.md | 6 +- ...onfiguring-an-outbound-web-proxy-server.md | 7 +- .../configuring-built-in-firewall-rules.md | 14 +- ...dress-using-the-virtual-machine-console.md | 6 +- .../configuring-time-synchronization.md | 10 +- ...-enterprise-server-with-a-load-balancer.md | 14 +- ...ithub-hosted-runners-in-your-enterprise.md | 14 +- .../configuring-email-for-notifications.md | 7 +- ...guring-github-pages-for-your-enterprise.md | 14 +- .../configuring-interactive-maps.md | 7 +- .../configuring-rate-limits.md | 14 +- .../configuring-web-commit-signing.md | 6 +- ...configuring-host-keys-for-your-instance.md | 7 +- ...the-referrer-policy-for-your-enterprise.md | 7 +- ...o-your-enterprise-with-an-ip-allow-list.md | 7 +- content/admin/configuring-settings/index.md | 5 +- ...or-github-codespaces-in-your-enterprise.md | 7 +- ...or-security-settings-in-your-enterprise.md | 27 +- ...-management-policies-in-your-enterprise.md | 12 +- .../about-pre-receive-hooks.md | 7 +- ...creating-a-pre-receive-hook-environment.md | 12 +- ...talling-github-enterprise-server-on-aws.md | 15 +- ...lling-github-enterprise-server-on-azure.md | 14 +- .../setting-up-a-staging-instance.md | 42 +- ...izing-user-messages-for-your-enterprise.md | 17 +- .../managing-projects-using-jira.md | 6 +- ...n-organization-owned-by-your-enterprise.md | 5 +- ...ving-organizations-from-your-enterprise.md | 15 +- ...ctor-authentication-for-an-organization.md | 13 +- ...r-owned-repositories-in-your-enterprise.md | 7 +- ...r-owned-repositories-in-your-enterprise.md | 7 +- .../auditing-users-across-your-enterprise.md | 11 +- .../enabling-guest-collaborators.md | 6 +- ...bership-information-for-your-enterprise.md | 7 +- ...organization-members-in-your-enterprise.md | 17 +- ...upport-entitlements-for-your-enterprise.md | 7 +- .../removing-a-member-from-your-enterprise.md | 7 +- .../viewing-people-in-your-enterprise.md | 7 +- ...guring-code-scanning-for-your-appliance.md | 15 +- ...d-security-features-for-your-enterprise.md | 7 +- ...ot-to-work-with-limited-internet-access.md | 7 +- ...he-dependency-graph-for-your-enterprise.md | 6 +- ...ting-github-actions-for-your-enterprise.md | 6 +- .../using-a-staging-environment.md | 7 +- ...g-github-actions-with-amazon-s3-storage.md | 18 +- ...-github-actions-with-azure-blob-storage.md | 12 +- ...ithub-actions-with-google-cloud-storage.md | 13 +- ...bling-github-actions-with-minio-storage.md | 7 +- ...ub-actions-for-github-enterprise-server.md | 22 +- ...self-hosted-runners-for-your-enterprise.md | 12 +- .../about-using-actions-in-your-enterprise.md | 15 +- ...-githubcom-actions-using-github-connect.md | 7 +- ...manually-syncing-actions-from-githubcom.md | 21 +- ...-hosted-runners-without-internet-access.md | 7 +- ...version-of-the-official-bundled-actions.md | 7 +- ...le-sign-on-for-enterprise-managed-users.md | 13 +- ...ovisioning-for-enterprise-managed-users.md | 14 +- ...siderations-for-external-authentication.md | 17 +- ...f-your-identity-provider-is-unavailable.md | 7 +- ...-accounts-single-sign-on-recovery-codes.md | 6 +- ...configuring-scim-provisioning-for-users.md | 6 +- ...configuring-scim-provisioning-with-okta.md | 12 +- ...mberships-with-identity-provider-groups.md | 14 +- ...and-groups-with-scim-using-the-rest-api.md | 7 +- .../user-provisioning-with-scim-on-ghes.md | 12 +- .../migrating-from-oidc-to-saml.md | 7 +- .../migrating-from-saml-to-oidc.md | 20 +- ...ication-for-users-outside-your-provider.md | 7 +- .../using-ldap.md | 35 +- ...saml-single-sign-on-for-your-enterprise.md | 14 +- ...saml-single-sign-on-for-your-enterprise.md | 7 +- .../enabling-encrypted-assertions.md | 7 +- .../troubleshooting-saml-authentication.md | 11 +- .../changing-the-url-for-your-enterprise.md | 14 +- .../accessing-reports-for-your-instance.md | 7 +- ...about-the-audit-log-for-your-enterprise.md | 7 +- ...uring-the-audit-log-for-your-enterprise.md | 7 +- ...ching-the-audit-log-for-your-enterprise.md | 7 +- .../configuring-a-repository-cache.md | 7 +- .../about-cluster-nodes.md | 7 +- ...-availability-replication-for-a-cluster.md | 84 ++- ...ng-a-cluster-node-running-data-services.md | 19 +- .../monitoring-the-health-of-your-cluster.md | 20 +- .../rebalancing-cluster-workloads.md | 7 +- .../replacing-a-cluster-node.md | 34 +- .../creating-a-high-availability-replica.md | 6 +- ...ng-a-failover-to-your-replica-appliance.md | 14 +- ...ering-a-high-availability-configuration.md | 7 +- .../removing-a-high-availability-replica.md | 5 +- .../about-system-logs.md | 7 +- ...ting-a-health-check-for-your-enterprise.md | 7 +- .../monitoring-using-snmp.md | 7 +- ...leshooting-resource-allocation-problems.md | 15 +- .../increasing-storage-capacity.md | 14 +- ...-up-a-trial-of-github-enterprise-server.md | 7 +- content/admin/overview/system-overview.md | 7 +- ...ng-from-github-enterprise-1110x-to-2123.md | 27 +- .../enabling-automatic-update-checks.md | 7 +- .../upgrade-requirements.md | 11 +- ...n-issues-with-upgrades-to-your-instance.md | 20 +- .../authenticating-as-a-github-app.md | 7 +- ...g-with-a-github-app-on-behalf-of-a-user.md | 7 +- ...g-a-json-web-token-jwt-for-a-github-app.md | 28 +- ...ng-a-user-access-token-for-a-github-app.md | 7 +- ...tallation-access-token-for-a-github-app.md | 7 +- .../managing-private-keys-for-github-apps.md | 7 +- .../refreshing-user-access-tokens.md | 7 +- .../about-the-setup-url.md | 7 +- ...ting-a-custom-badge-for-your-github-app.md | 5 +- .../using-webhooks-with-github-apps.md | 2 +- .../requirements-for-listing-an-app.md | 7 +- .../viewing-metrics-for-your-listing.md | 14 +- .../viewing-transactions-for-your-listing.md | 7 +- .../drafting-a-listing-for-your-app.md | 14 +- .../setting-pricing-plans-for-your-listing.md | 7 +- ...icing-plans-for-github-marketplace-apps.md | 7 +- .../receiving-payment-for-app-purchases.md | 7 +- .../handling-new-purchases-and-free-trials.md | 14 +- .../handling-plan-cancellations.md | 7 +- .../handling-plan-changes.md | 14 +- ...ndpoints-for-the-github-marketplace-api.md | 7 +- ...ating-optional-features-for-github-apps.md | 6 +- .../deleting-a-github-app.md | 14 +- .../suspending-a-github-app-installation.md | 7 +- .../authorizing-oauth-apps.md | 29 +- .../creating-an-oauth-app.md | 46 +- ...nces-between-github-apps-and-oauth-apps.md | 7 +- .../rate-limits-for-oauth-apps.md | 7 +- .../scopes-for-oauth-apps.md | 33 +- ...vating-optional-features-for-oauth-apps.md | 6 +- ...g-oauth-app-access-token-request-errors.md | 6 +- .../authorizing-oauth-apps.md | 28 +- ...onnecting-with-third-party-applications.md | 14 +- ...egistering-a-github-app-from-a-manifest.md | 7 +- .../authorizing-github-apps.md | 5 +- ...talling-a-github-app-from-a-third-party.md | 5 +- ...github-app-from-your-organization-owner.md | 11 +- ...ing-and-modifying-installed-github-apps.md | 7 +- .../managing-your-passkeys.md | 11 +- ...sh-key-for-use-with-saml-single-sign-on.md | 7 +- ...-and-managing-your-active-saml-sessions.md | 7 +- .../checking-for-existing-ssh-keys.md | 7 +- ...-ssh-key-and-adding-it-to-the-ssh-agent.md | 52 +- .../managing-deploy-keys.md | 15 +- .../testing-your-ssh-connection.md | 7 +- .../using-ssh-agent-forwarding.md | 7 +- .../working-with-ssh-key-passphrases.md | 7 +- .../about-authentication-to-github.md | 5 +- .../about-githubs-ip-addresses.md | 7 +- .../managing-your-personal-access-tokens.md | 28 +- .../reviewing-your-ssh-keys.md | 28 +- .../security-log-events.md | 11 +- .../sudo-mode.md | 14 +- .../switching-between-accounts.md | 7 +- .../token-expiration-and-revocation.md | 7 +- .../viewing-and-managing-your-sessions.md | 7 +- .../checking-for-existing-gpg-keys.md | 7 +- .../generating-a-new-gpg-key.md | 14 +- .../signing-commits.md | 21 +- ...out-mandatory-two-factor-authentication.md | 35 +- .../about-two-factor-authentication.md | 12 +- ...-github-using-two-factor-authentication.md | 11 +- ...g-your-two-factor-authentication-method.md | 7 +- .../configuring-two-factor-authentication.md | 29 +- ...uthentication-for-your-personal-account.md | 7 +- ...ccount-if-you-lose-your-2fa-credentials.md | 43 +- .../error-permission-denied-publickey.md | 14 +- ...dd-illegal-option----apple-use-keychain.md | 11 +- .../error-unknown-key-type.md | 11 +- 277 files changed, 1448 insertions(+), 3029 deletions(-) diff --git a/content/account-and-profile/managing-subscriptions-and-notifications-on-github/managing-subscriptions-for-activity-on-github/managing-your-subscriptions.md b/content/account-and-profile/managing-subscriptions-and-notifications-on-github/managing-subscriptions-for-activity-on-github/managing-your-subscriptions.md index bcdf32a42b34..6c954fb82383 100644 --- a/content/account-and-profile/managing-subscriptions-and-notifications-on-github/managing-subscriptions-for-activity-on-github/managing-your-subscriptions.md +++ b/content/account-and-profile/managing-subscriptions-and-notifications-on-github/managing-subscriptions-for-activity-on-github/managing-your-subscriptions.md @@ -14,11 +14,8 @@ shortTitle: Manage your subscriptions --- To help you understand your subscriptions and decide whether to unsubscribe, see "[AUTOTITLE](/account-and-profile/managing-subscriptions-and-notifications-on-github/managing-subscriptions-for-activity-on-github/viewing-your-subscriptions)." -{% note %} - -**Note:** Instead of unsubscribing, you have the option to ignore a repository. If you ignore a repository, you won't receive any notifications. We don't recommend ignoring repositories as you won't be notified if you're @mentioned. {% ifversion fpt or ghec %}If you're experiencing abuse and want to ignore a repository, please visit {% data variables.contact.contact_support_page %} so we can help. {% data reusables.policies.abuse %}{% endif %} - -{% endnote %} +> [!NOTE] +> Instead of unsubscribing, you have the option to ignore a repository. If you ignore a repository, you won't receive any notifications. We don't recommend ignoring repositories as you won't be notified if you're @mentioned. {% ifversion fpt or ghec %}If you're experiencing abuse and want to ignore a repository, please visit {% data variables.contact.contact_support_page %} so we can help. {% data reusables.policies.abuse %}{% endif %} ## Choosing how to unsubscribe diff --git a/content/account-and-profile/managing-subscriptions-and-notifications-on-github/managing-subscriptions-for-activity-on-github/viewing-your-subscriptions.md b/content/account-and-profile/managing-subscriptions-and-notifications-on-github/managing-subscriptions-for-activity-on-github/viewing-your-subscriptions.md index d48c918dc744..3125190241a1 100644 --- a/content/account-and-profile/managing-subscriptions-and-notifications-on-github/managing-subscriptions-for-activity-on-github/viewing-your-subscriptions.md +++ b/content/account-and-profile/managing-subscriptions-and-notifications-on-github/managing-subscriptions-for-activity-on-github/viewing-your-subscriptions.md @@ -33,11 +33,9 @@ We recommend auditing and unsubscribing from your subscriptions as a part of a h When your inbox has too many notifications to manage, consider whether you have oversubscribed or how you can change your notification settings to reduce the subscriptions you have and the types of notifications you're receiving. For example, you may consider disabling the settings to automatically watch all repositories {% ifversion team-discussions %}and all team discussions{% endif %} whenever you've joined a team or repository. For more information, see "[AUTOTITLE](/account-and-profile/managing-subscriptions-and-notifications-on-github/setting-up-notifications/configuring-notifications#automatic-watching)." To see an overview of your repository subscriptions, see "[Reviewing repositories that you're watching](#reviewing-repositories-that-youre-watching)." -{% tip %} -**Tip:** You can select the types of event to be notified of by using the **Custom** option of the **Watch/Unwatch** dropdown list in your [watching page](https://github.com/watching) or on any repository page on {% data variables.product.product_name %}. For more information, see "[AUTOTITLE](/account-and-profile/managing-subscriptions-and-notifications-on-github/setting-up-notifications/configuring-notifications#configuring-your-watch-settings-for-an-individual-repository)." - -{% endtip %} +> [!TIP] +> You can select the types of event to be notified of by using the **Custom** option of the **Watch/Unwatch** dropdown list in your [watching page](https://github.com/watching) or on any repository page on {% data variables.product.product_name %}. For more information, see "[AUTOTITLE](/account-and-profile/managing-subscriptions-and-notifications-on-github/setting-up-notifications/configuring-notifications#configuring-your-watch-settings-for-an-individual-repository)." Many people forget about repositories that they've chosen to watch in the past. From the "Watched repositories" page you can quickly unwatch repositories. For more information on ways to unsubscribe, see "[Unwatch recommendations](https://github.blog/changelog/2020-11-10-unwatch-recommendations/)" on {% data variables.product.prodname_blog %} and "[AUTOTITLE](/account-and-profile/managing-subscriptions-and-notifications-on-github/managing-subscriptions-for-activity-on-github/managing-your-subscriptions)." You can also create a triage workflow to help with the notifications you receive. For guidance on triage workflows, see "[AUTOTITLE](/account-and-profile/managing-subscriptions-and-notifications-on-github/viewing-and-triaging-notifications/customizing-a-workflow-for-triaging-your-notifications)." @@ -51,14 +49,9 @@ Many people forget about repositories that they've chosen to watch in the past. ![Screenshot of the "Subscriptions" tab. Three dropdown menus, titled "Reason", "Repository", and "Sort", are highlighted with an orange outline.](/assets/images/help/notifications-v2/all-subscriptions.png) -{% tip %} - -**Tips:** -* To review subscriptions you may have forgotten about, sort by "least recently subscribed." - -* To review a list of repositories that you can still receive notifications for, see the repository list in the "filter by repository" drop-down menu. - -{% endtip %} +> [!TIP] +> * To review subscriptions you may have forgotten about, sort by "least recently subscribed." +> * To review a list of repositories that you can still receive notifications for, see the repository list in the "filter by repository" drop-down menu. ## Reviewing repositories that you're watching @@ -66,10 +59,7 @@ Many people forget about repositories that they've chosen to watch in the past. ![Screenshot of the "Notifications" page. A dropdown menu, titled "Manage notifications", is highlighted with an orange outline.](/assets/images/help/notifications-v2/manage-notifications-options.png) 1. Evaluate the repositories that you are watching and decide if their updates are still relevant and helpful. When you watch a repository, you will be notified of all conversations for that repository. - {% tip %} - - **Tip:** Instead of watching a repository, consider only receiving notifications when there are updates to {% data reusables.notifications-v2.custom-notification-types %} (if enabled for the repository), or any combination of these options, or completely unwatching a repository. - - When you unwatch a repository, you can still be notified when you're @mentioned or participating in a thread. When you configure to receive notifications for certain event types, you're only notified when there are updates to these event types in the repository, you're participating in a thread, or you or a team you're on is @mentioned. - - {% endtip %} + > [!TIP] + > Instead of watching a repository, consider only receiving notifications when there are updates to {% data reusables.notifications-v2.custom-notification-types %} (if enabled for the repository), or any combination of these options, or completely unwatching a repository. + > + > When you unwatch a repository, you can still be notified when you're @mentioned or participating in a thread. When you configure to receive notifications for certain event types, you're only notified when there are updates to these event types in the repository, you're participating in a thread, or you or a team you're on is @mentioned. diff --git a/content/account-and-profile/managing-subscriptions-and-notifications-on-github/viewing-and-triaging-notifications/customizing-a-workflow-for-triaging-your-notifications.md b/content/account-and-profile/managing-subscriptions-and-notifications-on-github/viewing-and-triaging-notifications/customizing-a-workflow-for-triaging-your-notifications.md index 245cd622ebc0..1b4a127dba42 100644 --- a/content/account-and-profile/managing-subscriptions-and-notifications-on-github/viewing-and-triaging-notifications/customizing-a-workflow-for-triaging-your-notifications.md +++ b/content/account-and-profile/managing-subscriptions-and-notifications-on-github/viewing-and-triaging-notifications/customizing-a-workflow-for-triaging-your-notifications.md @@ -32,11 +32,8 @@ For example, you may decide to check your notifications in this order in the mor * Events where a team you're a member of is @mentioned, also called team mentions (filter by `reason:team-mention`) * CI workflow failures for a specific repository (filter by `reason:ci-activity` and `repo:owner/repo-name` and ensure you've enabled CI activity notifications for workflow failures in your notification settings) - {% tip %} - - **Tip:** To quickly review your highest priorities, set up custom filters in order of their reviewing priority. For more information, see "[AUTOTITLE](/account-and-profile/managing-subscriptions-and-notifications-on-github/viewing-and-triaging-notifications/managing-notifications-from-your-inbox#customizing-your-inbox-with-custom-filters)." - - {% endtip %} + > [!TIP] + > To quickly review your highest priorities, set up custom filters in order of their reviewing priority. For more information, see "[AUTOTITLE](/account-and-profile/managing-subscriptions-and-notifications-on-github/viewing-and-triaging-notifications/managing-notifications-from-your-inbox#customizing-your-inbox-with-custom-filters)." ## Following up on ongoing notification updates @@ -50,11 +47,10 @@ For example, you may decide to follow up in this order: After triaging the higher priority notifications, review the remaining notifications, such as participating notifications. Consider these questions: * Can you unsubscribe to this notification? Is this notification completed and ready to be marked as **Done**? - {% tip %} - **Tip:** When you unsubscribe from a notification you won't receive new updates unless you start participating in the thread or you're @mentioned or a team you're on is @mentioned. When you mark a notification as **Done**, the notification is removed from your main inbox view and can be viewed with the query `is:read`. For more information, see "[AUTOTITLE](/account-and-profile/managing-subscriptions-and-notifications-on-github/viewing-and-triaging-notifications/managing-notifications-from-your-inbox#triaging-options)." + > [!TIP] + > When you unsubscribe from a notification you won't receive new updates unless you start participating in the thread or you're @mentioned or a team you're on is @mentioned. When you mark a notification as **Done**, the notification is removed from your main inbox view and can be viewed with the query `is:read`. For more information, see "[AUTOTITLE](/account-and-profile/managing-subscriptions-and-notifications-on-github/viewing-and-triaging-notifications/managing-notifications-from-your-inbox#triaging-options)." - {% endtip %} * Would you like to receive future updates when this issue or pull request is closed or reopened, or when a pull request is merged? For more information on these options, see "[AUTOTITLE](/account-and-profile/managing-subscriptions-and-notifications-on-github/viewing-and-triaging-notifications/triaging-a-single-notification#customizing-when-to-receive-future-updates-for-an-issue-or-pull-request)." * Would you like to avoid receiving notifications like this in the future? If so, consider unsubscribing. For more information, see "[AUTOTITLE](/account-and-profile/managing-subscriptions-and-notifications-on-github/managing-subscriptions-for-activity-on-github)." diff --git a/content/account-and-profile/managing-subscriptions-and-notifications-on-github/viewing-and-triaging-notifications/managing-notifications-from-your-inbox.md b/content/account-and-profile/managing-subscriptions-and-notifications-on-github/viewing-and-triaging-notifications/managing-notifications-from-your-inbox.md index 9f4adc359047..7e4ac4f03df9 100644 --- a/content/account-and-profile/managing-subscriptions-and-notifications-on-github/viewing-and-triaging-notifications/managing-notifications-from-your-inbox.md +++ b/content/account-and-profile/managing-subscriptions-and-notifications-on-github/viewing-and-triaging-notifications/managing-notifications-from-your-inbox.md @@ -58,11 +58,8 @@ You can add up to 15 of your own custom filters. {% data reusables.notifications.access_notifications %} 1. To open the filter settings, in the left sidebar, next to "Filters", click {% octicon "gear" aria-label="Customize filters" %}. - {% tip %} - - **Tip:** You can quickly preview a filter's inbox results by creating a query in your inbox view and clicking **Save**, which opens the custom filter settings. - - {% endtip %} + > [!TIP] + > You can quickly preview a filter's inbox results by creating a query in your inbox view and clicking **Save**, which opens the custom filter settings. 1. Add a name for your filter and a filter query. For example, to only see notifications for a specific repository, you can create a filter using the query `repo:octocat/open-source-project-name reason:participating`. You can also add emojis with a native emoji keyboard. For a list of supported search queries, see "[Supported queries for custom filters](#supported-queries-for-custom-filters)." diff --git a/content/account-and-profile/setting-up-and-managing-your-github-profile/customizing-your-profile/managing-your-profile-readme.md b/content/account-and-profile/setting-up-and-managing-your-github-profile/customizing-your-profile/managing-your-profile-readme.md index e17f0801f24f..8a96e747d665 100644 --- a/content/account-and-profile/setting-up-and-managing-your-github-profile/customizing-your-profile/managing-your-profile-readme.md +++ b/content/account-and-profile/setting-up-and-managing-your-github-profile/customizing-your-profile/managing-your-profile-readme.md @@ -36,11 +36,8 @@ You can format text and include emoji, images, and GIFs in your profile README b * The repository contains a file named README.md in its root. * The README.md file contains any content. -{% note %} - -**Note**: If you created a public repository with the same name as your username before July 2020, {% data variables.product.prodname_dotcom %} won't automatically show the repository's README on your profile. You can manually share the repository's README to your profile by going to the repository on {% data variables.product.prodname_dotcom %} and clicking **Share to profile**. - -{% endnote %} +> [!NOTE] +> If you created a public repository with the same name as your username before July 2020, {% data variables.product.prodname_dotcom %} won't automatically show the repository's README on your profile. You can manually share the repository's README to your profile by going to the repository on {% data variables.product.prodname_dotcom %} and clicking **Share to profile**. ## Adding a profile README diff --git a/content/account-and-profile/setting-up-and-managing-your-github-profile/customizing-your-profile/personalizing-your-profile.md b/content/account-and-profile/setting-up-and-managing-your-github-profile/customizing-your-profile/personalizing-your-profile.md index 801b527f8075..68ef5505c27f 100644 --- a/content/account-and-profile/setting-up-and-managing-your-github-profile/customizing-your-profile/personalizing-your-profile.md +++ b/content/account-and-profile/setting-up-and-managing-your-github-profile/customizing-your-profile/personalizing-your-profile.md @@ -19,12 +19,8 @@ topics: shortTitle: Personalize --- -{% note %} - -**Note:** - Any details you add to your public {% data variables.product.product_name %} profile will be visible to all {% data variables.product.product_name %} users, including in regions where local laws, regulations, or cultural norms may pose risks to expressing your identity. We respect everyone’s decision about whether or not to share information about themselves on their {% data variables.product.product_name %} profile. - -{% endnote %} +> [!NOTE] +> Any details you add to your public {% data variables.product.product_name %} profile will be visible to all {% data variables.product.product_name %} users, including in regions where local laws, regulations, or cultural norms may pose risks to expressing your identity. We respect everyone’s decision about whether or not to share information about themselves on their {% data variables.product.product_name %} profile. ## Changing your profile picture @@ -32,14 +28,9 @@ Your profile picture helps identify you across {% data variables.product.product When you sign up for an account, {% data variables.product.product_name %} provides you with a randomly generated "identicon". [Your identicon](https://github.com/blog/1586-identicons) generates from a hash of your user ID, so there's no way to control its color or pattern. You can replace your identicon with an image that represents you. -{% note %} - -**Note{% ifversion ghec %}s{% endif %}**: {% ifversion ghec %} - -* {% endif %}Your profile picture should be a PNG, JPG, or GIF file, and it must be less than 1 MB in size and smaller than 3000 by 3000 pixels. For the best quality rendering, we recommend keeping the image at about 500 by 500 pixels. -{% ifversion ghec %}* Gravatar profile pictures are not supported with {% data variables.product.prodname_emus %}.{% endif %} - -{% endnote %} +> [!NOTE] {% ifversion ghec %} +> * {% endif %}Your profile picture should be a PNG, JPG, or GIF file, and it must be less than 1 MB in size and smaller than 3000 by 3000 pixels. For the best quality rendering, we recommend keeping the image at about 500 by 500 pixels. +{% ifversion ghec %}> * Gravatar profile pictures are not supported with {% data variables.product.prodname_emus %}.{% endif %} If you use Gravatar, and your Gravatar image is associated with the email you use for {% data variables.product.product_name %}, the image will be shown as your {% data variables.product.product_name %} profile picture by default (rather than an identicon). To change your {% data variables.product.product_name %} profile picture, you can either upload a new image to Gravatar, or upload a new image to {% data variables.product.product_name %} and override the Gravatar image. @@ -62,11 +53,10 @@ If you use Gravatar, and your Gravatar image is associated with the email you us You can change the name that is displayed on your profile. This name may also be displayed next to comments you make on private repositories owned by an organization. For more information, see "[AUTOTITLE](/organizations/managing-organization-settings/managing-the-display-of-member-names-in-your-organization)." {% ifversion fpt or ghec %} -{% note %} -**Note:** If you're a member of an {% data variables.enterprise.prodname_emu_enterprise %}, any changes to your profile name must be made through your identity provider instead of {% data variables.product.prodname_dotcom %}. {% data reusables.enterprise-accounts.emu-more-info-account %} +> [!NOTE] +> If you're a member of an {% data variables.enterprise.prodname_emu_enterprise %}, any changes to your profile name must be made through your identity provider instead of {% data variables.product.prodname_dotcom %}. {% data reusables.enterprise-accounts.emu-more-info-account %} -{% endnote %} {% endif %} {% data reusables.user-settings.access_settings %} @@ -78,21 +68,14 @@ Add a bio to your profile to share information about yourself with other {% data For a longer-form and more prominent way of displaying customized information about yourself, you can also use a profile README. For more information, see "[AUTOTITLE](/account-and-profile/setting-up-and-managing-your-github-profile/customizing-your-profile/managing-your-profile-readme)." -{% note %} - -**Note:** - If you have the activity overview section enabled for your profile and you @mention an organization you're a member of in your profile bio, then that organization will be featured first in your activity overview. For more information, see "[AUTOTITLE](/account-and-profile/setting-up-and-managing-your-github-profile/managing-contribution-settings-on-your-profile/showing-an-overview-of-your-activity-on-your-profile)." - -{% endnote %} +> [!NOTE] +> If you have the activity overview section enabled for your profile and you @mention an organization you're a member of in your profile bio, then that organization will be featured first in your activity overview. For more information, see "[AUTOTITLE](/account-and-profile/setting-up-and-managing-your-github-profile/managing-contribution-settings-on-your-profile/showing-an-overview-of-your-activity-on-your-profile)." {% data reusables.user-settings.access_settings %} 1. Under "Public profile", in the "Bio" field, type the content that you want displayed on your profile. The bio field is limited to 160 characters. - {% tip %} - - **Tip:** When you @mention an organization, only those that you're a member of will autocomplete. You can still @mention organizations that you're not a member of, like a previous employer, but the organization name won't autocomplete for you. - - {% endtip %} + > [!TIP] + > When you @mention an organization, only those that you're a member of will autocomplete. You can still @mention organizations that you're not a member of, like a previous employer, but the organization name won't autocomplete for you. {% data reusables.profile.update-profile %} @@ -210,11 +193,8 @@ Achievements celebrate specific events and actions that happen on {% data variab To stop private contributions from counting toward your Achievements, or to turn off Achievements entirely, see "[AUTOTITLE](/account-and-profile/setting-up-and-managing-your-github-profile/managing-contribution-settings-on-your-profile/showing-your-private-contributions-and-achievements-on-your-profile)." -{% note %} - -**Note:** This feature is currently in {% data variables.release-phases.public_preview %} and subject to change. - -{% endnote %} +> [!NOTE] +> This feature is currently in {% data variables.release-phases.public_preview %} and subject to change. {% endif %} diff --git a/content/account-and-profile/setting-up-and-managing-your-github-profile/customizing-your-profile/setting-your-profile-to-private.md b/content/account-and-profile/setting-up-and-managing-your-github-profile/customizing-your-profile/setting-your-profile-to-private.md index c1cdae1de1f2..a9a5f1beff1f 100644 --- a/content/account-and-profile/setting-up-and-managing-your-github-profile/customizing-your-profile/setting-your-profile-to-private.md +++ b/content/account-and-profile/setting-up-and-managing-your-github-profile/customizing-your-profile/setting-your-profile-to-private.md @@ -28,11 +28,8 @@ When your profile is private, the following content is hidden from your profile * Stars, projects, packages, and sponsoring tabs * Your pronouns -{% note %} - -**Note**: When your profile is private, some optional fields are still publicly visible, such as the README, biography, and profile photo. - -{% endnote %} +> [!NOTE] +> When your profile is private, some optional fields are still publicly visible, such as the README, biography, and profile photo. ## Changes to reporting on your activities @@ -45,11 +42,8 @@ When your profile is private, your {% data variables.product.prodname_dotcom %} * Site-wide search results * The [Trending](https://github.com/trending) page -{% note %} - -**Note**: Your activity on public repositories will still be publicly visible to anyone viewing those repositories, and some activity data may still be available through the {% data variables.product.prodname_dotcom %} API. - -{% endnote %} +> [!NOTE] +> Your activity on public repositories will still be publicly visible to anyone viewing those repositories, and some activity data may still be available through the {% data variables.product.prodname_dotcom %} API. ## Changing your profile's privacy settings diff --git a/content/account-and-profile/setting-up-and-managing-your-github-profile/managing-contribution-settings-on-your-profile/sharing-contributions-from-github-enterprise-server.md b/content/account-and-profile/setting-up-and-managing-your-github-profile/managing-contribution-settings-on-your-profile/sharing-contributions-from-github-enterprise-server.md index d211c45ecf4f..9df25ee93384 100644 --- a/content/account-and-profile/setting-up-and-managing-your-github-profile/managing-contribution-settings-on-your-profile/sharing-contributions-from-github-enterprise-server.md +++ b/content/account-and-profile/setting-up-and-managing-your-github-profile/managing-contribution-settings-on-your-profile/sharing-contributions-from-github-enterprise-server.md @@ -26,11 +26,8 @@ You can decide whether to show counts for private contributions on your profile. For more information about how contributions are calculated, see "[AUTOTITLE](/account-and-profile/setting-up-and-managing-your-github-profile/managing-contribution-settings-on-your-profile)." -{% note %} - -**Note:** The connection between your accounts is governed by [GitHub's Privacy Statement](/free-pro-team@latest/site-policy/privacy-policies/github-privacy-statement) and users enabling the connection must agree to the [GitHub Terms of Service](/free-pro-team@latest/site-policy/github-terms/github-terms-of-service). - -{% endnote %} +> [!NOTE] +> The connection between your accounts is governed by [GitHub's Privacy Statement](/free-pro-team@latest/site-policy/privacy-policies/github-privacy-statement) and users enabling the connection must agree to the [GitHub Terms of Service](/free-pro-team@latest/site-policy/github-terms/github-terms-of-service). ## Sending your enterprise contributions to your profile diff --git a/content/account-and-profile/setting-up-and-managing-your-github-profile/managing-contribution-settings-on-your-profile/viewing-contributions-on-your-profile.md b/content/account-and-profile/setting-up-and-managing-your-github-profile/managing-contribution-settings-on-your-profile/viewing-contributions-on-your-profile.md index 959208ad8918..7c4071fd714e 100644 --- a/content/account-and-profile/setting-up-and-managing-your-github-profile/managing-contribution-settings-on-your-profile/viewing-contributions-on-your-profile.md +++ b/content/account-and-profile/setting-up-and-managing-your-github-profile/managing-contribution-settings-on-your-profile/viewing-contributions-on-your-profile.md @@ -17,11 +17,8 @@ shortTitle: View contributions --- Your contribution graph and Achievements show activity from public repositories. You can choose to show activity from both public and private repositories, with specific details of your activity in private repositories anonymized. For more information, see "[AUTOTITLE](/account-and-profile/setting-up-and-managing-your-github-profile/managing-contribution-settings-on-your-profile/showing-your-private-contributions-and-achievements-on-your-profile)." -{% note %} - -**Note:** Commits will only appear on your contributions graph if the email address you used to author the commits is connected to your account on {% data variables.product.product_name %}. For more information, see "[AUTOTITLE](/account-and-profile/setting-up-and-managing-your-github-profile/managing-contribution-settings-on-your-profile/why-are-my-contributions-not-showing-up-on-my-profile#your-local-git-commit-email-isnt-connected-to-your-account)" - -{% endnote %} +> [!NOTE] +> Commits will only appear on your contributions graph if the email address you used to author the commits is connected to your account on {% data variables.product.product_name %}. For more information, see "[AUTOTITLE](/account-and-profile/setting-up-and-managing-your-github-profile/managing-contribution-settings-on-your-profile/why-are-my-contributions-not-showing-up-on-my-profile#your-local-git-commit-email-isnt-connected-to-your-account)" ## What counts as a contribution @@ -57,11 +54,8 @@ Your contributions calendar shows your contribution activity. * Click on a day's square to show the contributions made during that 24-hour period. * Press _Shift_ and click on another day's square to show contributions made during that time span. -{% note %} - -**Note:** You can select up to a one-month range on your contributions calendar. If you select a larger time span, we will only display one month of contributions. - -{% endnote %} +> [!NOTE] +> You can select up to a one-month range on your contributions calendar. If you select a larger time span, we will only display one month of contributions. ![Screenshot of the contributions graph on a user profile.](/assets/images/help/profile/contributions-graph.png) diff --git a/content/account-and-profile/setting-up-and-managing-your-github-profile/managing-contribution-settings-on-your-profile/why-are-my-contributions-not-showing-up-on-my-profile.md b/content/account-and-profile/setting-up-and-managing-your-github-profile/managing-contribution-settings-on-your-profile/why-are-my-contributions-not-showing-up-on-my-profile.md index 0011608f4bf5..d7491b92641d 100644 --- a/content/account-and-profile/setting-up-and-managing-your-github-profile/managing-contribution-settings-on-your-profile/why-are-my-contributions-not-showing-up-on-my-profile.md +++ b/content/account-and-profile/setting-up-and-managing-your-github-profile/managing-contribution-settings-on-your-profile/why-are-my-contributions-not-showing-up-on-my-profile.md @@ -73,11 +73,10 @@ The email address in the `From:` field is the address that was set in the [local If the email address used for the commit is not connected to your account on {% data variables.product.prodname_dotcom %}, you must [add the email address](/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/adding-an-email-address-to-your-github-account) to your account on {% data variables.product.prodname_dotcom %}. Your contributions graph will be rebuilt automatically when you add the new address. {% ifversion fpt or ghec %} -{% note %} -**Note**: If you use a {% data variables.enterprise.prodname_managed_user %}, you cannot add additional email addresses to the account, even if multiple email addresses are registered with your identity provider (IdP). Therefore, only commits that are authored by the primary email address registered with your IdP can be associated with your {% data variables.enterprise.prodname_managed_user %}. +> [!NOTE] +> If you use a {% data variables.enterprise.prodname_managed_user %}, you cannot add additional email addresses to the account, even if multiple email addresses are registered with your identity provider (IdP). Therefore, only commits that are authored by the primary email address registered with your IdP can be associated with your {% data variables.enterprise.prodname_managed_user %}. -{% endnote %} {% endif %} Generic email addresses, such as `jane@computer.local`, cannot be added to {% data variables.product.prodname_dotcom %} accounts and linked to commits. If you've authored any commits using a generic email address, the commits will not be linked to your {% data variables.product.prodname_dotcom %} profile and will not show up in your contribution graph. @@ -90,11 +89,8 @@ If your commits are in a non-default or non-`gh-pages` branch and you'd like the * [Open a pull request](/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request) to have your changes merged into the default branch or the `gh-pages` branch. * [Change the default branch](/repositories/configuring-branches-and-merges-in-your-repository/managing-branches-in-your-repository/changing-the-default-branch) of the repository. -{% warning %} - -**Warning**: Changing the default branch of the repository will change it for all repository collaborators. Only do this if you want the new branch to become the base against which all future pull requests and commits will be made. - -{% endwarning %} +> [!WARNING] +> Changing the default branch of the repository will change it for all repository collaborators. Only do this if you want the new branch to become the base against which all future pull requests and commits will be made. ### Commit was made in a fork diff --git a/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/adding-an-email-address-to-your-github-account.md b/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/adding-an-email-address-to-your-github-account.md index 8d503773cdc2..37583e5b5544 100644 --- a/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/adding-an-email-address-to-your-github-account.md +++ b/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/adding-an-email-address-to-your-github-account.md @@ -17,13 +17,9 @@ shortTitle: Add an email address --- {% ifversion fpt or ghec %} -{% note %} - -**Notes**: -* {% data reusables.user-settings.no-verification-disposable-emails %} -* If you're a member of an {% data variables.enterprise.prodname_emu_enterprise %}, you cannot make changes to your email address on {% data variables.product.prodname_dotcom %}. {% data reusables.enterprise-accounts.emu-more-info-account %} - -{% endnote %} +> [!NOTE] +> * {% data reusables.user-settings.no-verification-disposable-emails %} +> * If you're a member of an {% data variables.enterprise.prodname_emu_enterprise %}, you cannot make changes to your email address on {% data variables.product.prodname_dotcom %}. {% data reusables.enterprise-accounts.emu-more-info-account %} {% endif %} diff --git a/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/remembering-your-github-username-or-email.md b/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/remembering-your-github-username-or-email.md index e9a9f6eb8cfe..059b34073d36 100644 --- a/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/remembering-your-github-username-or-email.md +++ b/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/remembering-your-github-username-or-email.md @@ -53,11 +53,8 @@ YOUR-USERNAME If you have any local copies of personal repositories you have created or forked, you can check the URL of the remote repository. -{% tip %} - -**Tip**: This method only works if you have an original repository or your own fork of someone else's repository. If you clone someone else's repository, their username will show instead of yours. Similarly, organization repositories will show the name of the organization instead of a particular user in the remote URL. - -{% endtip %} +> [!TIP] +> This method only works if you have an original repository or your own fork of someone else's repository. If you clone someone else's repository, their username will show instead of yours. Similarly, organization repositories will show the name of the organization instead of a particular user in the remote URL. ```shell $ cd YOUR-REPOSITORY diff --git a/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/setting-your-commit-email-address.md b/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/setting-your-commit-email-address.md index 8b5fa437257c..769d560805ad 100644 --- a/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/setting-your-commit-email-address.md +++ b/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/setting-your-commit-email-address.md @@ -32,11 +32,8 @@ For web-based Git operations, you can set your commit email address on {% data v {% ifversion fpt or ghec %} -{% note %} - -**Note**: {% data reusables.user-settings.no-verification-disposable-emails %} - -{% endnote %} +> [!NOTE] +> {% data reusables.user-settings.no-verification-disposable-emails %} {% endif %} @@ -50,11 +47,8 @@ To ensure that commits are attributed to you and appear in your contributions gr
-{% note %} - -**Note:** If you created your account _after_ July 18, 2017, your `noreply` email address for is an ID number and your username in the form of ID+USERNAME@users.noreply.github.com. If you created your account _prior to_ July 18, 2017, and enabled **Keep my email address private** prior to that date, your `noreply` email address is USERNAME@users.noreply.github.com. You can get an ID-based `noreply` email address by selecting (or deselecting and reselecting) **Keep my email address private** in your email settings. - -{% endnote %} +> [!NOTE] +> If you created your account _after_ July 18, 2017, your `noreply` email address for is an ID number and your username in the form of ID+USERNAME@users.noreply.github.com. If you created your account _prior to_ July 18, 2017, and enabled **Keep my email address private** prior to that date, your `noreply` email address is USERNAME@users.noreply.github.com. You can get an ID-based `noreply` email address by selecting (or deselecting and reselecting) **Keep my email address private** in your email settings. If you use your `noreply` email address for {% data variables.product.github %} to make commits and then change your username, those commits will not be associated with your account. This does not apply if you're using the ID-based `noreply` address from {% data variables.product.github %}. For more information, see "[AUTOTITLE](/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-user-account-settings/changing-your-github-username)."{% endif %} diff --git a/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/verifying-your-email-address.md b/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/verifying-your-email-address.md index 81b99bcc4a84..02210f1025f2 100644 --- a/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/verifying-your-email-address.md +++ b/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/verifying-your-email-address.md @@ -34,14 +34,9 @@ If you do not verify your email address, you will not be able to: * Sponsor developers with {% data variables.product.prodname_sponsors %} * Accept organization invitations -{% warning %} - -**Warnings**: - -* {% data reusables.user-settings.no-verification-disposable-emails %} -* {% data reusables.user-settings.verify-org-approved-email-domain %} - -{% endwarning %} +> [!WARNING] +> * {% data reusables.user-settings.no-verification-disposable-emails %} +> * {% data reusables.user-settings.verify-org-approved-email-domain %} {% ifversion ghec %} diff --git a/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-user-account-settings/about-your-personal-dashboard.md b/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-user-account-settings/about-your-personal-dashboard.md index d843ce3deb24..612a59de71dd 100644 --- a/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-user-account-settings/about-your-personal-dashboard.md +++ b/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-user-account-settings/about-your-personal-dashboard.md @@ -49,11 +49,8 @@ You can also find a list of your recently visited repositories, teams, and proje {% ifversion feed %} -{% note %} - -**Note:** The new feed is currently in {% data variables.release-phases.public_preview %} and subject to change. - -{% endnote %} +> [!NOTE] +> The new feed is currently in {% data variables.release-phases.public_preview %} and subject to change. The feed is designed to help you discover relevant content from projects you follow, keep up with your friends and community members, and track recent activity in your communities. @@ -89,11 +86,8 @@ For more information about following people and starring repositories, see "[AUT ### For you feed -{% note %} - -**Note:** This new tab is currently in {% data variables.release-phases.public_preview %} and subject to change. - -{% endnote %} +> [!NOTE] +> This new tab is currently in {% data variables.release-phases.public_preview %} and subject to change. This feed shows activity and recommendations based on your network on {% data variables.product.product_name %}. It's designed to provide updates that inspire you, keep you up-to-date, and help you find new communities you want to participate in. Your network includes: diff --git a/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-user-account-settings/changing-your-github-username.md b/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-user-account-settings/changing-your-github-username.md index f94371b6948d..9d75b2c01dde 100644 --- a/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-user-account-settings/changing-your-github-username.md +++ b/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-user-account-settings/changing-your-github-username.md @@ -22,20 +22,18 @@ shortTitle: Change your username {% ifversion ghec or ghes %} -{% note %} - {% ifversion ghec %} -**Note**: Members of an {% data variables.enterprise.prodname_emu_enterprise %} cannot change usernames. Your enterprise's IdP administrator controls your username for {% data variables.product.product_name %}. For more information, see "[AUTOTITLE](/admin/identity-and-access-management/using-enterprise-managed-users-for-iam/about-enterprise-managed-users)." +> [!NOTE] +> Members of an {% data variables.enterprise.prodname_emu_enterprise %} cannot change usernames. Your enterprise's IdP administrator controls your username for {% data variables.product.product_name %}. For more information, see "[AUTOTITLE](/admin/identity-and-access-management/using-enterprise-managed-users-for-iam/about-enterprise-managed-users)." {% elsif ghes %} -**Note**: If you sign into {% data variables.location.product_location %} with LDAP credentials or single sign-on (SSO), only your local administrator can change your username. For more information about authentication methods for {% data variables.product.product_name %}, see "[AUTOTITLE](/admin/identity-and-access-management/managing-iam-for-your-enterprise)." +> [!NOTE] +> If you sign into {% data variables.location.product_location %} with LDAP credentials or single sign-on (SSO), only your local administrator can change your username. For more information about authentication methods for {% data variables.product.product_name %}, see "[AUTOTITLE](/admin/identity-and-access-management/managing-iam-for-your-enterprise)." {% endif %} -{% endnote %} - {% endif %} ## About username changes @@ -88,14 +86,9 @@ If your Git commits are associated with another email address you've added to yo {% ifversion fpt or ghec %}If you've been using a {% data variables.product.prodname_dotcom %}-provided private commit email address, whether or not your commit history will be retained after an account rename depends on the format of the email address. Git commits that are associated with your {% data variables.product.product_name %}-provided `noreply` email address won't be attributed to your new username and won't appear in your contributions graph, unless your `noreply` email address is in the form of `ID+USERNAME@users.noreply.github.com`. Older versions of the `noreply` email address that do not contain a numeric ID will not be associated with your {% data variables.product.prodname_dotcom %} account after changing your username.{% endif %} -{% warning %} - -**Warnings:** - -* After a username change, verified commits signed using the previous {% data variables.product.product_name %}-provided `noreply` email address will lose their "Verified" status. -* When verifying a signature, {% data variables.product.product_name %} checks that the email address of the committer or tagger exactly matches one of the email addresses associated with the GPG key's identities. Additionally, {% data variables.product.product_name %} confirms that the email address is verified and linked to the user's account. This ensures that the key belongs to you and that you created the commit or tag. Because the username of the `noreply` email address changes, these commits can no longer be verified. - -{% endwarning %} +> [!WARNING] +> * After a username change, verified commits signed using the previous {% data variables.product.product_name %}-provided `noreply` email address will lose their "Verified" status. +> * When verifying a signature, {% data variables.product.product_name %} checks that the email address of the committer or tagger exactly matches one of the email addresses associated with the GPG key's identities. Additionally, {% data variables.product.product_name %} confirms that the email address is verified and linked to the user's account. This ensures that the key belongs to you and that you created the commit or tag. Because the username of the `noreply` email address changes, these commits can no longer be verified. ## Your gists diff --git a/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-user-account-settings/permission-levels-for-a-personal-account-repository.md b/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-user-account-settings/permission-levels-for-a-personal-account-repository.md index edaf16ae7f37..423d364e689c 100644 --- a/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-user-account-settings/permission-levels-for-a-personal-account-repository.md +++ b/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-user-account-settings/permission-levels-for-a-personal-account-repository.md @@ -21,11 +21,8 @@ Repositories owned by personal accounts have one owner. Ownership permissions ca You can also {% ifversion fpt or ghec %}invite{% else %}add{% endif %} users on {% data variables.product.product_name %} to your repository as collaborators. For more information, see "[AUTOTITLE](/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-access-to-your-personal-repositories/inviting-collaborators-to-a-personal-repository)." -{% tip %} - -**Tip:** If you require more granular access to a repository owned by your personal account, consider transferring the repository to an organization. For more information, see "[AUTOTITLE](/repositories/creating-and-managing-repositories/transferring-a-repository#transferring-a-repository-owned-by-your-personal-account)." - -{% endtip %} +> [!TIP] +> If you require more granular access to a repository owned by your personal account, consider transferring the repository to an organization. For more information, see "[AUTOTITLE](/repositories/creating-and-managing-repositories/transferring-a-repository#transferring-a-repository-owned-by-your-personal-account)." ## Owner access for a repository owned by a personal account @@ -70,11 +67,8 @@ The repository owner has full control of the repository. In addition to the acti Collaborators on a personal repository can pull (read) the contents of the repository and push (write) changes to the repository. -{% note %} - -**Note:** In a private repository, repository owners can only grant write access to collaborators. Collaborators can't have read-only access to repositories owned by a personal account. - -{% endnote %} +> [!NOTE] +> In a private repository, repository owners can only grant write access to collaborators. Collaborators can't have read-only access to repositories owned by a personal account. Collaborators can also perform the following actions. diff --git a/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-membership-in-organizations/about-organization-membership.md b/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-membership-in-organizations/about-organization-membership.md index 44cd00623d2d..1c4d03954c07 100644 --- a/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-membership-in-organizations/about-organization-membership.md +++ b/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-membership-in-organizations/about-organization-membership.md @@ -30,11 +30,8 @@ When you accept an invitation to join an organization, the organization owners m For more information, see the [{% data variables.product.prodname_dotcom %} Privacy Statement](/free-pro-team@latest/site-policy/privacy-policies/github-privacy-statement). - {% note %} - - **Note:** Owners are not able to view member IP addresses in the organization's audit log. In the event of a security incident, such as an account compromise or inadvertent sharing of sensitive data, organization owners may request details of access to private repositories. The information we return may include your IP address. - - {% endnote %} +> [!NOTE] +> Owners are not able to view member IP addresses in the organization's audit log. In the event of a security incident, such as an account compromise or inadvertent sharing of sensitive data, organization owners may request details of access to private repositories. The information we return may include your IP address. By default, your organization membership visibility is set to private. You can choose to publicize individual organization memberships on your profile. For more information, see "[AUTOTITLE](/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-membership-in-organizations/publicizing-or-hiding-organization-membership)." diff --git a/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-membership-in-organizations/accessing-an-organization.md b/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-membership-in-organizations/accessing-an-organization.md index deed03c4481a..0091b07b3459 100644 --- a/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-membership-in-organizations/accessing-an-organization.md +++ b/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-membership-in-organizations/accessing-an-organization.md @@ -16,11 +16,9 @@ versions: topics: - Accounts --- -{% tip %} -**Tip:** Only organization owners can see and change the account settings for an organization. - -{% endtip %} +> [!TIP] +> Only organization owners can see and change the account settings for an organization. {% data reusables.profile.access_org %} {% data reusables.user-settings.access_org %} diff --git a/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-membership-in-organizations/removing-yourself-from-an-organization.md b/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-membership-in-organizations/removing-yourself-from-an-organization.md index d0a30f7ce223..adef359a21c3 100644 --- a/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-membership-in-organizations/removing-yourself-from-an-organization.md +++ b/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-membership-in-organizations/removing-yourself-from-an-organization.md @@ -17,13 +17,10 @@ shortTitle: Leave an organization --- {% ifversion fpt or ghec %} -{% warning %} - -**Warning:** If you're currently responsible for paying for {% data variables.product.product_name %} in your organization, removing yourself from the organization **does not** update the billing information on file for the organization. If you are currently responsible for billing, **you must** have another owner or billing manager for the organization [update the organization's payment method](/billing/managing-your-github-billing-settings/adding-or-editing-a-payment-method). - -For more information, see "[AUTOTITLE](/organizations/managing-organization-settings/transferring-organization-ownership)." - -{% endwarning %} +> [!WARNING] +> If you're currently responsible for paying for {% data variables.product.product_name %} in your organization, removing yourself from the organization **does not** update the billing information on file for the organization. If you are currently responsible for billing, **you must** have another owner or billing manager for the organization [update the organization's payment method](/billing/managing-your-github-billing-settings/adding-or-editing-a-payment-method). +> +> For more information, see "[AUTOTITLE](/organizations/managing-organization-settings/transferring-organization-ownership)." {% endif %} diff --git a/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-membership-in-organizations/viewing-peoples-roles-in-an-organization.md b/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-membership-in-organizations/viewing-peoples-roles-in-an-organization.md index 05f4868f618f..e5bc96043555 100644 --- a/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-membership-in-organizations/viewing-peoples-roles-in-an-organization.md +++ b/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-membership-in-organizations/viewing-peoples-roles-in-an-organization.md @@ -40,11 +40,8 @@ If your organization is managed by an enterprise account, then you can view the You can also view whether an enterprise owner has a specific role in the organization. Enterprise owners can also be an organization member, any other organization role, or be un-affiliated with the organization. -{% note %} - -**Note:** If you're an organization owner, you can also invite an enterprise owner to have a role in the organization. If an enterprise owner accepts the invitation, a seat or license in the organization is used from the available licenses for your enterprise. For more information about how licensing works, see "[AUTOTITLE](/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/roles-in-an-enterprise#enterprise-owner)." - -{% endnote %} +> [!NOTE] +> If you're an organization owner, you can also invite an enterprise owner to have a role in the organization. If an enterprise owner accepts the invitation, a seat or license in the organization is used from the available licenses for your enterprise. For more information about how licensing works, see "[AUTOTITLE](/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/roles-in-an-enterprise#enterprise-owner)." | **Enterprise role** | **Organization role** | **Organization access or impact** | |----|----|----| diff --git a/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-personal-account/converting-a-user-into-an-organization.md b/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-personal-account/converting-a-user-into-an-organization.md index 37729bcdd19c..91125262b38f 100644 --- a/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-personal-account/converting-a-user-into-an-organization.md +++ b/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-personal-account/converting-a-user-into-an-organization.md @@ -17,24 +17,23 @@ topics: - Accounts shortTitle: User into an organization --- -{% warning %} -**Warning**: Before converting a user into an organization, keep these points in mind. - -* You will **no longer** be able to sign into the converted personal account. -* You will **no longer** be able to create or modify gists owned by the converted personal account. -* An organization **cannot** be converted back to a user. -* The SSH keys, OAuth tokens, job profile, reactions, and associated user information, **will not** be transferred to the organization. This is only true for the personal account that's being converted, not any of the personal account's collaborators. -* Any {% data variables.product.prodname_github_apps %} installed on the converted personal account will be uninstalled. -* Any commits made with the converted personal account **will no longer be linked** to that account. The commits themselves **will** remain intact. -* Any existing comments made by the converted personal account **will no longer be linked** to that account. The comments themselves **will** remain intact, but will be associated with the `ghost` user. -* Any forks of private repositories made with the converted personal account will be deleted. -* Since organizations cannot star repositories, you will no longer have access to your original list of starred repositories. -* You will no longer have access to the list of users you were following from your user account. -* Any followers of your user account will not automatically follow the new organization. -{% ifversion projects-v2 %}- Any existing collaborators on your projects will still have access to those projects in the new organization.{% endif %} -* {% data variables.product.prodname_actions %} is not automatically enabled on the account after converting it to an organization, and will have to be re-enabled. To re-enable {% data variables.product.prodname_actions %}, create a new workflow file in the `.github/workflows` directory of your repository. -{% endwarning %} +> [!WARNING] +> Before converting a user into an organization, keep these points in mind. +> +> * You will **no longer** be able to sign into the converted personal account. +> * You will **no longer** be able to create or modify gists owned by the converted personal account. +> * An organization **cannot** be converted back to a user. +> * The SSH keys, OAuth tokens, job profile, reactions, and associated user information, **will not** be transferred to the organization. This is only true for the personal account that's being converted, not any of the personal account's collaborators. +> * Any {% data variables.product.prodname_github_apps %} installed on the converted personal account will be uninstalled. +> * Any commits made with the converted personal account **will no longer be linked** to that account. The commits themselves **will** remain intact. +> * Any existing comments made by the converted personal account **will no longer be linked** to that account. The comments themselves **will** remain intact, but will be associated with the `ghost` user. +> * Any forks of private repositories made with the converted personal account will be deleted. +> * Since organizations cannot star repositories, you will no longer have access to your original list of starred repositories. +> * You will no longer have access to the list of users you were following from your user account. +> * Any followers of your user account will not automatically follow the new organization. +{% ifversion projects-v2 %}> * Any existing collaborators on your projects will still have access to those projects in the new organization.{% endif %} +> * {% data variables.product.prodname_actions %} is not automatically enabled on the account after converting it to an organization, and will have to be re-enabled. To re-enable {% data variables.product.prodname_actions %}, create a new workflow file in the `.github/workflows` directory of your repository. ## Prerequisites diff --git a/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-personal-account/deleting-your-personal-account.md b/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-personal-account/deleting-your-personal-account.md index c8942c35eef3..7ff1d42e7316 100644 --- a/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-personal-account/deleting-your-personal-account.md +++ b/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-personal-account/deleting-your-personal-account.md @@ -31,11 +31,8 @@ Deleting your personal account removes all repositories, forks of private reposi {% ifversion ghec %} -{% note %} - -**Note**: If your enterprise manages your account and you sign into {% data variables.product.github %} through your company's identity provider (IdP), you cannot delete your account. For more information, see "[AUTOTITLE](/admin/identity-and-access-management/using-enterprise-managed-users-for-iam/about-enterprise-managed-users)." - -{% endnote %} +> [!NOTE] +> If your enterprise manages your account and you sign into {% data variables.product.github %} through your company's identity provider (IdP), you cannot delete your account. For more information, see "[AUTOTITLE](/admin/identity-and-access-management/using-enterprise-managed-users-for-iam/about-enterprise-managed-users)." {% endif %} @@ -58,11 +55,8 @@ For more information, see the following articles. Before you delete your personal account, make a copy of all repositories, private forks, wikis, issues, and pull requests owned by your account. For more information, see "[AUTOTITLE](/repositories/archiving-a-github-repository/backing-up-a-repository)." -{% warning %} - -**Warning:** Once your personal account has been deleted, {% ifversion fpt or ghec %}{% data variables.product.company_short %}{% elsif ghes %}an enterprise owner{% endif %} cannot restore your content. - -{% endwarning %} +> [!WARNING] +> Once your personal account has been deleted, {% ifversion fpt or ghec %}{% data variables.product.company_short %}{% elsif ghes %}an enterprise owner{% endif %} cannot restore your content. ## Deleting your personal account diff --git a/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-personal-account/managing-multiple-accounts.md b/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-personal-account/managing-multiple-accounts.md index 29a0aaa291e4..16ded05d0c51 100644 --- a/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-personal-account/managing-multiple-accounts.md +++ b/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-personal-account/managing-multiple-accounts.md @@ -24,11 +24,8 @@ If you need to use multiple accounts, you can stay signed in to your accounts an If you want to use one workstation to contribute from both accounts, you can simplify contribution with Git by using a mixture of protocols to access repository data, or by using credentials on a per-repository basis. -{% warning %} - -**Warning**: Be mindful when you use one workstation to contribute to two separate accounts. Management of two or more accounts can increase the chance of mistakenly leaking internal code to the public. - -{% endwarning %} +> [!WARNING] +> Be mindful when you use one workstation to contribute to two separate accounts. Management of two or more accounts can increase the chance of mistakenly leaking internal code to the public. If you aren't required to use a {% data variables.enterprise.prodname_managed_user %}, {% data variables.product.company_short %} recommends that you use one personal account for all your work on {% data variables.location.product_location %}. With a single personal account, you can contribute to a combination of personal, open source, or professional projects using one identity. Other people can invite the account to contribute to both individual repositories and repositories owned by an organization, and the account can be a member of multiple organizations or enterprises. diff --git a/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-personal-account/merging-multiple-personal-accounts.md b/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-personal-account/merging-multiple-personal-accounts.md index 3c4f3b9cb89f..f460aa00995b 100644 --- a/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-personal-account/merging-multiple-personal-accounts.md +++ b/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-personal-account/merging-multiple-personal-accounts.md @@ -16,29 +16,23 @@ topics: - Accounts shortTitle: Merge multiple accounts --- -{% tip %} +> [!TIP] {% ifversion ghec %} -**Tip:** {% data variables.product.prodname_emus %} allow an enterprise to provision unique personal accounts for its members through an identity provider (IdP). For more information, see "[AUTOTITLE](/admin/identity-and-access-management/using-enterprise-managed-users-for-iam/about-enterprise-managed-users)." For other use cases, we recommend using only one personal account to manage both personal and professional repositories. +> {% data variables.product.prodname_emus %} allow an enterprise to provision unique personal accounts for its members through an identity provider (IdP). For more information, see "[AUTOTITLE](/admin/identity-and-access-management/using-enterprise-managed-users-for-iam/about-enterprise-managed-users)." For other use cases, we recommend using only one personal account to manage both personal and professional repositories. {% else %} -**Tip:** We recommend using only one personal account to manage both personal and professional repositories. +> We recommend using only one personal account to manage both personal and professional repositories. {% endif %} -{% endtip %} - -{% warning %} - -**Warning:** -* Organization and repository access permissions aren't transferable between accounts. If the account you want to delete has an existing access permission, an organization owner or repository administrator will need to invite the account that you want to keep. -* Any commits authored with a {% data variables.product.company_short %}-provided `noreply` email address cannot be transferred from one account to another. If the account you want to delete used the **Keep my email address private** option, it won't be possible to transfer the commits authored by the account you are deleting to the account you want to keep. -* Issues, pull requests, and discussions will not be attributed to the new account. -* Achievements are not able to be transferred between accounts. - -{% endwarning %} +> [!WARNING] +> * Organization and repository access permissions aren't transferable between accounts. If the account you want to delete has an existing access permission, an organization owner or repository administrator will need to invite the account that you want to keep. +> * Any commits authored with a {% data variables.product.company_short %}-provided `noreply` email address cannot be transferred from one account to another. If the account you want to delete used the **Keep my email address private** option, it won't be possible to transfer the commits authored by the account you are deleting to the account you want to keep. +> * Issues, pull requests, and discussions will not be attributed to the new account. +> * Achievements are not able to be transferred between accounts. 1. [Transfer any repositories](/repositories/creating-and-managing-repositories/transferring-a-repository) from the account you want to delete to the account you want to keep. Issues, pull requests, and wikis are transferred as well. Verify the repositories exist on the account you want to keep. 1. [Update the remote URLs](/get-started/getting-started-with-git/managing-remote-repositories) in any local clones of the repositories that were moved. diff --git a/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-personal-account/unlinking-your-email-address-from-a-locked-account.md b/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-personal-account/unlinking-your-email-address-from-a-locked-account.md index e1ecad1ed377..c23a5ec71124 100644 --- a/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-personal-account/unlinking-your-email-address-from-a-locked-account.md +++ b/content/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-personal-account/unlinking-your-email-address-from-a-locked-account.md @@ -12,25 +12,16 @@ topics: shortTitle: Unlink your email --- -{% note %} - -**Notes:** - -* Following these steps will not disable 2FA or provide access to a locked account, but will instead unlink the associated email address so it may be used for a different account. If you cannot regain access to the 2FA locked account, these steps will permanently break the link between the account and the linked email address. Before continuing with this article, be sure you have lost all access to your account. For more information, see "[AUTOTITLE](/authentication/securing-your-account-with-two-factor-authentication-2fa/recovering-your-account-if-you-lose-your-2fa-credentials)." - -* If you recover access to your locked account, you can re-link an unlinked email address. For more information, see "[AUTOTITLE](/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/adding-an-email-address-to-your-github-account)." - -{% endnote %} +> [!NOTE] +> * Following these steps will not disable 2FA or provide access to a locked account, but will instead unlink the associated email address so it may be used for a different account. If you cannot regain access to the 2FA locked account, these steps will permanently break the link between the account and the linked email address. Before continuing with this article, be sure you have lost all access to your account. For more information, see "[AUTOTITLE](/authentication/securing-your-account-with-two-factor-authentication-2fa/recovering-your-account-if-you-lose-your-2fa-credentials)." +> * If you recover access to your locked account, you can re-link an unlinked email address. For more information, see "[AUTOTITLE](/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/adding-an-email-address-to-your-github-account)." ## About unlinking your email address Since an email address can only be associated with a single {% data variables.product.prodname_dotcom %} account, when you've lost your 2FA credentials and are unable to recover access, unlinking your email address from the locked account allows you to link that email address to a new or existing account. Additionally, linking a previously used commit email address to a new account will connect your commit history to that account. Unless you have chosen to keep your email address private, your account's commit email address is the same as your account's primary email address. For more information, see "[AUTOTITLE](/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/setting-your-commit-email-address)." Be aware that nothing else associated with your 2FA locked account, including your repositories, permissions, and profile, will transfer to your new account. -{% note %} - -**Note:** Backup email addresses are not associated with your commits. Unlinking a backup email address and linking the email address to a different account will not connect your commit history to that account. - -{% endnote %} +> [!NOTE] +> Backup email addresses are not associated with your commits. Unlinking a backup email address and linking the email address to a different account will not connect your commit history to that account. ## Unlinking your email address @@ -47,10 +38,7 @@ Since an email address can only be associated with a single {% data variables.pr 1. To verify your identity, type the one-time password from your email in the "One-time password" text field, then click **Verify email address**. {% data reusables.accounts.unlinking-email-address %} - {% note %} - - **Note:** You can also link your unlinked email to an existing {% data variables.product.prodname_dotcom %} account. For more information, see "[AUTOTITLE](/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/adding-an-email-address-to-your-github-account)." - - {% endnote %} + > [!NOTE] + > You can also link your unlinked email to an existing {% data variables.product.prodname_dotcom %} account. For more information, see "[AUTOTITLE](/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/adding-an-email-address-to-your-github-account)." 1. Optionally, if you have any form of payment set up on the locked account, please contact us through the {% data variables.contact.contact_support_portal %} to cancel future payments. For example, you might have a paid subscription or sponsor developers through {% data variables.product.prodname_sponsors %}. If you are sponsored through {% data variables.product.prodname_sponsors %}, please mention this so that the team can help you migrate your sponsorships. diff --git a/content/actions/administering-github-actions/usage-limits-billing-and-administration.md b/content/actions/administering-github-actions/usage-limits-billing-and-administration.md index 99d5efdb4d96..fe7c4c29cf41 100644 --- a/content/actions/administering-github-actions/usage-limits-billing-and-administration.md +++ b/content/actions/administering-github-actions/usage-limits-billing-and-administration.md @@ -39,11 +39,8 @@ GitHub Actions usage is free for {% data variables.product.prodname_ghe_server % {% ifversion fpt or ghec %} There are some limits on {% data variables.product.prodname_actions %} usage when using {% data variables.product.prodname_dotcom %}-hosted runners. These limits are subject to change. -{% note %} - -**Note:** For self-hosted runners, different usage limits apply. For more information, see "[AUTOTITLE](/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#usage-limits)." - -{% endnote %} +> [!NOTE] +> For self-hosted runners, different usage limits apply. For more information, see "[AUTOTITLE](/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#usage-limits)." * **Job execution time** - Each job in a workflow can run for up to 6 hours of execution time. If a job reaches this limit, the job is terminated and fails to complete. {% data reusables.actions.usage-workflow-run-time %} @@ -67,14 +64,9 @@ There are some limits on {% data variables.product.prodname_actions %} usage whe | Team | 1000 | 5 | 100 | | Enterprise | 1000 | 50 | 100 | - {% note %} - - **Notes:** - - * If required, customers on enterprise plans can request a higher limit for concurrent jobs. For more information, contact us through the {% data variables.contact.contact_support_portal %}, or contact your sales representative. - * The maximum concurrent macOS jobs is shared across standard {% data variables.product.prodname_dotcom %}-hosted runner and {% data variables.product.prodname_dotcom %}-hosted {% data variables.actions.hosted_runner %}s. - - {% endnote %} + > [!NOTE] + > * If required, customers on enterprise plans can request a higher limit for concurrent jobs. For more information, contact us through the {% data variables.contact.contact_support_portal %}, or contact your sales representative. + > * The maximum concurrent macOS jobs is shared across standard {% data variables.product.prodname_dotcom %}-hosted runner and {% data variables.product.prodname_dotcom %}-hosted {% data variables.actions.hosted_runner %}s. * **Job matrix** - {% data reusables.actions.usage-matrix-limits %} {% data reusables.actions.usage-workflow-queue-limits %} diff --git a/content/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/about-actions-runner-controller.md b/content/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/about-actions-runner-controller.md index a3a62902630d..49918323a06b 100644 --- a/content/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/about-actions-runner-controller.md +++ b/content/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/about-actions-runner-controller.md @@ -20,11 +20,8 @@ topics: The following diagram illustrates the architecture of ARC's autoscaling runner scaleset mode. -{% note %} - -**Note:** To view the following diagram in a larger size, see the [Autoscaling Runner Scale Sets mode](https://github.com/actions/actions-runner-controller/blob/master/docs/gha-runner-scale-set-controller/README.md#how-it-works) documentation in the Actions Runner Controller repository. - -{% endnote %} +> [!NOTE] +> To view the following diagram in a larger size, see the [Autoscaling Runner Scale Sets mode](https://github.com/actions/actions-runner-controller/blob/master/docs/gha-runner-scale-set-controller/README.md#how-it-works) documentation in the Actions Runner Controller repository. ![Diagram showing ARC's autoscaling runner ScaleSet mode.](/assets/images/help/actions/arc-diagram.png) @@ -51,11 +48,8 @@ Each resource that is deployed by ARC is given a name composed of: * an installation name, which is the installation name you specify when you install the Helm chart. * a resource identification suffix, which is a string that identifies the resource type. This value is not configurable. -{% note %} - -**Note:** Different versions of Kubernetes have different length limits for names of resources. The length limit for the resource name is calculated by adding the length of the installation name and the length of the resource identification suffix. If the resource name is longer than the reserved length, you will receive an error. - -{% endnote %} +> [!NOTE] +> Different versions of Kubernetes have different length limits for names of resources. The length limit for the resource name is calculated by adding the length of the installation name and the length of the resource identification suffix. If the resource name is longer than the reserved length, you will receive an error. ### Resources deployed by `gha-runner-scale-set-controller` diff --git a/content/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/about-support-for-actions-runner-controller.md b/content/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/about-support-for-actions-runner-controller.md index 1d2c843d76be..77e60ceba2ee 100644 --- a/content/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/about-support-for-actions-runner-controller.md +++ b/content/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/about-support-for-actions-runner-controller.md @@ -43,14 +43,9 @@ If you're uncertain if the issue is out of scope, open a ticket and we're happy For more information about contacting {% data variables.contact.github_support %}, see [AUTOTITLE](/support/contacting-github-support). -{% note %} - -**Note:** - -* OpenShift clusters are currently unsupported. -* ARC is only supported on GitHub Enterprise Server versions 3.9 and greater. - -{% endnote %} +> [!NOTE] +> * OpenShift clusters are currently unsupported. +> * ARC is only supported on GitHub Enterprise Server versions 3.9 and greater. ## Working with {% data variables.contact.github_support %} for Actions Runner Controller diff --git a/content/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/authenticating-to-the-github-api.md b/content/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/authenticating-to-the-github-api.md index c7aafde34363..511de0b2ba39 100644 --- a/content/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/authenticating-to-the-github-api.md +++ b/content/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/authenticating-to-the-github-api.md @@ -18,11 +18,8 @@ defaultPlatform: linux You can authenticate {% data variables.product.prodname_actions_runner_controller %} (ARC) to the {% data variables.product.prodname_dotcom %} API by using a {% data variables.product.prodname_github_app %} or by using a {% data variables.product.pat_v1 %}. -{% note %} - -**Note:** You cannot authenticate using a {% data variables.product.prodname_github_app %} for runners at the enterprise level. For more information, see "[AUTOTITLE](/actions/hosting-your-own-runners/managing-self-hosted-runners/managing-access-to-self-hosted-runners-using-groups#about-runner-groups)." - -{% endnote %} +> [!NOTE] +> You cannot authenticate using a {% data variables.product.prodname_github_app %} for runners at the enterprise level. For more information, see "[AUTOTITLE](/actions/hosting-your-own-runners/managing-self-hosted-runners/managing-access-to-self-hosted-runners-using-groups#about-runner-groups)." ## Authenticating ARC with a {% data variables.product.prodname_github_app %} @@ -32,11 +29,10 @@ You can authenticate {% data variables.product.prodname_actions_runner_controlle 1. Under "Permissions," click **Repository permissions**. Then use the dropdown menus to select the following access permissions. * **Administration**: Read and write - {% note %} - **Note**: `Administration: Read and write` is only required when configuring {% data variables.product.prodname_actions_runner_controller %} to register at the repository scope. It is not required to register at the organization scope. + > [!NOTE] + > `Administration: Read and write` is only required when configuring {% data variables.product.prodname_actions_runner_controller %} to register at the repository scope. It is not required to register at the organization scope. - {% endnote %} * **Metadata**: Read-only 1. Under "Permissions," click **Organization permissions**. Then use the dropdown menus to select the following access permissions. @@ -57,11 +53,10 @@ You can authenticate {% data variables.product.prodname_actions_runner_controlle ARC can use {% data variables.product.pat_v1_plural %} to register self-hosted runners. {% ifversion ghec or ghes %} -{% note %} -**Note:** Authenticating ARC with a {% data variables.product.pat_v1 %} is the only supported authentication method to register runners at the enterprise level. +> [!NOTE] +> Authenticating ARC with a {% data variables.product.pat_v1 %} is the only supported authentication method to register runners at the enterprise level. -{% endnote %} {% endif %} 1. Create a {% data variables.product.pat_v1 %} with the required scopes. The required scopes are different depending on whether you are registering runners at the repository{% ifversion ghec or ghes %}, organization, or enterprise{% else %} or organization{% endif %} level. For more information on how to create a {% data variables.product.pat_v1 %}, see "[AUTOTITLE](/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token#creating-a-personal-access-token-classic)." diff --git a/content/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/deploying-runner-scale-sets-with-actions-runner-controller.md b/content/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/deploying-runner-scale-sets-with-actions-runner-controller.md index 19cb479e8fe3..beaa3629a3e7 100644 --- a/content/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/deploying-runner-scale-sets-with-actions-runner-controller.md +++ b/content/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/deploying-runner-scale-sets-with-actions-runner-controller.md @@ -28,16 +28,11 @@ To deploy a runner scale set, you must have ARC up and running. For more informa You can deploy runner scale sets with ARC's Helm charts or by deploying the necessary manifests. Using ARC's Helm charts is the preferred method, especially if you do not have prior experience using ARC. -{% note %} - -**Notes:** - -* {% data reusables.actions.actions-runner-controller-security-practices-namespace %} -* {% data reusables.actions.actions-runner-controller-security-practices-secret %} -* We recommend running production workloads in isolation. {% data variables.product.prodname_actions %} workflows are designed to run arbitrary code, and using a shared Kubernetes cluster for production workloads could pose a security risk. -* Ensure you have implemented a way to collect and retain logs from the controller, listeners, and ephemeral runners. - -{% endnote %} +> [!NOTE] +> * {% data reusables.actions.actions-runner-controller-security-practices-namespace %} +> * {% data reusables.actions.actions-runner-controller-security-practices-secret %} +> * We recommend running production workloads in isolation. {% data variables.product.prodname_actions %} workflows are designed to run arbitrary code, and using a shared Kubernetes cluster for production workloads could pose a security risk. +> * Ensure you have implemented a way to collect and retain logs from the controller, listeners, and ephemeral runners. 1. To configure your runner scale set, run the following command in your terminal, using values from your ARC configuration. @@ -118,11 +113,8 @@ ARC offers several advanced configuration options. ### Configuring the runner scale set name -{% note %} - -**Note:** Runner scale set names are unique within the runner group they belong to. If you want to deploy multiple runner scale sets with the same name, they must belong to different runner groups. - -{% endnote %} +> [!NOTE] +> Runner scale set names are unique within the runner group they belong to. If you want to deploy multiple runner scale sets with the same name, they must belong to different runner groups. To configure the runner scale set name, you can define an `INSTALLATION_NAME` or set the value of `runnerScaleSetName` in your copy of the [`values.yaml`](https://github.com/actions/actions-runner-controller/blob/master/charts/gha-runner-scale-set/values.yaml) file. @@ -138,11 +130,10 @@ Make sure to pass the `values.yaml` file in your `helm install` command. See the Runner scale sets can be deployed at the repository, organization, or enterprise levels. {% ifversion ghec or ghes %} -{% note %} -**Note:** You can only deploy runner scale sets at the enterprise level when using {% data variables.product.pat_v1 %} authentication. +> [!NOTE] +> You can only deploy runner scale sets at the enterprise level when using {% data variables.product.pat_v1 %} authentication. -{% endnote %} {% endif %} To deploy runner scale sets to a specific level, set the value of `githubConfigUrl` in your copy of the `values.yaml` to the URL of your repository, organization, or enterprise. @@ -170,11 +161,8 @@ githubConfigUrl: "http(s):///<'enterprises/your_enterprise'/'org'/'org If you are not using enterprise-level runners, you can use {% data variables.product.prodname_github_apps %} to authenticate with the {% data variables.product.company_short %} API. For more information, see "[AUTOTITLE](/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/authenticating-to-the-github-api)." -{% note %} - -**Note:** Given the security risk associated with exposing your private key in plain text in a file on disk, we recommend creating a Kubernetes secret and passing the reference instead. - -{% endnote %} +> [!NOTE] +> Given the security risk associated with exposing your private key in plain text in a file on disk, we recommend creating a Kubernetes secret and passing the reference instead. You can either create a Kubernetes secret, or specify values in your [`values.yaml`](https://github.com/actions/actions-runner-controller/blob/master/charts/gha-runner-scale-set/values.yaml) file. @@ -266,11 +254,8 @@ ARC supports using anonymous or authenticated proxies. If you use authenticated The `maxRunners` and `minRunners` properties provide you with a range of options to customize your ARC setup. -{% note %} - -**Note:** ARC does not support scheduled maximum and minimum configurations. You can use a cronjob or any other scheduling solution to update the configuration on a schedule. - -{% endnote %} +> [!NOTE] +> ARC does not support scheduled maximum and minimum configurations. You can use a cronjob or any other scheduling solution to update the configuration on a schedule. #### Example: Unbounded number of runners @@ -302,11 +287,8 @@ minRunners: 20 In this configuration, {% data variables.product.prodname_actions_runner_controller %} will scale up to a maximum of `30` runners and will scale down to `20` runners when the jobs are complete. -{% note %} - -**Note:** The value of `minRunners` can never exceed that of `maxRunners`, unless `maxRunners` is commented out. - -{% endnote %} +> [!NOTE] +> The value of `minRunners` can never exceed that of `maxRunners`, unless `maxRunners` is commented out. ```yaml ## maxRunners is the max number of runners the auto scaling runner set will scale up to. @@ -332,11 +314,8 @@ minRunners: 0 ### Custom TLS certificates -{% note %} - -**Note:** If you are using a custom runner image that is not based on the `Debian` distribution, the following instructions will not work. - -{% endnote %} +> [!NOTE] +> If you are using a custom runner image that is not based on the `Debian` distribution, the following instructions will not work. Some environments require TLS certificates that are signed by a custom certificate authority (CA). Since the custom certificate authority certificates are not bundled with the controller or runner containers, you must inject them into their respective trust stores. @@ -473,13 +452,10 @@ If you are using container jobs and services or container actions, the `containe ### Using Docker-in-Docker mode -{% note %} - -**Note:** The Docker-in-Docker container requires privileged mode. For more information, see [Configure a Security Context for a Pod or Container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/) in the Kubernetes documentation. - -By default, the `dind` container uses the `docker:dind` image, which runs the Docker daemon as root. You can replace this image with `docker:dind-rootless` as long as you are aware of the [known limitations](https://docs.docker.com/engine/security/rootless/#known-limitations) and run the pods with `--privileged` mode. To learn how to customize the Docker-in-Docker configuration, see "[Customizing container modes](/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/deploying-runner-scale-sets-with-actions-runner-controller#customizing-container-modes)." - -{% endnote %} +> [!NOTE] +> The Docker-in-Docker container requires privileged mode. For more information, see [Configure a Security Context for a Pod or Container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/) in the Kubernetes documentation. +> +> By default, the `dind` container uses the `docker:dind` image, which runs the Docker daemon as root. You can replace this image with `docker:dind-rootless` as long as you are aware of the [known limitations](https://docs.docker.com/engine/security/rootless/#known-limitations) and run the pods with `--privileged` mode. To learn how to customize the Docker-in-Docker configuration, see "[Customizing container modes](/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/deploying-runner-scale-sets-with-actions-runner-controller#customizing-container-modes)." Docker-in-Docker mode is a configuration that allows you to run Docker inside a Docker container. In this configuration, for each runner pod created, ARC creates the following containers. @@ -582,29 +558,26 @@ containerMode: {% data reusables.actions.actions-runner-controller-helm-chart-options %} -{% note %} - -**Note:** When Kubernetes mode is enabled, workflows that are not configured with a container job will fail with an error similar to: - - ```bash - Jobs without a job container are forbidden on this runner, please add a 'container:' to your job or contact your self-hosted runner administrator. - ``` - -To allow jobs without a job container to run, set `ACTIONS_RUNNER_REQUIRE_JOB_CONTAINER` to `false` on your runner container. This instructs the runner to disable this check. - -```yaml -template: - spec: - containers: - - name: runner - image: ghcr.io/actions/actions-runner:latest - command: ["/home/runner/run.sh"] - env: - - name: ACTIONS_RUNNER_REQUIRE_JOB_CONTAINER - value: "false" -``` - -{% endnote %} +> [!NOTE] +> When Kubernetes mode is enabled, workflows that are not configured with a container job will fail with an error similar to: +> +> ```bash +> Jobs without a job container are forbidden on this runner, please add a 'container:' to your job or contact your self-hosted runner administrator. +> ``` +> +> To allow jobs without a job container to run, set `ACTIONS_RUNNER_REQUIRE_JOB_CONTAINER` to `false` on your runner container. This instructs the runner to disable this check. +> +> ```yaml +> template: +> spec: +> containers: +> - name: runner +> image: ghcr.io/actions/actions-runner:latest +> command: ["/home/runner/run.sh"] +> env: +> - name: ACTIONS_RUNNER_REQUIRE_JOB_CONTAINER +> value: "false" +> ``` ### Customizing container modes @@ -839,11 +812,8 @@ There are two options to configure hook extensions. * Store in your **custom runner image**. You can store the PodSpec in a YAML file anywhere in your custom runner image. For more information, see "[AUTOTITLE](/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/about-actions-runner-controller#creating-your-own-runner-image)." * Store in a **ConfigMap**. You can create a config map with the PodSpec and mount that config map in the runner container. For more information, see [ConfigMaps](https://kubernetes.io/docs/concepts/configuration/configmap/) in the Kubernetes documentation. -{% note %} - -**Note:** With both options, you must set the `ACTIONS_RUNNER_CONTAINER_HOOK_TEMPLATE` environment variable in the runner container spec to point to the path of the YAML file mounted in the runner container. - -{% endnote %} +> [!NOTE] +> With both options, you must set the `ACTIONS_RUNNER_CONTAINER_HOOK_TEMPLATE` environment variable in the runner container spec to point to the path of the YAML file mounted in the runner container. ##### Example: Using config map to set securityContext @@ -879,11 +849,8 @@ data: ## Enabling metrics -{% note %} - -**Note:** Metrics for ARC are available as of version gha-runner-scale-set-0.5.0. - -{% endnote %} +> [!NOTE] +> Metrics for ARC are available as of version gha-runner-scale-set-0.5.0. ARC can emit metrics about your runners, your jobs, and time spent on executing your workflows. Metrics can be used to identify congestion, monitor the health of your ARC deployment, visualize usage trends, optimize resource consumption, among many other use cases. Metrics are emitted by the controller-manager and listener pods in Prometheus format. For more information, see [Exposition formats](https://prometheus.io/docs/instrumenting/exposition_formats/) in the Prometheus documentation. @@ -898,11 +865,8 @@ metrics: listenerEndpoint: "/metrics" ``` -{% note %} - -**Note:** If the `metrics:` object is not provided or is commented out, the following flags will be applied to the controller-manager and listener pods with empty values: `--metrics-addr`, `--listener-metrics-addr`, `--listener-metrics-endpoint`. This will disable metrics for ARC. - -{% endnote %} +> [!NOTE] +> If the `metrics:` object is not provided or is commented out, the following flags will be applied to the controller-manager and listener pods with empty values: `--metrics-addr`, `--listener-metrics-addr`, `--listener-metrics-endpoint`. This will disable metrics for ARC. Once these properties are configured, your controller-manager and listener pods emit metrics via the listenerEndpoint bound to the ports that you specify in your [`values.yaml`](https://github.com/actions/actions-runner-controller/blob/master/charts/gha-runner-scale-set-controller/values.yaml) file. In the above example, the endpoint is `/metrics` and the port is `:8080`. You can use this endpoint to scrape metrics from your controller-manager and listener pods. @@ -912,11 +876,8 @@ To turn off metrics, update your [`values.yaml`](https://github.com/actions/acti The following table shows the metrics emitted by the controller-manager and listener pods. -{% note %} - -**Note:** The metrics that the controller-manager emits pertain to the controller runtime and are not owned by {% data variables.product.company_short %}. - -{% endnote %} +> [!NOTE] +> The metrics that the controller-manager emits pertain to the controller runtime and are not owned by {% data variables.product.company_short %}. | Owner | Metric | Type | Description | | ------------------ | --------------------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------- | @@ -977,26 +938,16 @@ For more information, see "[Deploying a runner scale set](/actions/hosting-your- If you would like to upgrade ARC but are concerned about downtime, you can deploy ARC in a high availability configuration to ensure runners are always available. For more information, see "[High availability and automatic failover](/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/deploying-runner-scale-sets-with-actions-runner-controller#high-availability-and-automatic-failover)." -{% note %} - -**Note:** - -Transitioning from the [community supported version of ARC](https://github.com/actions/actions-runner-controller/discussions/2775) to the GitHub supported version is a substantial architectural change. The GitHub supported version involves a redesign of many components of ARC. It is not a minor software upgrade. For these reasons, we recommend testing the new versions in a staging environment that matches your production environment first. This will ensure stability and reliability of the setup before deploying in production. - -{% endnote %} +> [!NOTE] +> Transitioning from the [community supported version of ARC](https://github.com/actions/actions-runner-controller/discussions/2775) to the GitHub supported version is a substantial architectural change. The GitHub supported version involves a redesign of many components of ARC. It is not a minor software upgrade. For these reasons, we recommend testing the new versions in a staging environment that matches your production environment first. This will ensure stability and reliability of the setup before deploying in production. ### Deploying a canary image You can test features before they are released by using canary releases of the controller-manager container image. Canary images are published with tag format `canary-SHORT_SHA`. For more information, see [`gha-runner-scale-set-controller`](https://github.com/actions/actions-runner-controller/pkgs/container/gha-runner-scale-set-controller) on the {% data variables.product.prodname_container_registry %}. -{% note %} - -**Notes:** - -* You must use Helm charts on your local file system. -* You cannot use the released Helm charts. - -{% endnote %} +> [!NOTE] +> * You must use Helm charts on your local file system. +> * You cannot use the released Helm charts. 1. Update the `tag` in the [gha-runner-scale-set-controller `values.yaml`](https://github.com/actions/actions-runner-controller/blob/master/charts/gha-runner-scale-set-controller/values.yaml) file to: `canary-SHORT_SHA` 1. Update the field `appVersion` in the [`Chart.yaml`](https://github.com/actions/actions-runner-controller/blob/master/charts/gha-runner-scale-set/Chart.yaml) file for `gha-runner-scale-set` to: `canary-SHORT_SHA` diff --git a/content/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/quickstart-for-actions-runner-controller.md b/content/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/quickstart-for-actions-runner-controller.md index 7eb808463032..b3a39c0101df 100644 --- a/content/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/quickstart-for-actions-runner-controller.md +++ b/content/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/quickstart-for-actions-runner-controller.md @@ -28,11 +28,8 @@ In order to use ARC, ensure you have the following. * For a managed cloud environment, you can use AKS. For more information, see [Azure Kubernetes Service](https://azure.microsoft.com/en-us/products/kubernetes-service) in the Azure documentation. * For a local setup, you can use minikube or kind. For more information, see [minikube start](https://minikube.sigs.k8s.io/docs/start/) in the minikube documentation and [kind](https://kind.sigs.k8s.io/) in the kind documentation. - {% note %} - - **Note:** OpenShift clusters are currently unsupported. - - {% endnote %} + > [!NOTE] + > OpenShift clusters are currently unsupported. * Helm 3 * For more information, see [Installing Helm](https://helm.sh/docs/intro/install/) in the Helm documentation. @@ -69,13 +66,9 @@ In order to use ARC, ensure you have the following. * Set `GITHUB_CONFIG_URL` to the URL of your repository, organization, or enterprise. This is the entity that the runners will belong to. * This example command installs the latest version of the Helm chart. To install a specific version, you can pass the `--version` argument with the version of the chart you wish to install. You can find the list of releases in the [GitHub Container Registry](https://github.com/actions/actions-runner-controller/pkgs/container/actions-runner-controller-charts%2Fgha-runner-scale-set). - {% note %} - - **Note:** - * {% data reusables.actions.actions-runner-controller-security-practices-namespace %} - * {% data reusables.actions.actions-runner-controller-security-practices-secret %} For more information, see "[AUTOTITLE](/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/deploying-runner-scale-sets-with-actions-runner-controller)." - - {% endnote %} + > [!NOTE] + > * {% data reusables.actions.actions-runner-controller-security-practices-namespace %} + > * {% data reusables.actions.actions-runner-controller-security-practices-secret %} For more information, see "[AUTOTITLE](/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/deploying-runner-scale-sets-with-actions-runner-controller)." ```bash copy INSTALLATION_NAME="arc-runner-set" diff --git a/content/actions/hosting-your-own-runners/managing-self-hosted-runners/adding-self-hosted-runners.md b/content/actions/hosting-your-own-runners/managing-self-hosted-runners/adding-self-hosted-runners.md index d90f15fb5bd2..a57269ea0904 100644 --- a/content/actions/hosting-your-own-runners/managing-self-hosted-runners/adding-self-hosted-runners.md +++ b/content/actions/hosting-your-own-runners/managing-self-hosted-runners/adding-self-hosted-runners.md @@ -21,13 +21,10 @@ If you are an organization or enterprise administrator, you might want to add yo For information on supported operating systems for self-hosted runners, or using self-hosted runners with a proxy server, see "[AUTOTITLE](/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners)." -{% warning %} - -**Warning:** {% data reusables.actions.self-hosted-runner-security %} - -For more information, see "[AUTOTITLE](/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#self-hosted-runner-security-with-public-repositories)." - -{% endwarning %} +> [!WARNING] +> {% data reusables.actions.self-hosted-runner-security %} +> +> For more information, see "[AUTOTITLE](/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#self-hosted-runner-security-with-public-repositories)." You can set up automation to scale the number of self-hosted runners. For more information, see "[AUTOTITLE](/actions/hosting-your-own-runners/managing-self-hosted-runners/autoscaling-with-self-hosted-runners)." @@ -49,11 +46,8 @@ You can add self-hosted runners to a single repository. To add a self-hosted run For information about how to add a self-hosted runner with the REST API, see "[AUTOTITLE](/rest/actions/self-hosted-runners)." -{% note %} - -**Note**: {% data reusables.actions.disable-selfhosted-runners-crossrefs %} - -{% endnote %} +> [!NOTE] +> {% data reusables.actions.disable-selfhosted-runners-crossrefs %} {% data reusables.repositories.navigate-to-repo %} {% data reusables.repositories.sidebar-settings %} diff --git a/content/actions/hosting-your-own-runners/managing-self-hosted-runners/autoscaling-with-self-hosted-runners.md b/content/actions/hosting-your-own-runners/managing-self-hosted-runners/autoscaling-with-self-hosted-runners.md index 58ef47ad3780..2b076c487c11 100644 --- a/content/actions/hosting-your-own-runners/managing-self-hosted-runners/autoscaling-with-self-hosted-runners.md +++ b/content/actions/hosting-your-own-runners/managing-self-hosted-runners/autoscaling-with-self-hosted-runners.md @@ -45,11 +45,8 @@ To add an ephemeral runner to your environment, include the `--ephemeral` parame The {% data variables.product.prodname_actions %} service will then automatically de-register the runner after it has processed one job. You can then create your own automation that wipes the runner after it has been de-registered. -{% note %} - -**Note:** If a job is labeled for a certain type of runner, but none matching that type are available, the job does not immediately fail at the time of queueing. Instead, the job will remain queued until the 24 hour timeout period expires. - -{% endnote %} +> [!NOTE] +> If a job is labeled for a certain type of runner, but none matching that type are available, the job does not immediately fail at the time of queueing. Instead, the job will remain queued until the 24 hour timeout period expires. {% ifversion actions-single-use-tokens %} diff --git a/content/actions/hosting-your-own-runners/managing-self-hosted-runners/configuring-the-self-hosted-runner-application-as-a-service.md b/content/actions/hosting-your-own-runners/managing-self-hosted-runners/configuring-the-self-hosted-runner-application-as-a-service.md index 2db234d35f3f..3501ace620e4 100644 --- a/content/actions/hosting-your-own-runners/managing-self-hosted-runners/configuring-the-self-hosted-runner-application-as-a-service.md +++ b/content/actions/hosting-your-own-runners/managing-self-hosted-runners/configuring-the-self-hosted-runner-application-as-a-service.md @@ -20,12 +20,10 @@ defaultPlatform: linux {% capture service_nonwindows_intro %} -{% note %} - -**Note:** You must add a runner to {% data variables.product.product_name %} before you can configure the self-hosted runner application as a service. +> [!NOTE] +> You must add a runner to {% data variables.product.product_name %} before you can configure the self-hosted runner application as a service. For more information, see "[AUTOTITLE](/actions/hosting-your-own-runners/managing-self-hosted-runners/adding-self-hosted-runners)." -{% endnote %} {% endcapture %} {% capture service_win_name %}actions.runner.*{% endcapture %} @@ -42,13 +40,10 @@ For Linux systems that use `systemd`, you can use the `svc.sh` script that is cr {% windows %} -{% note %} - -**Note:** Configuring the self-hosted runner application as a service on Windows is part of the application configuration process. If you have already configured the self-hosted runner application but did not choose to configure it as a service, you must remove the runner from {% data variables.product.prodname_dotcom %} and re-configure the application. When you re-configure the application, choose the option to configure the application as a service. - -For more information, see "[AUTOTITLE](/actions/hosting-your-own-runners/managing-self-hosted-runners/removing-self-hosted-runners)" and "[AUTOTITLE](/actions/hosting-your-own-runners/managing-self-hosted-runners/adding-self-hosted-runners)." - -{% endnote %} +> [!NOTE] +> Configuring the self-hosted runner application as a service on Windows is part of the application configuration process. If you have already configured the self-hosted runner application but did not choose to configure it as a service, you must remove the runner from {% data variables.product.prodname_dotcom %} and re-configure the application. When you re-configure the application, choose the option to configure the application as a service. +> +> For more information, see "[AUTOTITLE](/actions/hosting-your-own-runners/managing-self-hosted-runners/removing-self-hosted-runners)" and "[AUTOTITLE](/actions/hosting-your-own-runners/managing-self-hosted-runners/adding-self-hosted-runners)." You can manage the runner service in the Windows **Services** application, or you can use PowerShell to run the commands below. diff --git a/content/actions/hosting-your-own-runners/managing-self-hosted-runners/customizing-the-containers-used-by-jobs.md b/content/actions/hosting-your-own-runners/managing-self-hosted-runners/customizing-the-containers-used-by-jobs.md index a6db11fd05b3..c526b3cefb97 100644 --- a/content/actions/hosting-your-own-runners/managing-self-hosted-runners/customizing-the-containers-used-by-jobs.md +++ b/content/actions/hosting-your-own-runners/managing-self-hosted-runners/customizing-the-containers-used-by-jobs.md @@ -9,11 +9,8 @@ type: reference shortTitle: Customize containers used by jobs --- -{% note %} - -**Note**: This feature is currently in {% data variables.release-phases.public_preview %} and is subject to change. - -{% endnote %} +> [!NOTE] +> This feature is currently in {% data variables.release-phases.public_preview %} and is subject to change. ## About container customization @@ -476,11 +473,8 @@ No output is expected for `run_script_step`. {% data variables.product.prodname_dotcom %} has created an example repository that demonstrates how to generate customization scripts for Docker and Kubernetes. -{% note %} - -**Note:** The resulting scripts are available for testing purposes, and you will need to determine whether they are appropriate for your requirements. - -{% endnote %} +> [!NOTE] +> The resulting scripts are available for testing purposes, and you will need to determine whether they are appropriate for your requirements. 1. Clone the [actions/runner-container-hooks](https://github.com/actions/runner-container-hooks) repository to your self-hosted runner. @@ -502,11 +496,8 @@ When the resulting `index.js` is triggered by {% data variables.product.prodname The custom script must be located on the runner, but should not be stored in the self-hosted runner application directory (that is, the directory into which you downloaded and unpacked the runner software). The scripts are executed in the security context of the service account that's running the runner service. -{% note %} - -**Note**: The triggered script is processed synchronously, so it will block job execution while running. - -{% endnote %} +> [!NOTE] +> The triggered script is processed synchronously, so it will block job execution while running. The script is automatically executed when the runner has the following environment variable containing an absolute path to the script: diff --git a/content/actions/hosting-your-own-runners/managing-self-hosted-runners/removing-self-hosted-runners.md b/content/actions/hosting-your-own-runners/managing-self-hosted-runners/removing-self-hosted-runners.md index 5678dd02e59d..ca785aa0466b 100644 --- a/content/actions/hosting-your-own-runners/managing-self-hosted-runners/removing-self-hosted-runners.md +++ b/content/actions/hosting-your-own-runners/managing-self-hosted-runners/removing-self-hosted-runners.md @@ -17,18 +17,13 @@ shortTitle: Remove self-hosted runners ## Removing a runner from a repository -{% note %} - -**Notes:** - -* {% data reusables.actions.self-hosted-runner-removal-impact %} -* {% data reusables.actions.self-hosted-runner-auto-removal %} +> [!NOTE] +> * {% data reusables.actions.self-hosted-runner-removal-impact %} +> * {% data reusables.actions.self-hosted-runner-auto-removal %} {%- ifversion actions-single-use-tokens %} -* {% data reusables.actions.jit-runner-removal %} +> * {% data reusables.actions.jit-runner-removal %} {%- endif %} -{% endnote %} - To remove a self-hosted runner from a user repository you must be the repository owner. Organization owners{% ifversion custom-org-roles %} and users with the "Manage organization runners and runner groups" permission{% endif %} can remove a runner from a repository in the organization. {% ifversion custom-org-roles %}For more information about custom organization roles, see "[AUTOTITLE](/organizations/managing-peoples-access-to-your-organization-with-roles/about-custom-organization-roles)."{% endif %} We recommend that you also have access to the self-hosted runner machine. @@ -44,18 +39,13 @@ For information about how to remove a self-hosted runner with the REST API, see ## Removing a runner from an organization -{% note %} - -**Notes:** - -* {% data reusables.actions.self-hosted-runner-removal-impact %} -* {% data reusables.actions.self-hosted-runner-auto-removal %} +> [!NOTE] +> * {% data reusables.actions.self-hosted-runner-removal-impact %} +> * {% data reusables.actions.self-hosted-runner-auto-removal %} {%- ifversion actions-single-use-tokens %} -* {% data reusables.actions.jit-runner-removal %} +> * {% data reusables.actions.jit-runner-removal %} {%- endif %} -{% endnote %} - To remove a self-hosted runner from an organization, you must be an organization owner{% ifversion custom-org-roles %} or have the "Manage organization runners and runner groups" permission{% endif %}. We recommend that you also have access to the self-hosted runner machine. For information about how to remove a self-hosted runner with the REST API, see "[AUTOTITLE](/rest/actions/self-hosted-runners)." {% ifversion custom-org-roles %}For more information about custom organization roles, see "[AUTOTITLE](/organizations/managing-peoples-access-to-your-organization-with-roles/about-custom-organization-roles)."{% endif %} @@ -73,16 +63,14 @@ To remove a self-hosted runner from an organization, you must be an organization If you use {% data variables.product.prodname_ghe_cloud %}, you can also remove runners from an enterprise. For more information, see the [{% data variables.product.prodname_ghe_cloud %} documentation](/enterprise-cloud@latest/actions/hosting-your-own-runners/managing-self-hosted-runners/removing-self-hosted-runners#removing-a-runner-from-an-enterprise). {% endif %} {% ifversion ghec or ghes %} -{% note %} -**Notes:** - -* {% data reusables.actions.self-hosted-runner-removal-impact %} -* {% data reusables.actions.self-hosted-runner-auto-removal %} +> [!NOTE] +> * {% data reusables.actions.self-hosted-runner-removal-impact %} +> * {% data reusables.actions.self-hosted-runner-auto-removal %} {%- ifversion actions-single-use-tokens %} -* {% data reusables.actions.jit-runner-removal %} +> * {% data reusables.actions.jit-runner-removal %} + {%- endif %} -{% endnote %} To remove a self-hosted runner from an enterprise, you must be an enterprise owner. We recommend that you also have access to the self-hosted runner machine. For information about how to remove a self-hosted runner with the REST API, see the enterprise endpoints in the [{% data variables.product.prodname_actions %} REST API](/rest/actions/self-hosted-runners). diff --git a/content/actions/hosting-your-own-runners/managing-self-hosted-runners/running-scripts-before-or-after-a-job.md b/content/actions/hosting-your-own-runners/managing-self-hosted-runners/running-scripts-before-or-after-a-job.md index bcf37637d63d..9fd6434d4b7d 100644 --- a/content/actions/hosting-your-own-runners/managing-self-hosted-runners/running-scripts-before-or-after-a-job.md +++ b/content/actions/hosting-your-own-runners/managing-self-hosted-runners/running-scripts-before-or-after-a-job.md @@ -29,11 +29,8 @@ Your custom scripts can use the following features: Your script files must use a file extension for the relevant language, such as `.sh` or `.ps1`, in order to run successfully. -{% note %} - -**Note**: Avoid using your scripts to output sensitive information to the console, as anyone with read access to the repository might be able to see the output in the UI logs. - -{% endnote %} +> [!NOTE] +> Avoid using your scripts to output sensitive information to the console, as anyone with read access to the repository might be able to see the output in the UI logs. ### Handling exit codes @@ -45,11 +42,8 @@ The [`continue-on-error`](/actions/using-workflows/workflow-syntax-for-github-ac The custom scripts must be located on the runner, but should not be stored in the `actions-runner` application directory. The scripts are executed in the security context of the service account that's running the runner service. -{% note %} - -**Note**: The triggered scripts are processed synchronously, so they will block job execution while they are running. - -{% endnote %} +> [!NOTE] +> The triggered scripts are processed synchronously, so they will block job execution while they are running. The scripts are automatically executed when the runner has the following environment variables containing an absolute path to the script: * `ACTIONS_RUNNER_HOOK_JOB_STARTED`: The script defined in this environment variable is triggered when a job has been assigned to a runner, but before the job starts running. @@ -61,11 +55,8 @@ To set these environment variables, you can either add them to the operating sys ACTIONS_RUNNER_HOOK_JOB_STARTED=/opt/runner/cleanup_script.sh ``` -{% note %} - -**Note**: The script defined in `ACTIONS_RUNNER_HOOK_JOB_COMPLETED` is executed at the end of the job, before the job completes. This makes it unsuitable for use cases that may interrupt a runner, such as deleting the runner machine as part of an autoscaling implementation. - -{% endnote %} +> [!NOTE] +> The script defined in `ACTIONS_RUNNER_HOOK_JOB_COMPLETED` is executed at the end of the job, before the job completes. This makes it unsuitable for use cases that may interrupt a runner, such as deleting the runner machine as part of an autoscaling implementation. ## Troubleshooting diff --git a/content/actions/hosting-your-own-runners/managing-self-hosted-runners/using-labels-with-self-hosted-runners.md b/content/actions/hosting-your-own-runners/managing-self-hosted-runners/using-labels-with-self-hosted-runners.md index c7e66c3ae253..e89ef73ce467 100644 --- a/content/actions/hosting-your-own-runners/managing-self-hosted-runners/using-labels-with-self-hosted-runners.md +++ b/content/actions/hosting-your-own-runners/managing-self-hosted-runners/using-labels-with-self-hosted-runners.md @@ -17,7 +17,8 @@ For information on how to use labels to route jobs to specific types of self-hos {% data reusables.actions.self-hosted-runner-management-permissions-required %} ->[!NOTE]Actions Runner Controller does not support multiple labels, to find out more please read our [Actions Runner Controller documentation](/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/about-actions-runner-controller#using-arc-runners-in-a-workflow) +> [!NOTE] +> Actions Runner Controller does not support multiple labels, to find out more please read our [Actions Runner Controller documentation](/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/about-actions-runner-controller#using-arc-runners-in-a-workflow) ## Creating a custom label @@ -27,11 +28,8 @@ You can create custom labels for runners at the repository{% ifversion ghec or g * [Creating a custom label for an organization runner](#creating-a-custom-label-for-an-organization-runner){% ifversion ghec or ghes %} * [Creating a custom label for an enterprise runner](#creating-a-custom-label-for-an-enterprise-runner){% endif %} -{% note %} - -**Note:** Labels are case-insensitive. - -{% endnote %} +> [!NOTE] +> Labels are case-insensitive. ### Creating a custom label for a repository runner @@ -124,11 +122,8 @@ You can programmatically assign labels to a self-hosted runner after the runner * To programmatically assign labels to an existing self-hosted runner, you must use the REST API. For more information, see "[AUTOTITLE](/rest/actions/self-hosted-runners)." * To programmatically assign labels to a self-hosted runner during the initial runner configuration, you can pass label names to the `config` script using the `labels` parameter. - {% note %} - - **Note:** You cannot use the `config` script to assign labels to an existing self-hosted runner. - - {% endnote %} + > [!NOTE] + > You cannot use the `config` script to assign labels to an existing self-hosted runner. For example, this command assigns a label named `gpu` when configuring a new self-hosted runner: @@ -144,8 +139,5 @@ You can programmatically assign labels to a self-hosted runner after the runner ./config.sh --url --token --labels gpu,x64,linux ``` - {% note %} - - **Note:** If you replace an existing runner, then you must reassign any custom labels. - - {% endnote %} + > [!NOTE] + > If you replace an existing runner, then you must reassign any custom labels. diff --git a/content/actions/managing-workflow-runs-and-deployments/managing-deployments/managing-environments-for-deployment.md b/content/actions/managing-workflow-runs-and-deployments/managing-deployments/managing-environments-for-deployment.md index 670e09f9faf1..c40bd9e80165 100644 --- a/content/actions/managing-workflow-runs-and-deployments/managing-deployments/managing-environments-for-deployment.md +++ b/content/actions/managing-workflow-runs-and-deployments/managing-deployments/managing-environments-for-deployment.md @@ -31,13 +31,12 @@ You can configure environments with protection rules and secrets. When a workflo {% ifversion actions-break-glass %}Optionally, you can bypass an environment's protection rules and force all pending jobs referencing the environment to proceed. For more information, see "[AUTOTITLE](/actions/managing-workflow-runs/reviewing-deployments#bypassing-environment-protection-rules)."{% endif %} {% ifversion fpt %} -{% note %} -**Note:** Users with {% data variables.product.prodname_free_user %} plans can only configure environments for public repositories. If you convert a repository from public to private, any configured protection rules or environment secrets will be ignored, and you will not be able to configure any environments. If you convert your repository back to public, you will have access to any previously configured protection rules and environment secrets. +> [!NOTE] +> Users with {% data variables.product.prodname_free_user %} plans can only configure environments for public repositories. If you convert a repository from public to private, any configured protection rules or environment secrets will be ignored, and you will not be able to configure any environments. If you convert your repository back to public, you will have access to any previously configured protection rules and environment secrets. +> +> Organizations with {% data variables.product.prodname_team %} and users with {% data variables.product.prodname_pro %} can configure environments for private repositories. For more information, see "[AUTOTITLE](/get-started/learning-about-github/githubs-plans)." -Organizations with {% data variables.product.prodname_team %} and users with {% data variables.product.prodname_pro %} can configure environments for private repositories. For more information, see "[AUTOTITLE](/get-started/learning-about-github/githubs-plans)." - -{% endnote %} {% endif %} ## Deployment protection rules @@ -58,21 +57,23 @@ Use required reviewers to require a specific person or team to approve workflow For more information on reviewing jobs that reference an environment with required reviewers, see "[AUTOTITLE](/actions/managing-workflow-runs/reviewing-deployments)." -{% ifversion fpt %}{% note %} +{% ifversion fpt %} -**Note:** If you are on a {% data variables.product.prodname_free_user %}, {% data variables.product.prodname_pro %}, or {% data variables.product.prodname_team %} plan, required reviewers are only available for public repositories. +> [!NOTE] +> If you are on a {% data variables.product.prodname_free_user %}, {% data variables.product.prodname_pro %}, or {% data variables.product.prodname_team %} plan, required reviewers are only available for public repositories. -{% endnote %}{% endif %} +{% endif %} ### Wait timer Use a wait timer to delay a job for a specific amount of time after the job is initially triggered. The time (in minutes) must be an integer between 1 and 43,200 (30 days). -{% ifversion fpt %}{% note %} +{% ifversion fpt %} -**Note:** If you are on a {% data variables.product.prodname_free_user %}, {% data variables.product.prodname_pro %}, or {% data variables.product.prodname_team %} plan, wait timers are only available for public repositories. +> [!NOTE] +> If you are on a {% data variables.product.prodname_free_user %}, {% data variables.product.prodname_pro %}, or {% data variables.product.prodname_team %} plan, wait timers are only available for public repositories. -{% endnote %}{% endif %} +{% endif %} ### Deployment branches{% ifversion deployment-protections-tag-patterns %} and tags{% endif %} @@ -85,11 +86,10 @@ Use deployment branches{% ifversion deployment-protections-tag-patterns %} and t {%- endif %} * **Protected branches{% ifversion deployment-protections-tag-patterns %} only{% endif %}**: Only branches with branch protection rules enabled can deploy to the environment. If no branch protection rules are defined for any branch in the repository, then all branches can deploy. For more information about branch protection rules, see "[AUTOTITLE](/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches)."{% ifversion actions-protected-branches-restrictions %} - {% note %} + > [!NOTE] + > Deployment workflow runs triggered by tags with the same name as a protected branch and forks with branches that match the protected branch name cannot deploy to the environment. - **Note:** Deployment workflow runs triggered by tags with the same name as a protected branch and forks with branches that match the protected branch name cannot deploy to the environment. - - {% endnote %}{% endif %} + {% endif %} * **Selected branches{% ifversion deployment-protections-tag-patterns %} and tags{% endif %}**: Only branches{% ifversion deployment-protections-tag-patterns %} and tags{% endif %} that match your specified name patterns can deploy to the environment. If you specify `releases/*` as a deployment branch{% ifversion deployment-protections-tag-patterns %} or tag{% endif %} rule, only a branch{% ifversion deployment-protections-tag-patterns %} or tag{% endif %} whose name begins with `releases/` can deploy to the environment. (Wildcard characters will not match `/`. To match branches{% ifversion deployment-protections-tag-patterns %} or tags{% endif %} that begin with `release/` and contain an additional single slash, use `release/*/*`.) If you add `main` as a branch rule, a branch named `main` can also deploy to the environment. For more information about syntax options for deployment branches, see the [Ruby `File.fnmatch` documentation](https://ruby-doc.org/core-2.5.1/File.html#method-c-fnmatch). @@ -100,11 +100,12 @@ Use deployment branches{% ifversion deployment-protections-tag-patterns %} and t {% endif %} -{% ifversion fpt %}{% note %} +{% ifversion fpt %} -**Note:** Deployment branches{% ifversion deployment-protections-tag-patterns %} and tags{% endif %} are available for all public repositories. For users on {% data variables.product.prodname_pro %} or {% data variables.product.prodname_team %} plans, deployment branches{% ifversion deployment-protections-tag-patterns %} and tags{% endif %} are also available for private repositories. +> [!NOTE] +> Deployment branches{% ifversion deployment-protections-tag-patterns %} and tags{% endif %} are available for all public repositories. For users on {% data variables.product.prodname_pro %} or {% data variables.product.prodname_team %} plans, deployment branches{% ifversion deployment-protections-tag-patterns %} and tags{% endif %} are also available for private repositories. -{% endnote %}{% endif %} +{% endif %} {% ifversion actions-break-glass %} @@ -114,11 +115,12 @@ By default, administrators can bypass the protection rules and force deployments Alternatively, you can configure environments to disallow bypassing the protection rules for all deployments to the environment. -{% ifversion fpt %}{% note %} +{% ifversion fpt %} -**Note:** Allowing administrators to bypass protection rules is only available for public repositories for users on {% data variables.product.prodname_free_user %}, {% data variables.product.prodname_pro %}, and {% data variables.product.prodname_team %} plans. +> [!NOTE] +> Allowing administrators to bypass protection rules is only available for public repositories for users on {% data variables.product.prodname_free_user %}, {% data variables.product.prodname_pro %}, and {% data variables.product.prodname_team %} plans. -{% endnote %}{% endif %} +{% endif %} {% endif %} {% ifversion actions-custom-deployment-protection-rules-beta %} @@ -131,11 +133,12 @@ Alternatively, you can configure environments to disallow bypassing the protecti Once custom deployment protection rules have been created and installed on a repository, you can enable the custom deployment protection rule for any environment in the repository. For more information about configuring and enabling custom deployment protection rules, see "[AUTOTITLE](/actions/deployment/protecting-deployments/configuring-custom-deployment-protection-rules)." -{% ifversion fpt %}{% note %} +{% ifversion fpt %} -**Note:** Custom deployment protection rules are only available for public repositories for users on {% data variables.product.prodname_free_user %}, {% data variables.product.prodname_pro %}, and {% data variables.product.prodname_team %} plans. +> [!NOTE] +> Custom deployment protection rules are only available for public repositories for users on {% data variables.product.prodname_free_user %}, {% data variables.product.prodname_pro %}, and {% data variables.product.prodname_team %} plans. -{% endnote %}{% endif %} +{% endif %} {% endif %} @@ -144,45 +147,38 @@ Once custom deployment protection rules have been created and installed on a rep Secrets stored in an environment are only available to workflow jobs that reference the environment. If the environment requires approval, a job cannot access environment secrets until one of the required reviewers approves it. For more information about secrets, see "[AUTOTITLE](/actions/security-guides/using-secrets-in-github-actions)." {% ifversion fpt %} -{% note %} - -**Notes:** +> [!NOTE] +> * Workflows that run on self-hosted runners are not run in an isolated container, even if they use environments. Environment secrets should be treated with the same level of security as repository and organization secrets. For more information, see "[AUTOTITLE](/actions/security-guides/security-hardening-for-github-actions#hardening-for-self-hosted-runners)." +> * Environment secrets are only available in public repositories if you are using {% data variables.product.prodname_free_user %}. For access to environment secrets in private or internal repositories, you must use {% data variables.product.prodname_pro %}, {% data variables.product.prodname_team %}, or {% data variables.product.prodname_enterprise %}. For more information on switching your plan, see "[AUTOTITLE](/billing/managing-the-plan-for-your-github-account/upgrading-your-accounts-plan)." -* Workflows that run on self-hosted runners are not run in an isolated container, even if they use environments. Environment secrets should be treated with the same level of security as repository and organization secrets. For more information, see "[AUTOTITLE](/actions/security-guides/security-hardening-for-github-actions#hardening-for-self-hosted-runners)." -* Environment secrets are only available in public repositories if you are using {% data variables.product.prodname_free_user %}. For access to environment secrets in private or internal repositories, you must use {% data variables.product.prodname_pro %}, {% data variables.product.prodname_team %}, or {% data variables.product.prodname_enterprise %}. For more information on switching your plan, see "[AUTOTITLE](/billing/managing-the-plan-for-your-github-account/upgrading-your-accounts-plan)." - -{% endnote %} {% else %} -{% note %} -**Note:** Workflows that run on self-hosted runners are not run in an isolated container, even if they use environments. Environment secrets should be treated with the same level of security as repository and organization secrets. For more information, see "[AUTOTITLE](/actions/security-guides/security-hardening-for-github-actions#hardening-for-self-hosted-runners)." +> [!NOTE] +> Workflows that run on self-hosted runners are not run in an isolated container, even if they use environments. Environment secrets should be treated with the same level of security as repository and organization secrets. For more information, see "[AUTOTITLE](/actions/security-guides/security-hardening-for-github-actions#hardening-for-self-hosted-runners)." -{% endnote %} {% endif %} ## Environment variables Variables stored in an environment are only available to workflow jobs that reference the environment. These variables are only accessible using the [`vars`](/actions/learn-github-actions/contexts#vars-context) context. For more information, see "[AUTOTITLE](/actions/learn-github-actions/variables)." -{% ifversion fpt %}{% note %} +{% ifversion fpt %} -**Note:** Environment variables are available for all public repositories. For users on {% data variables.product.prodname_pro %} or {% data variables.product.prodname_team %} plans, environment variables are also available for private repositories. +> [!NOTE] +> Environment variables are available for all public repositories. For users on {% data variables.product.prodname_pro %} or {% data variables.product.prodname_team %} plans, environment variables are also available for private repositories. -{% endnote %}{% endif %} +{% endif %} ## Creating an environment {% data reusables.actions.permissions-statement-environment %} {% ifversion fpt %} -{% note %} - -**Notes:** -* Creation of an environment in a private repository is available to organizations with {% data variables.product.prodname_team %} and users with {% data variables.product.prodname_pro %}. -* Some features for environments have no or limited availability for private repositories. If you are unable to access a feature described in the instructions below, please see the documentation linked in the related step for availability information. +> [!NOTE] +> * Creation of an environment in a private repository is available to organizations with {% data variables.product.prodname_team %} and users with {% data variables.product.prodname_pro %}. +> * Some features for environments have no or limited availability for private repositories. If you are unable to access a feature described in the instructions below, please see the documentation linked in the related step for availability information. -{% endnote %} {% endif %} {% data reusables.repositories.navigate-to-repo %} diff --git a/content/actions/managing-workflow-runs-and-deployments/managing-deployments/reviewing-deployments.md b/content/actions/managing-workflow-runs-and-deployments/managing-deployments/reviewing-deployments.md index 87295e370a6f..4b34487ae409 100644 --- a/content/actions/managing-workflow-runs-and-deployments/managing-deployments/reviewing-deployments.md +++ b/content/actions/managing-workflow-runs-and-deployments/managing-deployments/reviewing-deployments.md @@ -27,11 +27,12 @@ For more information about environments and required approvals, see "[AUTOTITLE] * To approve the job, click **Approve and deploy**. Once a job is approved (and any other deployment protection rules have passed), the job will proceed. At this point, the job can access any secrets stored in the environment. * To reject the job, click **Reject**. If a job is rejected, the workflow will fail. -{% ifversion deployments-prevent-self-approval %}{% note %} +{% ifversion deployments-prevent-self-approval %} -**Note:** If the targeted environment is configured to prevent self-approvals for deployments, you will not be able to approve a deployment from a workflow run you initiated. For more information, see "[AUTOTITLE](/actions/deployment/targeting-different-environments/managing-environments-for-deployment#required-reviewers)." +> [!NOTE] +> If the targeted environment is configured to prevent self-approvals for deployments, you will not be able to approve a deployment from a workflow run you initiated. For more information, see "[AUTOTITLE](/actions/deployment/targeting-different-environments/managing-environments-for-deployment#required-reviewers)." -{% endnote %}{% endif %} +{% endif %} {% ifversion actions-break-glass %} @@ -39,14 +40,9 @@ For more information about environments and required approvals, see "[AUTOTITLE] If you have configured deployment protection rules that control whether software can be deployed to an environment, you can bypass these rules and force all pending jobs referencing the environment to proceed. -{% note %} - -**Notes:** - -* You cannot bypass deployment protection rules if the environment has been configured to prevent admins from bypassing configured protection rules. For more information, see "[AUTOTITLE](/actions/deployment/targeting-different-environments/managing-environments-for-deployment#creating-an-environment)." -* You can only bypass deployment protection rules during workflow execution when a job referencing the environment is in a "Pending" state. - -{% endnote %} +> [!NOTE] +> * You cannot bypass deployment protection rules if the environment has been configured to prevent admins from bypassing configured protection rules. For more information, see "[AUTOTITLE](/actions/deployment/targeting-different-environments/managing-environments-for-deployment#creating-an-environment)." +> * You can only bypass deployment protection rules during workflow execution when a job referencing the environment is in a "Pending" state. 1. Navigate to the workflow run. For more information about navigating to a workflow run, see "[AUTOTITLE](/actions/monitoring-and-troubleshooting-workflows/viewing-workflow-run-history)." 1. To the right of **Deployment protection rules**, click **Start all waiting jobs**. diff --git a/content/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/disabling-and-enabling-a-workflow.md b/content/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/disabling-and-enabling-a-workflow.md index 6219e13dd85e..6a0e6169272b 100644 --- a/content/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/disabling-and-enabling-a-workflow.md +++ b/content/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/disabling-and-enabling-a-workflow.md @@ -22,11 +22,8 @@ Temporarily disabling a workflow can be useful in many scenarios. These are a fe * A workflow that sends requests to a service that is down. * Workflows on a forked repository that aren't needed (for example, scheduled workflows). -{% warning %} - -**Warning:** {% data reusables.actions.scheduled-workflows-disabled %} - -{% endwarning %} +> [!WARNING] +> {% data reusables.actions.scheduled-workflows-disabled %} You can also disable and enable a workflow using the REST API. For more information, see "[AUTOTITLE](/rest/actions/workflows)." diff --git a/content/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/manually-running-a-workflow.md b/content/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/manually-running-a-workflow.md index 291ae42af5da..998deaa1de9d 100644 --- a/content/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/manually-running-a-workflow.md +++ b/content/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/manually-running-a-workflow.md @@ -35,11 +35,9 @@ To trigger the `workflow_dispatch` event, your workflow must be in the default b ![Screenshot of the "Actions" page. Features apart from one workflow in the left sidebar are grayed out.](/assets/images/help/repository/actions-select-workflow.png) {%- endif %} 1. Above the list of workflow runs, click the **Run workflow** button. - {% note %} - **Note:** To see the **Run workflow** button, your workflow file must use the `workflow_dispatch` event trigger. Only workflow files that use the `workflow_dispatch` event trigger will have the option to run the workflow manually using the **Run workflow** button. For more information about configuring the `workflow_dispatch` event, see "[AUTOTITLE](/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch)." - - {% endnote %} + > [!NOTE] + > To see the **Run workflow** button, your workflow file must use the `workflow_dispatch` event trigger. Only workflow files that use the `workflow_dispatch` event trigger will have the option to run the workflow manually using the **Run workflow** button. For more information about configuring the `workflow_dispatch` event, see "[AUTOTITLE](/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch)." ![Screenshot of a workflow page. Above the list of workflow runs, a button, labeled "Run workflow", is outlined in dark orange.](/assets/images/help/actions/actions-workflow-dispatch.png) 1. Select the **Branch** dropdown menu and click a branch to run the workflow on. @@ -88,10 +86,7 @@ gh run watch When using the REST API, you configure the `inputs` and `ref` as request body parameters. If the inputs are omitted, the default values defined in the workflow file are used. -{% note %} - -**Note:** You can define up to 10 `inputs` for a `workflow_dispatch` event. - -{% endnote %} +> [!NOTE] +> You can define up to 10 `inputs` for a `workflow_dispatch` event. For more information about using the REST API, see "[AUTOTITLE](/rest/actions/workflows#create-a-workflow-dispatch-event)." diff --git a/content/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/removing-workflow-artifacts.md b/content/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/removing-workflow-artifacts.md index 7dd813db73d9..29c0160d9b12 100644 --- a/content/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/removing-workflow-artifacts.md +++ b/content/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/removing-workflow-artifacts.md @@ -14,11 +14,8 @@ redirect_from: ## Deleting an artifact -{% warning %} - -**Warning:** Once you delete an artifact, it cannot be restored. - -{% endwarning %} +> [!WARNING] +> Once you delete an artifact, it cannot be restored. {% data reusables.repositories.permissions-statement-write %} diff --git a/content/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/skipping-workflow-runs.md b/content/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/skipping-workflow-runs.md index cb5913ec662b..3dd59237c949 100644 --- a/content/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/skipping-workflow-runs.md +++ b/content/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/skipping-workflow-runs.md @@ -12,11 +12,8 @@ redirect_from: {% data reusables.actions.enterprise-github-hosted-runners %} -{% note %} - -**Note:** If a workflow is skipped due to [path filtering](/actions/using-workflows/workflow-syntax-for-github-actions#onpushpull_requestpull_request_targetpathspaths-ignore), [branch filtering](/actions/using-workflows/workflow-syntax-for-github-actions#onpull_requestpull_request_targetbranchesbranches-ignore) or a commit message (see below), then checks associated with that workflow will remain in a "Pending" state. A pull request that requires those checks to be successful will be blocked from merging. - -{% endnote %} +> [!NOTE] +> If a workflow is skipped due to [path filtering](/actions/using-workflows/workflow-syntax-for-github-actions#onpushpull_requestpull_request_targetpathspaths-ignore), [branch filtering](/actions/using-workflows/workflow-syntax-for-github-actions#onpull_requestpull_request_targetbranchesbranches-ignore) or a commit message (see below), then checks associated with that workflow will remain in a "Pending" state. A pull request that requires those checks to be successful will be blocked from merging. Workflows that would otherwise be triggered using `on: push` or `on: pull_request` won't be triggered if you add any of the following strings to the commit message in a push, or the HEAD commit of a pull request: @@ -34,10 +31,7 @@ Alternatively, you can add a `skip-checks` trailer to your commit message. The t You won't be able to merge the pull request if your repository is configured to require specific checks to pass first. To allow the pull request to be merged you can push a new commit to the pull request without the skip instruction in the commit message. -{% note %} - -**Note:** Skip instructions only apply to the `push` and `pull_request` events. For example, adding `[skip ci]` to a commit message won't stop a workflow that's triggered `on: pull_request_target` from running. - -{% endnote %} +> [!NOTE] +> Skip instructions only apply to the `push` and `pull_request` events. For example, adding `[skip ci]` to a commit message won't stop a workflow that's triggered `on: pull_request_target` from running. Skip instructions only apply to the workflow run(s) that would be triggered by the commit that contains the skip instructions. You can also disable a workflow from running. For more information, see "[AUTOTITLE](/actions/managing-workflow-runs/disabling-and-enabling-a-workflow)." diff --git a/content/actions/migrating-to-github-actions/using-github-actions-importer-to-automate-migrations/extending-github-actions-importer-with-custom-transformers.md b/content/actions/migrating-to-github-actions/using-github-actions-importer-to-automate-migrations/extending-github-actions-importer-with-custom-transformers.md index 3a7a7d15acad..07849e65717d 100644 --- a/content/actions/migrating-to-github-actions/using-github-actions-importer-to-automate-migrations/extending-github-actions-importer-with-custom-transformers.md +++ b/content/actions/migrating-to-github-actions/using-github-actions-importer-to-automate-migrations/extending-github-actions-importer-with-custom-transformers.md @@ -43,11 +43,8 @@ Alternatively, you can use the glob pattern syntax to specify multiple custom tr gh actions-importer ... --custom-transformers transformers/*.rb ``` -{% note %} - -**Note:** When you use custom transformers, the custom transformer files must reside in the same directory, or in subdirectories, from where the `gh actions-importer` command is run. - -{% endnote %} +> [!NOTE] +> When you use custom transformers, the custom transformer files must reside in the same directory, or in subdirectories, from where the `gh actions-importer` command is run. ## Creating custom transformers for items @@ -91,11 +88,8 @@ The above example results in the following {% data variables.product.prodname_ac The `transform` method uses the identifier of the build step from your source CI/CD instance in an argument. In this example, the identifier is `buildJavaScriptLibrary`. You can also use comma-separated values to pass multiple identifiers to the `transform` method. For example, `transform "buildJavaScriptApp", "buildTypeScriptApp" { |item| ... }`. -{% note %} - -**Note**: The data structure of `item` will be different depending on the CI/CD platform and the type of item being converted. - -{% endnote %} +> [!NOTE] +> The data structure of `item` will be different depending on the CI/CD platform and the type of item being converted. ## Creating custom transformers for runners @@ -171,11 +165,8 @@ There are several ways you can set up custom transformers to map your environmen env /^(.+)_SSH_KEY/, secret("%s_SSH_KEY) ``` - {% note %} - - **Note**: The order in which `env` methods are defined matters when using regular expressions. The first `env` transformer that matches an environment variable name takes precedence over subsequent `env` methods. You should define your most specific environment variable transformers first. - - {% endnote %} + > [!NOTE] + > The order in which `env` methods are defined matters when using regular expressions. The first `env` transformer that matches an environment variable name takes precedence over subsequent `env` methods. You should define your most specific environment variable transformers first. ## Legal notice diff --git a/content/actions/migrating-to-github-actions/using-github-actions-importer-to-automate-migrations/migrating-from-azure-devops-with-github-actions-importer.md b/content/actions/migrating-to-github-actions/using-github-actions-importer-to-automate-migrations/migrating-from-azure-devops-with-github-actions-importer.md index 8a1455c24259..7656ccaa8001 100644 --- a/content/actions/migrating-to-github-actions/using-github-actions-importer-to-automate-migrations/migrating-from-azure-devops-with-github-actions-importer.md +++ b/content/actions/migrating-to-github-actions/using-github-actions-importer-to-automate-migrations/migrating-from-azure-devops-with-github-actions-importer.md @@ -536,11 +536,8 @@ steps: | stage | `job` | Partially supported | | stageList | `job` | Partially supported | -{% note %} - -**Note:** A template used under the `step` key with this parameter type is only serialized as a composite action if the steps are used at the beginning or end of the template steps. A template used under the `stage`, `deployment`, and `job` keys with this parameter type are not transformed into a reusable workflow, and instead are serialized as a standalone workflow. - -{% endnote %} +> [!NOTE] +> A template used under the `step` key with this parameter type is only serialized as a composite action if the steps are used at the beginning or end of the template steps. A template used under the `stage`, `deployment`, and `job` keys with this parameter type are not transformed into a reusable workflow, and instead are serialized as a standalone workflow. ## Legal notice diff --git a/content/actions/migrating-to-github-actions/using-github-actions-importer-to-automate-migrations/migrating-from-bamboo-with-github-actions-importer.md b/content/actions/migrating-to-github-actions/using-github-actions-importer-to-automate-migrations/migrating-from-bamboo-with-github-actions-importer.md index 6719c745f615..edeb53ce93f9 100644 --- a/content/actions/migrating-to-github-actions/using-github-actions-importer-to-automate-migrations/migrating-from-bamboo-with-github-actions-importer.md +++ b/content/actions/migrating-to-github-actions/using-github-actions-importer-to-automate-migrations/migrating-from-bamboo-with-github-actions-importer.md @@ -371,11 +371,8 @@ For more information about supported Bamboo concept and plugin mappings, see the | `bamboo.shortPlanKey` | {% raw %}`${{ github.workflow }}`{% endraw %} | `bamboo.shortPlanName` | {% raw %}`${{ github.workflow }}`{% endraw %} -{% note %} - -**Note:** Unknown variables are transformed to {% raw %}`${{ env. }}`{% endraw %} and must be replaced or added under `env` for proper operation. For example, `${bamboo.jira.baseUrl}` will become {% raw %}`${{ env.jira_baseUrl }}`{% endraw %}. - -{% endnote %} +> [!NOTE] +> Unknown variables are transformed to {% raw %}`${{ env. }}`{% endraw %} and must be replaced or added under `env` for proper operation. For example, `${bamboo.jira.baseUrl}` will become {% raw %}`${{ env.jira_baseUrl }}`{% endraw %}. ### System Variables diff --git a/content/actions/migrating-to-github-actions/using-github-actions-importer-to-automate-migrations/migrating-from-jenkins-with-github-actions-importer.md b/content/actions/migrating-to-github-actions/using-github-actions-importer-to-automate-migrations/migrating-from-jenkins-with-github-actions-importer.md index 5e6dc8f84a23..c6d0b1799eea 100644 --- a/content/actions/migrating-to-github-actions/using-github-actions-importer-to-automate-migrations/migrating-from-jenkins-with-github-actions-importer.md +++ b/content/actions/migrating-to-github-actions/using-github-actions-importer-to-automate-migrations/migrating-from-jenkins-with-github-actions-importer.md @@ -212,11 +212,8 @@ This section contains reference information on environment variables, optional a * `GITHUB_INSTANCE_URL`: The URL to the target {% data variables.product.prodname_dotcom %} instance (for example, `https://github.com`). * `JENKINS_ACCESS_TOKEN`: The Jenkins API token used to view Jenkins resources. - {% note %} - - **Note**: This token requires access to all jobs that you want to migrate or audit. In cases where a folder or job does not inherit access control lists from their parent, you must grant explicit permissions or full admin privileges. - - {% endnote %} + > [!NOTE] + > This token requires access to all jobs that you want to migrate or audit. In cases where a folder or job does not inherit access control lists from their parent, you must grant explicit permissions or full admin privileges. * `JENKINS_USERNAME`: The username of the user account that created the Jenkins API token. * `JENKINS_INSTANCE_URL`: The URL of the Jenkins instance. diff --git a/content/actions/monitoring-and-troubleshooting-workflows/monitoring-workflows/adding-a-workflow-status-badge.md b/content/actions/monitoring-and-troubleshooting-workflows/monitoring-workflows/adding-a-workflow-status-badge.md index 2857b0565dbd..1404091f6837 100644 --- a/content/actions/monitoring-and-troubleshooting-workflows/monitoring-workflows/adding-a-workflow-status-badge.md +++ b/content/actions/monitoring-and-troubleshooting-workflows/monitoring-workflows/adding-a-workflow-status-badge.md @@ -13,11 +13,8 @@ versions: {% data reusables.actions.enterprise-github-hosted-runners %} -{% note %} - -**Note**: Workflow badges in a private repository are not accessible externally, so you won't be able to embed them or link to them from an external site. - -{% endnote %} +> [!NOTE] +> Workflow badges in a private repository are not accessible externally, so you won't be able to embed them or link to them from an external site. {% data reusables.repositories.actions-workflow-status-badge-intro %} diff --git a/content/actions/monitoring-and-troubleshooting-workflows/monitoring-workflows/using-workflow-run-logs.md b/content/actions/monitoring-and-troubleshooting-workflows/monitoring-workflows/using-workflow-run-logs.md index 9deda463f111..b03b02284256 100644 --- a/content/actions/monitoring-and-troubleshooting-workflows/monitoring-workflows/using-workflow-run-logs.md +++ b/content/actions/monitoring-and-troubleshooting-workflows/monitoring-workflows/using-workflow-run-logs.md @@ -63,11 +63,8 @@ You can download the log files from your workflow run. You can also download a w {% ifversion re-run-jobs %} - {% note %} - - **Note**: When you download the log archive for a workflow that was partially re-run, the archive only includes the jobs that were re-run. To get a complete set of logs for jobs that were run from a workflow, you must download the log archives for the previous run attempts that ran the other jobs. - - {% endnote %} + > [!NOTE] + > When you download the log archive for a workflow that was partially re-run, the archive only includes the jobs that were re-run. To get a complete set of logs for jobs that were run from a workflow, you must download the log archives for the previous run attempts that ran the other jobs. {% endif %} diff --git a/content/actions/monitoring-and-troubleshooting-workflows/monitoring-workflows/viewing-job-execution-time.md b/content/actions/monitoring-and-troubleshooting-workflows/monitoring-workflows/viewing-job-execution-time.md index 027c0100e1fd..879cc99893c7 100644 --- a/content/actions/monitoring-and-troubleshooting-workflows/monitoring-workflows/viewing-job-execution-time.md +++ b/content/actions/monitoring-and-troubleshooting-workflows/monitoring-workflows/viewing-job-execution-time.md @@ -21,8 +21,5 @@ Billable job execution minutes are only shown for jobs run on private repositori 1. Under the job summary, you can view the job's execution time. 1. To view details about the billable job execution time, in the left sidebar under "Run details", click **{% octicon "stopwatch" aria-hidden="true" %} Usage**. - {% note %} - - **Note:** The billable time shown does not include any minute multipliers. To view your total {% data variables.product.prodname_actions %} usage, including minute multipliers, see "[AUTOTITLE](/billing/managing-billing-for-github-actions/viewing-your-github-actions-usage)." - - {% endnote %} + > [!NOTE] + > The billable time shown does not include any minute multipliers. To view your total {% data variables.product.prodname_actions %} usage, including minute multipliers, see "[AUTOTITLE](/billing/managing-billing-for-github-actions/viewing-your-github-actions-usage)." diff --git a/content/actions/monitoring-and-troubleshooting-workflows/troubleshooting-workflows/working-with-support-for-github-actions.md b/content/actions/monitoring-and-troubleshooting-workflows/troubleshooting-workflows/working-with-support-for-github-actions.md index cf758961165d..6cc24558de71 100644 --- a/content/actions/monitoring-and-troubleshooting-workflows/troubleshooting-workflows/working-with-support-for-github-actions.md +++ b/content/actions/monitoring-and-troubleshooting-workflows/troubleshooting-workflows/working-with-support-for-github-actions.md @@ -36,19 +36,13 @@ Some information that {% data variables.contact.github_support %} will request c Self-hosted runner log file names are be formatted: `Runner_YYYY####-xxxxxx-utc.log` and `Worker_YYYY####-xxxxxx-utc.log`. -{% note %} +> [!NOTE] +> Attach files to your support ticket by changing the file's extension to `.txt` or `.zip`. If you include textual data such as log or workflow file snippets inline in your ticket, ensure they are formatted correctly as Markdown code blocks. For more information about proper Markdown formatting, see "[AUTOTITLE](/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#quoting-code)." +> +> If the information you provide is unreadable due to the loss of formatting by improper Markdown syntax, {% data variables.contact.github_support %} may request that resubmit the information either as an attachment or with the correct Markdown formatting. -**Note:** Attach files to your support ticket by changing the file's extension to `.txt` or `.zip`. If you include textual data such as log or workflow file snippets inline in your ticket, ensure they are formatted correctly as Markdown code blocks. For more information about proper Markdown formatting, see "[AUTOTITLE](/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#quoting-code)." - -If the information you provide is unreadable due to the loss of formatting by improper Markdown syntax, {% data variables.contact.github_support %} may request that resubmit the information either as an attachment or with the correct Markdown formatting. - -{% endnote %} - -{% warning %} - -**Warning:** Ensure all files and text provided to {% data variables.contact.github_support %} have been properly redacted to remove sensitive information such as tokens and other secrets. - -{% endwarning %} +> [!WARNING] +> Ensure all files and text provided to {% data variables.contact.github_support %} have been properly redacted to remove sensitive information such as tokens and other secrets. {% ifversion ghes %} Depending on the nature of your inquiry, {% data variables.contact.github_support %} may also request that you generate and upload a support bundle for further review and analysis. For more information about providing data to {% data variables.contact.github_support %} and support bundles, see "[AUTOTITLE](/support/contacting-github-support/providing-data-to-github-support)." diff --git a/content/actions/security-for-github-actions/security-guides/automatic-token-authentication.md b/content/actions/security-for-github-actions/security-guides/automatic-token-authentication.md index 791f333c4fc2..4062da830817 100644 --- a/content/actions/security-for-github-actions/security-guides/automatic-token-authentication.md +++ b/content/actions/security-for-github-actions/security-guides/automatic-token-authentication.md @@ -30,11 +30,8 @@ The token is also available in the `github.token` context. For more information, You can use the `GITHUB_TOKEN` by using the standard syntax for referencing secrets: {% raw %}`${{ secrets.GITHUB_TOKEN }}`{% endraw %}. Examples of using the `GITHUB_TOKEN` include passing the token as an input to an action, or using it to make an authenticated {% ifversion fpt or ghec %}{% data variables.product.prodname_dotcom %}{% else %}{% data variables.product.product_name %}{% endif %} API request. -{% note %} - -**Important:** An action can access the `GITHUB_TOKEN` through the `github.token` context even if the workflow does not explicitly pass the `GITHUB_TOKEN` to the action. As a good security practice, you should always make sure that actions only have the minimum access they require by limiting the permissions granted to the `GITHUB_TOKEN`. For more information, see "[Permissions for the `GITHUB_TOKEN`](#permissions-for-the-github_token)." - -{% endnote %} +> [!IMPORTANT] +> An action can access the `GITHUB_TOKEN` through the `github.token` context even if the workflow does not explicitly pass the `GITHUB_TOKEN` to the action. As a good security practice, you should always make sure that actions only have the minimum access they require by limiting the permissions granted to the `GITHUB_TOKEN`. For more information, see "[Permissions for the `GITHUB_TOKEN`](#permissions-for-the-github_token)." {% data reusables.actions.actions-do-not-trigger-workflows %} @@ -104,14 +101,10 @@ The following table shows the permissions granted to the `GITHUB_TOKEN` by defau {% endrowheaders %} -{% note %} - -**Notes:** -* When a workflow is triggered by the [`pull_request_target`](/actions/using-workflows/events-that-trigger-workflows#pull_request_target) event, the `GITHUB_TOKEN` is granted read/write repository permission, even when it is triggered from a public fork. For more information, see "[AUTOTITLE](/actions/using-workflows/events-that-trigger-workflows#pull_request_target)." -* Private repositories can control whether pull requests from forks can run workflows, and can configure the permissions assigned to `GITHUB_TOKEN`. For more information, see "[AUTOTITLE](/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-github-actions-settings-for-a-repository#enabling-workflows-for-forks-of-private-repositories)." -* {% data reusables.actions.workflow-runs-dependabot-note %} - -{% endnote %} +> [!NOTE] +> * When a workflow is triggered by the [`pull_request_target`](/actions/using-workflows/events-that-trigger-workflows#pull_request_target) event, the `GITHUB_TOKEN` is granted read/write repository permission, even when it is triggered from a public fork. For more information, see "[AUTOTITLE](/actions/using-workflows/events-that-trigger-workflows#pull_request_target)." +> * Private repositories can control whether pull requests from forks can run workflows, and can configure the permissions assigned to `GITHUB_TOKEN`. For more information, see "[AUTOTITLE](/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-github-actions-settings-for-a-repository#enabling-workflows-for-forks-of-private-repositories)." +> * {% data reusables.actions.workflow-runs-dependabot-note %} ### Modifying the permissions for the `GITHUB_TOKEN` @@ -127,11 +120,8 @@ The two workflow examples earlier in this article show the `permissions` key bei For full details of the `permissions` key, see "[AUTOTITLE](/actions/using-workflows/workflow-syntax-for-github-actions#permissions)." -{% note %} - -**Note:** Organization{% ifversion not fpt %} and enterprise{% endif %} owners can prevent you from granting write access to the `GITHUB_TOKEN` at the repository level. For more information, see "[AUTOTITLE](/organizations/managing-organization-settings/disabling-or-limiting-github-actions-for-your-organization#setting-the-permissions-of-the-github_token-for-your-organization){% ifversion not fpt %} and "[AUTOTITLE](/admin/policies/enforcing-policies-for-your-enterprise/enforcing-policies-for-github-actions-in-your-enterprise#enforcing-a-policy-for-workflow-permissions-in-your-enterprise)."{% else %}."{% endif %} - -{% endnote %} +> [!NOTE] +> Organization{% ifversion not fpt %} and enterprise{% endif %} owners can prevent you from granting write access to the `GITHUB_TOKEN` at the repository level. For more information, see "[AUTOTITLE](/organizations/managing-organization-settings/disabling-or-limiting-github-actions-for-your-organization#setting-the-permissions-of-the-github_token-for-your-organization){% ifversion not fpt %} and "[AUTOTITLE](/admin/policies/enforcing-policies-for-your-enterprise/enforcing-policies-for-github-actions-in-your-enterprise#enforcing-a-policy-for-workflow-permissions-in-your-enterprise)."{% else %}."{% endif %} #### How the permissions are calculated for a workflow job diff --git a/content/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions.md b/content/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions.md index 7c9530efb940..731482319d13 100644 --- a/content/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions.md +++ b/content/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions.md @@ -46,11 +46,8 @@ To help prevent accidental disclosure, {% data variables.product.product_name %} * **Consider requiring review for access to secrets** * You can use required reviewers to protect environment secrets. A workflow job cannot access environment secrets until approval is granted by a reviewer. For more information about storing secrets in environments or requiring reviews for environments, see "[AUTOTITLE](/actions/security-guides/using-secrets-in-github-actions)" and "[AUTOTITLE](/actions/deployment/targeting-different-environments/managing-environments-for-deployment)." -{% warning %} - -**Warning**: Any user with write access to your repository has read access to all secrets configured in your repository. Therefore, you should ensure that the credentials being used within workflows have the least privileges required. - -{% endwarning %} +> [!WARNING] +> Any user with write access to your repository has read access to all secrets configured in your repository. Therefore, you should ensure that the credentials being used within workflows have the least privileges required. ## Using `CODEOWNERS` to monitor changes @@ -237,11 +234,8 @@ These sections consider some of the steps an attacker can take if they're able t {% ifversion fpt or ghec %} -{% note %} - -**Note:** {% data variables.product.prodname_dotcom %}-hosted runners do not scan for malicious code downloaded by a user during their job, such as a compromised third party library. - -{% endnote %} +> [!NOTE] +> {% data variables.product.prodname_dotcom %}-hosted runners do not scan for malicious code downloaded by a user during their job, such as a compromised third party library. {% endif %} @@ -353,11 +347,8 @@ Some customers might attempt to partially mitigate these risks by implementing s To improve runner registration security, you can use the REST API to create ephemeral, just-in-time (JIT) runners. These self-hosted runners perform at most one job before being automatically removed from the repository, organization, or enterprise. For more information about configuring JIT runners, see "[AUTOTITLE](/rest/actions/self-hosted-runners#create-configuration-for-a-just-in-time-runner-for-an-organization)." -{% note %} - -**Note:** Re-using hardware to host JIT runners can risk exposing information from the environment. Use automation to ensure the JIT runner uses a clean environment. For more information, see "[AUTOTITLE](/actions/hosting-your-own-runners/managing-self-hosted-runners/autoscaling-with-self-hosted-runners#using-ephemeral-runners-for-autoscaling)." - -{% endnote %} +> [!NOTE] +> Re-using hardware to host JIT runners can risk exposing information from the environment. Use automation to ensure the JIT runner uses a clean environment. For more information, see "[AUTOTITLE](/actions/hosting-your-own-runners/managing-self-hosted-runners/autoscaling-with-self-hosted-runners#using-ephemeral-runners-for-autoscaling)." Once you have the config file from the REST API response, you can pass it to the runner at startup. diff --git a/content/actions/security-for-github-actions/security-guides/using-githubs-security-features-to-secure-your-use-of-github-actions.md b/content/actions/security-for-github-actions/security-guides/using-githubs-security-features-to-secure-your-use-of-github-actions.md index ef600c9e54db..335cfcee54b0 100644 --- a/content/actions/security-for-github-actions/security-guides/using-githubs-security-features-to-secure-your-use-of-github-actions.md +++ b/content/actions/security-for-github-actions/security-guides/using-githubs-security-features-to-secure-your-use-of-github-actions.md @@ -43,11 +43,8 @@ You can set up your repositories so that you: You can use {% data variables.product.prodname_dependabot %} to monitor the actions in your workflows and enable {% data variables.product.prodname_dependabot_alerts %} to notify you when an action you use has a reported vulnerability. {% data variables.product.prodname_dependabot %} performs a scan of the default branch of the repositories where it is enabled to detect insecure dependencies. {% data variables.product.prodname_dependabot %} generates {% data variables.product.prodname_dependabot_alerts %} when a new advisory is added to the {% data variables.product.prodname_advisory_database %} or when an action you use is updated. -{% note %} - -**Note:** {% data variables.product.prodname_dependabot %} only creates alerts for vulnerable actions that use semantic versioning and will not create alerts for actions pinned to SHA values. - -{% endnote %} +> [!NOTE] +> {% data variables.product.prodname_dependabot %} only creates alerts for vulnerable actions that use semantic versioning and will not create alerts for actions pinned to SHA values. {% ifversion fpt or ghec %}You can enable {% data variables.product.prodname_dependabot_alerts %} for your personal account, for a repository, or for an organization. For more information, see "[AUTOTITLE](/code-security/dependabot/dependabot-alerts/configuring-dependabot-alerts)."{% else %}An enterprise owner must first set up {% data variables.product.prodname_dependabot %} for your enterprise before you can manage {% data variables.product.prodname_dependabot_alerts %} for your repository. For more information, see "[AUTOTITLE](/admin/configuration/configuring-github-connect/enabling-dependabot-for-your-enterprise)."{% endif %} @@ -76,13 +73,8 @@ The following features can automatically update the actions in your workflows. * **{% data variables.product.prodname_dependabot_version_updates %}** open pull requests to update actions to the latest version when a new version is released. * **{% data variables.product.prodname_dependabot_security_updates %}** open pull requests to update actions with reported vulnerabilities to the minimum patched version. -{% note %} - -**Notes:** - -{% data reusables.actions.dependabot-version-updates-actions-caveats %} - -{% endnote %} +> [!NOTE] +> {% data reusables.actions.dependabot-version-updates-actions-caveats %} For information on how to configure {% data variables.product.prodname_dependabot_version_updates %}, see "[AUTOTITLE](/code-security/dependabot/dependabot-version-updates/configuring-dependabot-version-updates)." diff --git a/content/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions.md b/content/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions.md index b5f48a3b47c2..a121cf3b279a 100644 --- a/content/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions.md +++ b/content/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions.md @@ -27,11 +27,8 @@ Secrets are variables that you create in an organization, repository, or reposit For secrets stored at the environment level, you can enable required reviewers to control access to the secrets. A workflow job cannot access environment secrets until approval is granted by required approvers. -{% note %} - -**Note**: {% data reusables.actions.about-oidc-short-overview %} - -{% endnote %} +> [!NOTE] +> {% data reusables.actions.about-oidc-short-overview %} ### Naming your secrets @@ -65,11 +62,8 @@ When generating a {% data variables.product.pat_v1 %}, select the fewest scopes Instead of using a {% data variables.product.pat_generic %}, consider using a {% data variables.product.prodname_github_app %}, which uses fine-grained permissions and short lived tokens, similar to a {% data variables.product.pat_v2 %}. Unlike a {% data variables.product.pat_generic %}, a {% data variables.product.prodname_github_app %} is not tied to a user, so the workflow will continue to work even if the user who installed the app leaves your organization. For more information, see "[AUTOTITLE](/apps/creating-github-apps/guides/making-authenticated-api-requests-with-a-github-app-in-a-github-actions-workflow)." -{% note %} - -**Note:** Users with collaborator access to a repository can use the REST API to manage secrets for that repository, and users with admin access to an organization can use the REST API to manage secrets for that organization. For more information, see "[AUTOTITLE](/rest/actions/secrets)." - -{% endnote %} +> [!NOTE] +> Users with collaborator access to a repository can use the REST API to manage secrets for that repository, and users with admin access to an organization can use the REST API to manage secrets for that organization. For more information, see "[AUTOTITLE](/rest/actions/secrets)." ## Creating secrets for a repository @@ -171,15 +165,12 @@ gh secret list --env ENV_NAME {% cli %} -{% note %} - -**Note:** By default, {% data variables.product.prodname_cli %} authenticates with the `repo` and `read:org` scopes. To manage organization secrets, you must additionally authorize the `admin:org` scope. - -```shell -gh auth login --scopes "admin:org" -``` - -{% endnote %} +> [!NOTE] +> By default, {% data variables.product.prodname_cli %} authenticates with the `repo` and `read:org` scopes. To manage organization secrets, you must additionally authorize the `admin:org` scope. +> +> ```shell +> gh auth login --scopes "admin:org" +> ``` To add a secret for an organization, use the `gh secret set` subcommand with the `--org` or `-o` flag followed by the organization name. @@ -218,15 +209,9 @@ You can check which access policies are being applied to a secret in your organi ## Using secrets in a workflow -{% note %} - -**Notes:** - -* {% data reusables.actions.forked-secrets %} - -* Secrets are not automatically passed to reusable workflows. For more information, see "[AUTOTITLE](/actions/using-workflows/reusing-workflows#passing-inputs-and-secrets-to-a-reusable-workflow)." - -{% endnote %} +> [!NOTE] +> * {% data reusables.actions.forked-secrets %} +> * Secrets are not automatically passed to reusable workflows. For more information, see "[AUTOTITLE](/actions/using-workflows/reusing-workflows#passing-inputs-and-secrets-to-a-reusable-workflow)." To provide an action with a secret as an input or environment variable, you can use the `secrets` context to access secrets you've created in your repository. For more information, see "[AUTOTITLE](/actions/learn-github-actions/contexts)" and "[AUTOTITLE](/actions/using-workflows/workflow-syntax-for-github-actions)." @@ -312,11 +297,8 @@ Secrets are limited to 48 KB in size. To store larger secrets, see the "[Storing To use secrets that are larger than 48 KB, you can use a workaround to store secrets in your repository and save the decryption passphrase as a secret on {% data variables.product.prodname_dotcom %}. For example, you can use `gpg` to encrypt a file containing your secret locally before checking the encrypted file in to your repository on {% data variables.product.prodname_dotcom %}. For more information, see the "[gpg manpage](https://www.gnupg.org/gph/de/manual/r1023.html)." -{% warning %} - -**Warning**: Be careful that your secrets do not get printed when your workflow runs. When using this workaround, {% data variables.product.prodname_dotcom %} does not redact secrets that are printed in logs. - -{% endwarning %} +> [!WARNING] +> Be careful that your secrets do not get printed when your workflow runs. When using this workaround, {% data variables.product.prodname_dotcom %} does not redact secrets that are printed in logs. 1. Run the following command from your terminal to encrypt the file containing your secret using `gpg` and the AES256 cipher algorithm. In this example, `my_secret.json` is the file containing the secret. @@ -330,11 +312,8 @@ To use secrets that are larger than 48 KB, you can use a workaround to store sec 1. Copy your encrypted file to a path in your repository and commit it. In this example, the encrypted file is `my_secret.json.gpg`. - {% warning %} - - **Warning**: Make sure to copy the encrypted `my_secret.json.gpg` file ending with the `.gpg` file extension, and **not** the unencrypted `my_secret.json` file. - - {% endwarning %} + > [!WARNING] + > Make sure to copy the encrypted `my_secret.json.gpg` file ending with the `.gpg` file extension, and **not** the unencrypted `my_secret.json` file. ```shell git add my_secret.json.gpg @@ -391,11 +370,8 @@ To use secrets that are larger than 48 KB, you can use a workaround to store sec You can use Base64 encoding to store small binary blobs as secrets. You can then reference the secret in your workflow and decode it for use on the runner. For the size limits, see "[AUTOTITLE](/actions/security-guides/using-secrets-in-github-actions#limits-for-secrets)." -{% note %} - -**Note**: Note that Base64 only converts binary to text, and is not a substitute for actual encryption. - -{% endnote %} +> [!NOTE] +> Note that Base64 only converts binary to text, and is not a substitute for actual encryption. 1. Use `base64` to encode your file into a Base64 string. For example: @@ -440,11 +416,8 @@ You can use Base64 encoding to store small binary blobs as secrets. You can then openssl x509 -in cert.der -inform DER -text -noout ``` -{% note %} - -**Note**: Using another shell might require different commands for decoding the secret to a file. On Windows runners, we recommend [using a bash shell](/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsshell) with `shell: bash` to use the commands in the `run` step above. - -{% endnote %} +> [!NOTE] +> Using another shell might require different commands for decoding the secret to a file. On Windows runners, we recommend [using a bash shell](/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsshell) with `shell: bash` to use the commands in the `run` step above. ## Redacting secrets from workflow run logs diff --git a/content/actions/security-for-github-actions/security-hardening-your-deployments/about-security-hardening-with-openid-connect.md b/content/actions/security-for-github-actions/security-hardening-your-deployments/about-security-hardening-with-openid-connect.md index 5e948e5730bc..fe7842c1a013 100644 --- a/content/actions/security-for-github-actions/security-hardening-your-deployments/about-security-hardening-with-openid-connect.md +++ b/content/actions/security-for-github-actions/security-hardening-your-deployments/about-security-hardening-with-openid-connect.md @@ -166,11 +166,8 @@ If you need more granular trust conditions, you can customize the {% ifversion g There are also many additional claims supported in the OIDC token that can be used for setting these conditions. In addition, your cloud provider could allow you to assign a role to the access tokens, letting you specify even more granular permissions. -{% note %} - -**Note**: To control how your cloud provider issues access tokens, you **must** define at least one condition, so that untrusted repositories can’t request access tokens for your cloud resources. - -{% endnote %} +> [!NOTE] +> To control how your cloud provider issues access tokens, you **must** define at least one condition, so that untrusted repositories can’t request access tokens for your cloud resources. ### Example subject claims @@ -294,19 +291,13 @@ After this setting is applied, the JWT will contain the updated `iss` value. In To help improve security, compliance, and standardization, you can customize the standard claims to suit your required access conditions. If your cloud provider supports conditions on subject claims, you can create a condition that checks whether the `sub` value matches the path of the reusable workflow, such as `"job_workflow_ref:octo-org/octo-automation/.github/workflows/oidc.yml@refs/heads/main"`. The exact format will vary depending on your cloud provider's OIDC configuration. To configure the matching condition on {% data variables.product.prodname_dotcom %}, you can can use the REST API to require that the `sub` claim must always include a specific custom claim, such as `job_workflow_ref`. You can use the REST API to apply a customization template for the OIDC subject claim; for example, you can require that the `sub` claim within the OIDC token must always include a specific custom claim, such as `job_workflow_ref`. For more information, see "[AUTOTITLE](/rest/actions/oidc)." -{% note %} - -**Note**: When the organization template is applied, it will not affect any workflows already using OIDC unless their repository has opted in to custom organization templates. For all repositories, existing and new, the repository owner will need to use the repository-level REST API to opt in to receive this configuration by setting `use_default` to `false`. Alternatively, the repository owner could use the REST API to apply a different configuration specific to the repository. For more information, see "[AUTOTITLE](/rest/actions/oidc#set-the-customization-template-for-an-oidc-subject-claim-for-a-repository)." - -{% endnote %} +> [!NOTE] +> When the organization template is applied, it will not affect any workflows already using OIDC unless their repository has opted in to custom organization templates. For all repositories, existing and new, the repository owner will need to use the repository-level REST API to opt in to receive this configuration by setting `use_default` to `false`. Alternatively, the repository owner could use the REST API to apply a different configuration specific to the repository. For more information, see "[AUTOTITLE](/rest/actions/oidc#set-the-customization-template-for-an-oidc-subject-claim-for-a-repository)." Customizing the claims results in a new format for the entire `sub` claim, which replaces the default predefined `sub` format in the token described in "[AUTOTITLE](/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#example-subject-claims)." -{% note %} - -**Note**: The `sub` claim uses the shortened form `repo` (for example, `repo:ORG-NAME/REPO-NAME`) instead of `repository` to reference the repository. - -{% endnote %} +> [!NOTE] +> The `sub` claim uses the shortened form `repo` (for example, `repo:ORG-NAME/REPO-NAME`) instead of `repository` to reference the repository. The following example templates demonstrate various ways to customize the subject claim. To configure these settings on {% data variables.product.prodname_dotcom %}, admins use the REST API to specify a list of claims that must be included in the subject (`sub`) claim. diff --git a/content/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services.md b/content/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services.md index 67b9380663bc..bfcf57d73a54 100644 --- a/content/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services.md +++ b/content/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services.md @@ -34,16 +34,13 @@ This guide explains how to configure AWS to trust {% data variables.product.prod {% ifversion ghes %} {% data reusables.actions.oidc-endpoints %} - {% note %} - **Note:** You can restrict access to the OIDC endpoints by allowing only [AWS IP address ranges](https://docs.aws.amazon.com/vpc/latest/userguide/aws-ip-ranges.html). + > [!NOTE] + > You can restrict access to the OIDC endpoints by allowing only [AWS IP address ranges](https://docs.aws.amazon.com/vpc/latest/userguide/aws-ip-ranges.html). - {% endnote %} - {% note %} + > [!NOTE] + > {% data variables.product.prodname_dotcom %} does not natively support AWS session tags. - **Note:** {% data variables.product.prodname_dotcom %} does not natively support AWS session tags. - - {% endnote %} {% endif %} ## Adding the identity provider to AWS @@ -57,11 +54,8 @@ To add the {% data variables.product.prodname_dotcom %} OIDC provider to IAM, se To configure the role and trust in IAM, see the AWS documentation "[Configure AWS Credentials for GitHub Actions](https://github.com/aws-actions/configure-aws-credentials#configure-aws-credentials-for-github-actions)" and "[Configuring a role for GitHub OIDC identity provider](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-idp_oidc.html#idp_oidc_Create_GitHub)." -{% note %} - -**Note**: AWS Identity and Access Management (IAM) recommends that users evaluate the IAM condition key, `token.actions.githubusercontent.com:sub`, in the trust policy of any role that trusts {% data variables.product.prodname_dotcom %}’s OIDC identity provider (IdP). Evaluating this condition key in the role trust policy limits which {% data variables.product.prodname_dotcom %} actions are able to assume the role. - -{% endnote %} +> [!NOTE] +> AWS Identity and Access Management (IAM) recommends that users evaluate the IAM condition key, `token.actions.githubusercontent.com:sub`, in the trust policy of any role that trusts {% data variables.product.prodname_dotcom %}’s OIDC identity provider (IdP). Evaluating this condition key in the role trust policy limits which {% data variables.product.prodname_dotcom %} actions are able to assume the role. Edit the trust policy, adding the `sub` field to the validation conditions. For example: diff --git a/content/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-azure.md b/content/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-azure.md index c17355077ce7..7db66568c528 100644 --- a/content/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-azure.md +++ b/content/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-azure.md @@ -32,11 +32,9 @@ This guide gives an overview of how to configure Azure to trust {% data variable {% ifversion ghes %} {% data reusables.actions.oidc-endpoints %} - {% note %} - **Note:** Microsoft Entra ID (previously known as Azure AD) does not have fixed IP ranges defined for these endpoints. - - {% endnote %} + > [!NOTE] + > Microsoft Entra ID (previously known as Azure AD) does not have fixed IP ranges defined for these endpoints. * Make sure that the value of the issuer claim that's included with the JSON Web Token (JWT) is set to a publicly routable URL. For more information, see "[AUTOTITLE](/enterprise-server@latest/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)." {% endif %} diff --git a/content/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-hashicorp-vault.md b/content/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-hashicorp-vault.md index 27569033f54f..350d8d4afeed 100644 --- a/content/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-hashicorp-vault.md +++ b/content/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-hashicorp-vault.md @@ -49,11 +49,10 @@ To configure your Vault server to accept JSON Web Tokens (JWT) for authenticatio ``` {% ifversion ghec %} - {% note %} - **Note:** If a unique issuer URL for an enterprise was set using the REST API (as described in "[AUTOTITLE](/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#switching-to-a-unique-token-url)"), the values for `bound_issuer` and `oidc_discover_url` must match that unique URL. For example, for an enterprise named `octocat` that uses the unique issuer URL, `bound_issuer` and `oidc_discovery_url` must be set to `https://token.actions.githubusercontent.com/octocat`. + > [!NOTE] + > If a unique issuer URL for an enterprise was set using the REST API (as described in "[AUTOTITLE](/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#switching-to-a-unique-token-url)"), the values for `bound_issuer` and `oidc_discover_url` must match that unique URL. For example, for an enterprise named `octocat` that uses the unique issuer URL, `bound_issuer` and `oidc_discovery_url` must be set to `https://token.actions.githubusercontent.com/octocat`. - {% endnote %} {% endif %} 1. Configure a policy that only grants access to the specific paths your workflows will use to retrieve secrets. For more advanced policies, see the HashiCorp Vault [Policies documentation](https://www.vaultproject.io/docs/concepts/policies). @@ -112,13 +111,8 @@ This example demonstrates how to use OIDC with the official action to request a {% data reusables.actions.oidc-permissions-token %} -{% note %} - -**Note**: - -When the `permissions` key is used, all unspecified permissions are set to _no access_, with the exception of the metadata scope, which always gets _read_ access. As a result, you may need to add other permissions, such as `contents: read`. See [Automatic token authentication](/actions/security-guides/automatic-token-authentication) for more information. - -{% endnote %} +> [!NOTE] +> When the `permissions` key is used, all unspecified permissions are set to _no access_, with the exception of the metadata scope, which always gets _read_ access. As a result, you may need to add other permissions, such as `contents: read`. See [Automatic token authentication](/actions/security-guides/automatic-token-authentication) for more information. ### Requesting the access token @@ -153,14 +147,9 @@ jobs: # This step has access to the secret retrieved above; see hashicorp/vault-action for more details. ``` -{% note %} - -**Note**: - -* If your Vault server is not accessible from the public network, consider using a self-hosted runner with other available Vault [auth methods](https://www.vaultproject.io/docs/auth). For more information, see "[AUTOTITLE](/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners)." -* `VAULT-NAMESPACE` must be set for a Vault Enterprise (including HCP Vault) deployment. For more information, see [Vault namespace](https://www.vaultproject.io/docs/enterprise/namespaces). - -{% endnote %} +> [!NOTE] +> * If your Vault server is not accessible from the public network, consider using a self-hosted runner with other available Vault [auth methods](https://www.vaultproject.io/docs/auth). For more information, see "[AUTOTITLE](/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners)." +> * `VAULT-NAMESPACE` must be set for a Vault Enterprise (including HCP Vault) deployment. For more information, see [Vault namespace](https://www.vaultproject.io/docs/enterprise/namespaces). ### Revoking the access token diff --git a/content/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-pypi.md b/content/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-pypi.md index 17cc80c42ba6..b41dd32b67b3 100644 --- a/content/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-pypi.md +++ b/content/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-pypi.md @@ -35,12 +35,8 @@ To use OIDC with PyPI, add a trust configuration that links each project on PyPI 1. Configure a trust relationship between the PyPI project and a {% data variables.product.prodname_dotcom %} repository (and workflow within the repository). For example, if your {% data variables.product.prodname_dotcom %} repository is at `myorg/myproject` and your release workflow is defined in `release.yml` with an environment of `release`, you should use the following settings for your trusted publisher on PyPI. - {% note %} - - **Note:** Enter these values carefully. Giving the incorrect user, repository, or workflow - the ability to publish to your PyPI project is equivalent to sharing an API token. - - {% endnote %} + > [!NOTE] + > Enter these values carefully. Giving the incorrect user, repository, or workflow the ability to publish to your PyPI project is equivalent to sharing an API token. * Owner: `myorg` * Repository name: `myproject` diff --git a/content/actions/sharing-automations/creating-actions/metadata-syntax-for-github-actions.md b/content/actions/sharing-automations/creating-actions/metadata-syntax-for-github-actions.md index 532e46c1d6d8..a63f0591c252 100644 --- a/content/actions/sharing-automations/creating-actions/metadata-syntax-for-github-actions.md +++ b/content/actions/sharing-automations/creating-actions/metadata-syntax-for-github-actions.md @@ -43,11 +43,8 @@ Action metadata files use YAML syntax. If you're new to YAML, you can read "[Lea This example configures two inputs: `num-octocats` and `octocat-eye-color`. The `num-octocats` input is not required and will default to a value of `1`. `octocat-eye-color` is required and has no default value. -{% note %} - -**Note:** Actions using `required: true` will not automatically return an error if the input is not specified. - -{% endnote %} +> [!NOTE] +> Actions using `required: true` will not automatically return an error if the input is not specified. Workflow files that use this action can use the `with` keyword to set an input value for `octocat-eye-color`. For more information about the `with` syntax, see "[AUTOTITLE](/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepswith)." diff --git a/content/actions/sharing-automations/creating-actions/publishing-actions-in-github-marketplace.md b/content/actions/sharing-automations/creating-actions/publishing-actions-in-github-marketplace.md index 201f777fabd1..1702f56d67b4 100644 --- a/content/actions/sharing-automations/creating-actions/publishing-actions-in-github-marketplace.md +++ b/content/actions/sharing-automations/creating-actions/publishing-actions-in-github-marketplace.md @@ -43,11 +43,9 @@ To draft a new release and publish the action to {% data variables.product.prodn 1. Navigate to the action metadata file in your repository (`action.yml` or `action.yaml`), and you'll see a banner to publish the action to {% data variables.product.prodname_marketplace %}. Click **Draft a release**. 1. Under "Release Action", select **Publish this Action to the {% data variables.product.prodname_marketplace %}**. - {% note %} + > [!NOTE] + > The "Publish" checkbox is disabled if the account that owns the repository has not yet accepted the {% data variables.product.prodname_marketplace %} Developer Agreement. If you own the repository or are an organization owner, click the link to "accept the GitHub Marketplace Developer Agreement", then accept the agreement. If there is no link, send the organization owner a link to this "Release Action" page and ask them to accept the agreement. - **Note**: The "Publish" checkbox is disabled if the account that owns the repository has not yet accepted the {% data variables.product.prodname_marketplace %} Developer Agreement. If you own the repository or are an organization owner, click the link to "accept the GitHub Marketplace Developer Agreement", then accept the agreement. If there is no link, send the organization owner a link to this "Release Action" page and ask them to accept the agreement. - - {% endnote %} 1. If the labels in your metadata file contain any problems, you will see an error message. Address them by updating your metadata file. Once complete, you will see an "Everything looks good!" message. 1. Select the **Primary Category** dropdown menu and click a category that will help people find your action in {% data variables.product.prodname_marketplace %}. 1. Optionally, select the **Another Category** dropdown menu and click a secondary category. @@ -73,12 +71,9 @@ When a repository admin transfers an action repository, {% data variables.produc Actions published on {% data variables.product.prodname_marketplace %} are linked to a repository by their unique `name` identifier, meaning you can publish new releases of an action from the transferred repository under the same {% data variables.product.prodname_marketplace %} listing. If an action repository is deleted, the {% data variables.product.prodname_marketplace %} listing is also deleted, and the unique `name` identifier becomes available. -{% note %} - -**Note:** The "Verified" badge seen on an organization's {% data variables.product.prodname_dotcom %} profile is different from the verified creator badge on {% data variables.product.prodname_marketplace %}. If you transfer an action repository, the {% data variables.product.prodname_marketplace %} listing will lose the verified creator badge unless the new owner is also a verified creator. +> [!NOTE] +> The "Verified" badge seen on an organization's {% data variables.product.prodname_dotcom %} profile is different from the verified creator badge on {% data variables.product.prodname_marketplace %}. If you transfer an action repository, the {% data variables.product.prodname_marketplace %} listing will lose the verified creator badge unless the new owner is also a verified creator. -{% endnote %} - ## About badges in {% data variables.product.prodname_marketplace %} Actions with the {% octicon "verified" aria-label="The verified badge" %}, or verified creator badge, indicate that {% data variables.product.prodname_dotcom %} has verified the creator of the action as a partner organization. Partners can email partnerships@github.com to request the verified creator badge. diff --git a/content/actions/sharing-automations/creating-workflow-templates-for-your-organization.md b/content/actions/sharing-automations/creating-workflow-templates-for-your-organization.md index daf71b8009d5..2cc1ded6de84 100644 --- a/content/actions/sharing-automations/creating-workflow-templates-for-your-organization.md +++ b/content/actions/sharing-automations/creating-workflow-templates-for-your-organization.md @@ -36,11 +36,8 @@ Workflow templates can be created by users with write access to the organization Workflow templates created by users can only be used to create workflows in public repositories. Organizations using {% data variables.product.prodname_ghe_cloud %} can also use workflow templates to create workflows in private repositories. For more information, see the [{% data variables.product.prodname_ghe_cloud %} documentation](/enterprise-cloud@latest/actions/using-workflows/creating-starter-workflows-for-your-organization). {% endif %} -{% note %} - -**Note:** To avoid duplication among workflow templates you can call reusable workflows from within a workflow. This can help make your workflows easier to maintain. For more information, see "[AUTOTITLE](/actions/using-workflows/reusing-workflows)." - -{% endnote %} +> [!NOTE] +> To avoid duplication among workflow templates you can call reusable workflows from within a workflow. This can help make your workflows easier to maintain. For more information, see "[AUTOTITLE](/actions/using-workflows/reusing-workflows)." This procedure demonstrates how to create a workflow template and metadata file. The metadata file describes how the workflow templates will be presented to users when they are creating a new workflow. @@ -51,15 +48,15 @@ This procedure demonstrates how to create a workflow template and metadata file. If you need to refer to a repository's default branch, you can use the `$default-branch` placeholder. When a workflow is created the placeholder will be automatically replaced with the name of the repository's default branch. {% ifversion ghes %} - {% note %} - - **Note:** The following values in the `runs-on` key are also treated as placeholders: - * "ubuntu-latest" is replaced with "[ self-hosted ]" - * "windows-latest" is replaced with "[ self-hosted, windows ]" - * "macos-latest" is replaced with "[ self-hosted, macOS ]" + > [!NOTE] + > The following values in the `runs-on` key are also treated as placeholders: + > + > * "ubuntu-latest" is replaced with "[ self-hosted ]" + > * "windows-latest" is replaced with "[ self-hosted, windows ]" + > * "macos-latest" is replaced with "[ self-hosted, macOS ]" - {% endnote %}{% endif %} + {% endif %} For example, this file named `octo-organization-ci.yml` demonstrates a basic workflow. diff --git a/content/actions/sharing-automations/reusing-workflows.md b/content/actions/sharing-automations/reusing-workflows.md index e8da4aa74278..4eb8e735f886 100644 --- a/content/actions/sharing-automations/reusing-workflows.md +++ b/content/actions/sharing-automations/reusing-workflows.md @@ -145,11 +145,9 @@ You can define inputs and secrets, which can be passed from the caller workflow {% ifversion actions-inherit-secrets-reusable-workflows %} 1. In the reusable workflow, reference the input or secret that you defined in the `on` key in the previous step. - {% note %} + > [!NOTE] + > If the secrets are inherited by using `secrets: inherit` in the calling workflow, you can reference them even if they are not explicitly defined in the `on` key. For more information, see "[AUTOTITLE](/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idsecretsinherit)." - **Note**: If the secrets are inherited by using `secrets: inherit` in the calling workflow, you can reference them even if they are not explicitly defined in the `on` key. For more information, see "[AUTOTITLE](/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idsecretsinherit)." - - {% endnote %} {%- else %} 1. In the reusable workflow, reference the input or secret that you defined in the `on` key in the previous step. {%- endif %} @@ -171,11 +169,8 @@ You can define inputs and secrets, which can be passed from the caller workflow In the example above, `personal_access_token` is a secret that's defined at the repository or organization level. - {% warning %} - - **Warning**: Environment secrets cannot be passed from the caller workflow as `on.workflow_call` does not support the `environment` keyword. If you include `environment` in the reusable workflow at the job level, the environment secret will be used, and not the secret passed from the caller workflow. For more information, see "[AUTOTITLE](/actions/deployment/targeting-different-environments/managing-environments-for-deployment#environment-secrets)" and "[AUTOTITLE](/actions/writing-workflows/workflow-syntax-for-github-actions#onworkflow_call)". - - {% endwarning %} + > [!WARNING] + > Environment secrets cannot be passed from the caller workflow as `on.workflow_call` does not support the `environment` keyword. If you include `environment` in the reusable workflow at the job level, the environment secret will be used, and not the secret passed from the caller workflow. For more information, see "[AUTOTITLE](/actions/deployment/targeting-different-environments/managing-environments-for-deployment#environment-secrets)" and "[AUTOTITLE](/actions/writing-workflows/workflow-syntax-for-github-actions#onworkflow_call)". 1. Pass the input or secret from the caller workflow. @@ -277,15 +272,11 @@ When you call a reusable workflow, you can only use the following keywords in th * [`jobs..concurrency`](/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idconcurrency) * [`jobs..permissions`](/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idpermissions) - {% note %} - - **Note:** - - * If `jobs..permissions` is not specified in the calling job, the called workflow will have the default permissions for the `GITHUB_TOKEN`. For more information, see "[AUTOTITLE](/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token)." - * The `GITHUB_TOKEN` permissions passed from the caller workflow can be only downgraded (not elevated) by the called workflow. - * If you use `jobs..concurrency.cancel-in-progress: true`, don't use the same value for `jobs..concurrency.group` in the called and caller workflows as this will cause the workflow that's already running to be cancelled. A called workflow uses the name of its caller workflow in {% raw %}${{ github.workflow }}{% endraw %}, so using this context as the value of `jobs..concurrency.group` in both caller and called workflows will cause the caller workflow to be cancelled when the called workflow runs. - - {% endnote %} + > [!NOTE] + > + > * If `jobs..permissions` is not specified in the calling job, the called workflow will have the default permissions for the `GITHUB_TOKEN`. For more information, see "[AUTOTITLE](/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token)." + > * The `GITHUB_TOKEN` permissions passed from the caller workflow can be only downgraded (not elevated) by the called workflow. + > * If you use `jobs..concurrency.cancel-in-progress: true`, don't use the same value for `jobs..concurrency.group` in the called and caller workflows as this will cause the workflow that's already running to be cancelled. A called workflow uses the name of its caller workflow in {% raw %}${{ github.workflow }}{% endraw %}, so using this context as the value of `jobs..concurrency.group` in both caller and called workflows will cause the caller workflow to be cancelled when the called workflow runs. ### Example caller workflow @@ -459,11 +450,8 @@ You can use the {% data variables.product.prodname_dotcom %} REST API to monitor For information about using the REST API to query the audit log for an organization, see "[AUTOTITLE](/rest/orgs#get-the-audit-log-for-an-organization)." -{% note %} - -**Note**: Audit data for `prepared_workflow_job` can only be viewed using the REST API. It is not visible in the {% data variables.product.prodname_dotcom %} web interface, or included in JSON/CSV exported audit data. - -{% endnote %} +> [!NOTE] +> Audit data for `prepared_workflow_job` can only be viewed using the REST API. It is not visible in the {% data variables.product.prodname_dotcom %} web interface, or included in JSON/CSV exported audit data. {% ifversion partial-reruns-with-reusable %} diff --git a/content/actions/sharing-automations/sharing-actions-and-workflows-from-your-private-repository.md b/content/actions/sharing-automations/sharing-actions-and-workflows-from-your-private-repository.md index a615d902befc..71ebdbdc6f7c 100644 --- a/content/actions/sharing-automations/sharing-actions-and-workflows-from-your-private-repository.md +++ b/content/actions/sharing-automations/sharing-actions-and-workflows-from-your-private-repository.md @@ -18,13 +18,9 @@ You can share actions and reusable workflows from your private repository, witho Any actions or reusable workflows stored in the private repository can be used in workflows defined in other private repositories owned by the same organization or user. Actions and reusable workflows stored in private repositories cannot be used in public repositories. -{% warning %} - -**Warning**: -* If you make a private repository accessible to {% data variables.product.prodname_actions %} workflows in other repositories, outside collaborators on the other repositories can indirectly access the private repository, even though they do not have direct access to these repositories. The outside collaborators can view logs for workflow runs when actions or workflows from the private repository are used. -* {% data reusables.actions.scoped-token-note %} - -{% endwarning %} +> [!WARNING] +> * If you make a private repository accessible to {% data variables.product.prodname_actions %} workflows in other repositories, outside collaborators on the other repositories can indirectly access the private repository, even though they do not have direct access to these repositories. The outside collaborators can view logs for workflow runs when actions or workflows from the private repository are used. +> * {% data reusables.actions.scoped-token-note %} ## Sharing actions and workflows from your private repository diff --git a/content/actions/sharing-automations/sharing-actions-and-workflows-with-your-enterprise.md b/content/actions/sharing-automations/sharing-actions-and-workflows-with-your-enterprise.md index e947c13c9c1e..bfd1c586083a 100644 --- a/content/actions/sharing-automations/sharing-actions-and-workflows-with-your-enterprise.md +++ b/content/actions/sharing-automations/sharing-actions-and-workflows-with-your-enterprise.md @@ -18,13 +18,9 @@ If your organization is owned by an enterprise account, you can share actions an Any actions or reusable workflows stored in the internal {% ifversion private-actions %}or private {% endif %}repository can be used in workflows defined in other internal or private repositories owned by the same organization, or by any organization owned by the enterprise. Actions and reusable workflows stored in internal repositories cannot be used in public repositories {% ifversion private-actions %}and actions and reusable workflows stored in private repositories cannot be used in public or internal repositories{% endif %}. -{% warning %} - -**Warning**: -* {% data reusables.actions.outside-collaborators-actions %} -* {% data reusables.actions.scoped-token-note %} - -{% endwarning %} +> [!WARNING] +> * {% data reusables.actions.outside-collaborators-actions %} +> * {% data reusables.actions.scoped-token-note %} ## Sharing actions and workflows with your enterprise diff --git a/content/actions/sharing-automations/sharing-actions-and-workflows-with-your-organization.md b/content/actions/sharing-automations/sharing-actions-and-workflows-with-your-organization.md index cc0afa7196e4..2b367e32b741 100644 --- a/content/actions/sharing-automations/sharing-actions-and-workflows-with-your-organization.md +++ b/content/actions/sharing-automations/sharing-actions-and-workflows-with-your-organization.md @@ -18,13 +18,9 @@ You can share actions and reusable workflows within your organization, without p Any actions or reusable workflows stored in the private repository can be used in workflows defined in other private repositories owned by the same organization. Actions and reusable workflows stored in private repositories cannot be used in public repositories. -{% warning %} - -**Warning**: -* {% data reusables.actions.outside-collaborators-actions %} -* {% data reusables.actions.scoped-token-note %} - -{% endwarning %} +> [!WARNING] +> * {% data reusables.actions.outside-collaborators-actions %} +> * {% data reusables.actions.scoped-token-note %} ## Sharing actions and workflows with your organization diff --git a/content/actions/use-cases-and-examples/building-and-testing/building-and-testing-net.md b/content/actions/use-cases-and-examples/building-and-testing/building-and-testing-net.md index 98fe86a22189..4a917a2b237a 100644 --- a/content/actions/use-cases-and-examples/building-and-testing/building-and-testing-net.md +++ b/content/actions/use-cases-and-examples/building-and-testing/building-and-testing-net.md @@ -154,11 +154,8 @@ steps: run: dotnet add package Newtonsoft.Json --version 12.0.1 ``` -{% note %} - -**Note:** Depending on the number of dependencies, it may be faster to use the dependency cache. Projects with many large dependencies should see a performance increase as it cuts down the time required for downloading. Projects with fewer dependencies may not see a significant performance increase and may even see a slight decrease due to how NuGet installs cached dependencies. The performance varies from project to project. - -{% endnote %} +> [!NOTE] +> Depending on the number of dependencies, it may be faster to use the dependency cache. Projects with many large dependencies should see a performance increase as it cuts down the time required for downloading. Projects with fewer dependencies may not see a significant performance increase and may even see a slight decrease due to how NuGet installs cached dependencies. The performance varies from project to project. ## Building and testing your code diff --git a/content/actions/use-cases-and-examples/building-and-testing/building-and-testing-powershell.md b/content/actions/use-cases-and-examples/building-and-testing/building-and-testing-powershell.md index f3244b084dec..0971b7a533d2 100644 --- a/content/actions/use-cases-and-examples/building-and-testing/building-and-testing-powershell.md +++ b/content/actions/use-cases-and-examples/building-and-testing/building-and-testing-powershell.md @@ -93,21 +93,15 @@ The table below describes the locations for various PowerShell modules in each { {% endrowheaders %} -{% note %} - -**Note:** On Ubuntu runners, Azure PowerShell modules are stored in `/usr/share/` instead of the default location of PowerShell add-on modules (i.e. `/usr/local/share/powershell/Modules/`). - -{% endnote %} +> [!NOTE] +> On Ubuntu runners, Azure PowerShell modules are stored in `/usr/share/` instead of the default location of PowerShell add-on modules (i.e. `/usr/local/share/powershell/Modules/`). ## Installing dependencies {% data variables.product.prodname_dotcom %}-hosted runners have PowerShell 7 and Pester installed. You can use `Install-Module` to install additional dependencies from the PowerShell Gallery before building and testing your code. -{% note %} - -**Note:** The pre-installed packages (such as Pester) used by {% data variables.product.prodname_dotcom %}-hosted runners are regularly updated, and can introduce significant changes. As a result, it is recommended that you always specify the required package versions by using `Install-Module` with `-MaximumVersion`. - -{% endnote %} +> [!NOTE] +> The pre-installed packages (such as Pester) used by {% data variables.product.prodname_dotcom %}-hosted runners are regularly updated, and can introduce significant changes. As a result, it is recommended that you always specify the required package versions by using `Install-Module` with `-MaximumVersion`. You can also cache dependencies to speed up your workflow. For more information, see "[AUTOTITLE](/actions/using-workflows/caching-dependencies-to-speed-up-workflows)." @@ -127,11 +121,8 @@ jobs: Install-Module SqlServer, PSScriptAnalyzer ``` -{% note %} - -**Note:** By default, no repositories are trusted by PowerShell. When installing modules from the PowerShell Gallery, you must explicitly set the installation policy for `PSGallery` to `Trusted`. - -{% endnote %} +> [!NOTE] +> By default, no repositories are trusted by PowerShell. When installing modules from the PowerShell Gallery, you must explicitly set the installation policy for `PSGallery` to `Trusted`. ### Caching dependencies diff --git a/content/actions/use-cases-and-examples/deploying/deploying-docker-to-azure-app-service.md b/content/actions/use-cases-and-examples/deploying/deploying-docker-to-azure-app-service.md index 46f3428b2a4b..221a655fed16 100644 --- a/content/actions/use-cases-and-examples/deploying/deploying-docker-to-azure-app-service.md +++ b/content/actions/use-cases-and-examples/deploying/deploying-docker-to-azure-app-service.md @@ -21,11 +21,8 @@ redirect_from: This guide explains how to use {% data variables.product.prodname_actions %} to build and deploy a Docker container to [Azure App Service](https://azure.microsoft.com/services/app-service/). -{% note %} - -**Note**: {% data reusables.actions.about-oidc-short-overview %} and "[AUTOTITLE](/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-azure)." - -{% endnote %} +> [!NOTE] +> {% data reusables.actions.about-oidc-short-overview %} and "[AUTOTITLE](/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-azure)." ## Prerequisites diff --git a/content/actions/use-cases-and-examples/deploying/deploying-java-to-azure-app-service.md b/content/actions/use-cases-and-examples/deploying/deploying-java-to-azure-app-service.md index d6dcbe5421ea..fcc7d297c6b6 100644 --- a/content/actions/use-cases-and-examples/deploying/deploying-java-to-azure-app-service.md +++ b/content/actions/use-cases-and-examples/deploying/deploying-java-to-azure-app-service.md @@ -20,11 +20,8 @@ redirect_from: This guide explains how to use {% data variables.product.prodname_actions %} to build and deploy a Java project to [Azure App Service](https://azure.microsoft.com/services/app-service/). -{% note %} - -**Note**: {% data reusables.actions.about-oidc-short-overview %} and "[AUTOTITLE](/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-azure)." - -{% endnote %} +> [!NOTE] +> {% data reusables.actions.about-oidc-short-overview %} and "[AUTOTITLE](/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-azure)." ## Prerequisites diff --git a/content/actions/use-cases-and-examples/deploying/deploying-net-to-azure-app-service.md b/content/actions/use-cases-and-examples/deploying/deploying-net-to-azure-app-service.md index abb6cd9b16eb..33bd394218b6 100644 --- a/content/actions/use-cases-and-examples/deploying/deploying-net-to-azure-app-service.md +++ b/content/actions/use-cases-and-examples/deploying/deploying-net-to-azure-app-service.md @@ -19,11 +19,8 @@ redirect_from: This guide explains how to use {% data variables.product.prodname_actions %} to build and deploy a .NET project to [Azure App Service](https://azure.microsoft.com/services/app-service/). -{% note %} - -**Note**: {% data reusables.actions.about-oidc-short-overview %} and "[AUTOTITLE](/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-azure)." - -{% endnote %} +> [!NOTE] +> {% data reusables.actions.about-oidc-short-overview %} and "[AUTOTITLE](/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-azure)." ## Prerequisites diff --git a/content/actions/use-cases-and-examples/deploying/deploying-nodejs-to-azure-app-service.md b/content/actions/use-cases-and-examples/deploying/deploying-nodejs-to-azure-app-service.md index b1a58369d85a..b91eca4683c5 100644 --- a/content/actions/use-cases-and-examples/deploying/deploying-nodejs-to-azure-app-service.md +++ b/content/actions/use-cases-and-examples/deploying/deploying-nodejs-to-azure-app-service.md @@ -24,11 +24,8 @@ topics: This guide explains how to use {% data variables.product.prodname_actions %} to build, test, and deploy a Node.js project to [Azure App Service](https://azure.microsoft.com/services/app-service/). -{% note %} - -**Note**: {% data reusables.actions.about-oidc-short-overview %} and "[AUTOTITLE](/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-azure)." - -{% endnote %} +> [!NOTE] +> {% data reusables.actions.about-oidc-short-overview %} and "[AUTOTITLE](/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-azure)." ## Prerequisites diff --git a/content/actions/use-cases-and-examples/deploying/deploying-php-to-azure-app-service.md b/content/actions/use-cases-and-examples/deploying/deploying-php-to-azure-app-service.md index 69c83d7cec6d..7e6b78a87d22 100644 --- a/content/actions/use-cases-and-examples/deploying/deploying-php-to-azure-app-service.md +++ b/content/actions/use-cases-and-examples/deploying/deploying-php-to-azure-app-service.md @@ -19,11 +19,8 @@ redirect_from: This guide explains how to use {% data variables.product.prodname_actions %} to build and deploy a PHP project to [Azure App Service](https://azure.microsoft.com/services/app-service/). -{% note %} - -**Note**: {% data reusables.actions.about-oidc-short-overview %} and "[AUTOTITLE](/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-azure)." - -{% endnote %} +> [!NOTE] +> {% data reusables.actions.about-oidc-short-overview %} and "[AUTOTITLE](/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-azure)." ## Prerequisites diff --git a/content/actions/use-cases-and-examples/deploying/deploying-python-to-azure-app-service.md b/content/actions/use-cases-and-examples/deploying/deploying-python-to-azure-app-service.md index f5093958c5de..43b09cc206d2 100644 --- a/content/actions/use-cases-and-examples/deploying/deploying-python-to-azure-app-service.md +++ b/content/actions/use-cases-and-examples/deploying/deploying-python-to-azure-app-service.md @@ -20,11 +20,8 @@ redirect_from: This guide explains how to use {% data variables.product.prodname_actions %} to build and deploy a Python project to [Azure App Service](https://azure.microsoft.com/services/app-service/). -{% note %} - -**Note**: {% data reusables.actions.about-oidc-short-overview %} and "[AUTOTITLE](/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-azure)." - -{% endnote %} +> [!NOTE] +> {% data reusables.actions.about-oidc-short-overview %} and "[AUTOTITLE](/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-azure)." ## Prerequisites diff --git a/content/actions/use-cases-and-examples/deploying/deploying-to-amazon-elastic-container-service.md b/content/actions/use-cases-and-examples/deploying/deploying-to-amazon-elastic-container-service.md index a412d1067cdf..ac5ae8b802cf 100644 --- a/content/actions/use-cases-and-examples/deploying/deploying-to-amazon-elastic-container-service.md +++ b/content/actions/use-cases-and-examples/deploying/deploying-to-amazon-elastic-container-service.md @@ -25,11 +25,8 @@ This guide explains how to use {% data variables.product.prodname_actions %} to On every new push to `main` in your {% data variables.product.company_short %} repository, the {% data variables.product.prodname_actions %} workflow builds and pushes a new container image to Amazon ECR, and then deploys a new task definition to Amazon ECS. -{% note %} - -**Note**: {% data reusables.actions.about-oidc-short-overview %} and "[AUTOTITLE](/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services)." - -{% endnote %} +> [!NOTE] +> {% data reusables.actions.about-oidc-short-overview %} and "[AUTOTITLE](/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services)." ## Prerequisites diff --git a/content/actions/use-cases-and-examples/deploying/deploying-to-azure-kubernetes-service.md b/content/actions/use-cases-and-examples/deploying/deploying-to-azure-kubernetes-service.md index fa27712abda8..9599f785648e 100644 --- a/content/actions/use-cases-and-examples/deploying/deploying-to-azure-kubernetes-service.md +++ b/content/actions/use-cases-and-examples/deploying/deploying-to-azure-kubernetes-service.md @@ -19,11 +19,8 @@ redirect_from: This guide explains how to use {% data variables.product.prodname_actions %} to build and deploy a project to [Azure Kubernetes Service](https://azure.microsoft.com/services/kubernetes-service/). -{% note %} - -**Note**: {% data reusables.actions.about-oidc-short-overview %} and "[AUTOTITLE](/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-azure)." - -{% endnote %} +> [!NOTE] +> {% data reusables.actions.about-oidc-short-overview %} and "[AUTOTITLE](/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-azure)." ## Prerequisites diff --git a/content/actions/use-cases-and-examples/deploying/deploying-to-azure-static-web-app.md b/content/actions/use-cases-and-examples/deploying/deploying-to-azure-static-web-app.md index ffc813a02775..7a73e1cb10ea 100644 --- a/content/actions/use-cases-and-examples/deploying/deploying-to-azure-static-web-app.md +++ b/content/actions/use-cases-and-examples/deploying/deploying-to-azure-static-web-app.md @@ -19,11 +19,8 @@ redirect_from: This guide explains how to use {% data variables.product.prodname_actions %} to build and deploy a web app to [Azure Static Web Apps](https://azure.microsoft.com/services/app-service/static/). -{% note %} - -**Note**: {% data reusables.actions.about-oidc-short-overview %} and "[AUTOTITLE](/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-azure)." - -{% endnote %} +> [!NOTE] +> {% data reusables.actions.about-oidc-short-overview %} and "[AUTOTITLE](/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-azure)." ## Prerequisites diff --git a/content/actions/use-cases-and-examples/deploying/deploying-to-google-kubernetes-engine.md b/content/actions/use-cases-and-examples/deploying/deploying-to-google-kubernetes-engine.md index 3ebec348679d..82eff64a6a0a 100644 --- a/content/actions/use-cases-and-examples/deploying/deploying-to-google-kubernetes-engine.md +++ b/content/actions/use-cases-and-examples/deploying/deploying-to-google-kubernetes-engine.md @@ -25,11 +25,8 @@ This guide explains how to use {% data variables.product.prodname_actions %} to GKE is a managed Kubernetes cluster service from Google Cloud that can host your containerized workloads in the cloud or in your own datacenter. For more information, see [Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine). -{% note %} - -**Note**: {% data reusables.actions.about-oidc-short-overview %} - -{% endnote %} +> [!NOTE] +> {% data reusables.actions.about-oidc-short-overview %} ## Prerequisites @@ -78,11 +75,8 @@ This procedure demonstrates how to create the service account for your GKE integ 1. Add roles to the service account. - {% note %} - - **Note**: Apply more restrictive roles to suit your requirements. - - {% endnote %} + > [!NOTE] + > Apply more restrictive roles to suit your requirements. ```shell copy gcloud projects add-iam-policy-binding $GKE_PROJECT \ diff --git a/content/actions/use-cases-and-examples/deploying/deploying-with-github-actions.md b/content/actions/use-cases-and-examples/deploying/deploying-with-github-actions.md index 363a1154d114..bdd7be83008b 100644 --- a/content/actions/use-cases-and-examples/deploying/deploying-with-github-actions.md +++ b/content/actions/use-cases-and-examples/deploying/deploying-with-github-actions.md @@ -62,11 +62,8 @@ For more information, see "[AUTOTITLE](/actions/using-workflows/events-that-trig Concurrency ensures that only a single job or workflow using the same concurrency group will run at a time. You can use concurrency so that an environment has a maximum of one deployment in progress and one deployment pending at a time. For more information about concurrency, see "[AUTOTITLE](/actions/using-jobs/using-concurrency)." -{% note %} - -**Note:** `concurrency` and `environment` are not connected. The concurrency value can be any string; it does not need to be an environment name. Additionally, if another workflow uses the same environment but does not specify concurrency, that workflow will not be subject to any concurrency rules. - -{% endnote %} +> [!NOTE] +> `concurrency` and `environment` are not connected. The concurrency value can be any string; it does not need to be an environment name. Additionally, if another workflow uses the same environment but does not specify concurrency, that workflow will not be subject to any concurrency rules. For example, when the following workflow runs, it will be paused with the status `pending` if any job or workflow that uses the `production` concurrency group is in progress. It will also cancel any job or workflow that uses the `production` concurrency group and has the status `pending`. This means that there will be a maximum of one running and one pending job or workflow in that uses the `production` concurrency group. diff --git a/content/actions/use-cases-and-examples/deploying/installing-an-apple-certificate-on-macos-runners-for-xcode-development.md b/content/actions/use-cases-and-examples/deploying/installing-an-apple-certificate-on-macos-runners-for-xcode-development.md index 9db381508c8d..161f2577184f 100644 --- a/content/actions/use-cases-and-examples/deploying/installing-an-apple-certificate-on-macos-runners-for-xcode-development.md +++ b/content/actions/use-cases-and-examples/deploying/installing-an-apple-certificate-on-macos-runners-for-xcode-development.md @@ -119,11 +119,8 @@ jobs: # ... ``` -{% note %} - -**Note:** For iOS build targets, your provisioning profile should have the extension `.mobileprovision`. For macOS build targets, the extension should be `.provisionprofile`. The example workflow above should be updated to reflect your target platform. - -{% endnote %} +> [!NOTE] +> For iOS build targets, your provisioning profile should have the extension `.mobileprovision`. For macOS build targets, the extension should be `.provisionprofile`. The example workflow above should be updated to reflect your target platform. ## Required clean-up on self-hosted runners diff --git a/content/actions/use-cases-and-examples/project-management/closing-inactive-issues.md b/content/actions/use-cases-and-examples/project-management/closing-inactive-issues.md index 124e10829859..00e3dc95ae90 100644 --- a/content/actions/use-cases-and-examples/project-management/closing-inactive-issues.md +++ b/content/actions/use-cases-and-examples/project-management/closing-inactive-issues.md @@ -67,11 +67,8 @@ In the tutorial, you will first make a workflow file that uses the [`actions/sta Based on the `schedule` parameter (for example, every day at 1:30 UTC), your workflow will find issues that have been inactive for the specified period of time and will add the specified comment and label. Additionally, your workflow will close any previously labeled issues if no additional activity has occurred for the specified period of time. -{% note %} - -**Note:** {% data reusables.actions.schedule-delay %} - -{% endnote %} +> [!NOTE] +> {% data reusables.actions.schedule-delay %} You can view the history of your workflow runs to see this workflow run periodically. For more information, see "[AUTOTITLE](/actions/monitoring-and-troubleshooting-workflows/viewing-workflow-run-history)." diff --git a/content/actions/use-cases-and-examples/project-management/scheduling-issue-creation.md b/content/actions/use-cases-and-examples/project-management/scheduling-issue-creation.md index 2d17f8d1f894..a0121d7d6369 100644 --- a/content/actions/use-cases-and-examples/project-management/scheduling-issue-creation.md +++ b/content/actions/use-cases-and-examples/project-management/scheduling-issue-creation.md @@ -98,11 +98,8 @@ In the tutorial, you will first make a workflow file that uses the {% data varia Based on the `schedule` parameter (for example, every Monday at 7:20 UTC), your workflow will create a new issue with the assignees, labels, title, and body that you specified. If you set `PINNED` to `true`, the workflow will pin the issue to your repository. If you set `CLOSE_PREVIOUS` to true, the workflow will close the most recent issue with matching labels. -{% note %} - -**Note:** {% data reusables.actions.schedule-delay %} - -{% endnote %} +> [!NOTE] +> {% data reusables.actions.schedule-delay %} You can view the history of your workflow runs to see this workflow run periodically. For more information, see "[AUTOTITLE](/actions/monitoring-and-troubleshooting-workflows/viewing-workflow-run-history)." diff --git a/content/actions/use-cases-and-examples/publishing-packages/publishing-docker-images.md b/content/actions/use-cases-and-examples/publishing-packages/publishing-docker-images.md index 07e8374a7ec5..d89219d88be2 100644 --- a/content/actions/use-cases-and-examples/publishing-packages/publishing-docker-images.md +++ b/content/actions/use-cases-and-examples/publishing-packages/publishing-docker-images.md @@ -24,11 +24,8 @@ layout: inline This guide shows you how to create a workflow that performs a Docker build, and then publishes Docker images to Docker Hub or {% data variables.product.prodname_registry %}. With a single workflow, you can publish images to a single registry or to multiple registries. -{% note %} - -**Note:** If you want to push to another third-party Docker registry, the example in the "[Publishing images to {% data variables.product.prodname_registry %}](#publishing-images-to-github-packages)" section can serve as a good template. - -{% endnote %} +> [!NOTE] +> If you want to push to another third-party Docker registry, the example in the "[Publishing images to {% data variables.product.prodname_registry %}](#publishing-images-to-github-packages)" section can serve as a good template. ## Prerequisites diff --git a/content/actions/using-github-hosted-runners/using-github-hosted-runners/about-github-hosted-runners.md b/content/actions/using-github-hosted-runners/using-github-hosted-runners/about-github-hosted-runners.md index 472e16a5bc73..341b962bb03a 100644 --- a/content/actions/using-github-hosted-runners/using-github-hosted-runners/about-github-hosted-runners.md +++ b/content/actions/using-github-hosted-runners/using-github-hosted-runners/about-github-hosted-runners.md @@ -125,11 +125,8 @@ While the job runs, the logs and output can be viewed in the {% data variables.p > [!NOTE] > The `-latest` runner images are the latest stable images that {% data variables.product.prodname_dotcom %} provides, and might not be the most recent version of the operating system available from the operating system vendor. -{% warning %} - -**Warning:** Beta and Deprecated Images are provided "as-is", "with all faults" and "as available" and are excluded from the service level agreement and warranty. Beta Images may not be covered by customer support. - -{% endwarning %} +> [!WARNING] +> Beta and Deprecated Images are provided "as-is", "with all faults" and "as available" and are excluded from the service level agreement and warranty. Beta Images may not be covered by customer support. {% endif %} diff --git a/content/actions/using-github-hosted-runners/using-github-hosted-runners/customizing-github-hosted-runners.md b/content/actions/using-github-hosted-runners/using-github-hosted-runners/customizing-github-hosted-runners.md index 573034c826ec..25fda15fffcc 100644 --- a/content/actions/using-github-hosted-runners/using-github-hosted-runners/customizing-github-hosted-runners.md +++ b/content/actions/using-github-hosted-runners/using-github-hosted-runners/customizing-github-hosted-runners.md @@ -41,11 +41,8 @@ jobs: sudo apt-get install jq ``` -{% note %} - -**Note:** Always run `sudo apt-get update` before installing a package. In case the `apt` index is stale, this command fetches and re-indexes any available packages, which helps prevent package installation failures. - -{% endnote %} +> [!NOTE] +> Always run `sudo apt-get update` before installing a package. In case the `apt` index is stale, this command fetches and re-indexes any available packages, which helps prevent package installation failures. ## Installing software on macOS runners diff --git a/content/actions/using-github-hosted-runners/using-larger-runners/controlling-access-to-larger-runners.md b/content/actions/using-github-hosted-runners/using-larger-runners/controlling-access-to-larger-runners.md index 3b2b07f09767..5d825bca61b2 100644 --- a/content/actions/using-github-hosted-runners/using-larger-runners/controlling-access-to-larger-runners.md +++ b/content/actions/using-github-hosted-runners/using-larger-runners/controlling-access-to-larger-runners.md @@ -11,11 +11,8 @@ redirect_from: - /actions/using-github-hosted-runners/about-larger-runners/controlling-access-to-larger-runners --- -{% note %} - -**Note:** {% data reusables.actions.windows-linux-larger-runners-note %} - -{% endnote %} +> [!NOTE] +> {% data reusables.actions.windows-linux-larger-runners-note %} ## About runner groups @@ -23,11 +20,8 @@ redirect_from: ### Managing access to your runners -{% note %} - -**Note**: Before your workflows can send jobs to {% data variables.actions.hosted_runner %}s, you must first configure permissions for the runner group. See the following sections for more information. - -{% endnote %} +> [!NOTE] +> Before your workflows can send jobs to {% data variables.actions.hosted_runner %}s, you must first configure permissions for the runner group. See the following sections for more information. Runner groups are used to control which repositories can run jobs on your {% data variables.actions.hosted_runner %}s. You must manage access to the group from each level of the management hierarchy, depending on where you've defined the {% data variables.actions.hosted_runner %}: diff --git a/content/actions/using-github-hosted-runners/using-larger-runners/running-jobs-on-larger-runners.md b/content/actions/using-github-hosted-runners/using-larger-runners/running-jobs-on-larger-runners.md index b1ea7ab100a6..56726037141e 100644 --- a/content/actions/using-github-hosted-runners/using-larger-runners/running-jobs-on-larger-runners.md +++ b/content/actions/using-github-hosted-runners/using-larger-runners/running-jobs-on-larger-runners.md @@ -39,11 +39,8 @@ Use the labels in the table below to run your workflows on the corresponding mac {% data reusables.actions.larger-runners-table %} -{% note %} - -**Note:** For macOS {% data variables.actions.hosted_runner %}s, the `-latest` runner label uses the macOS 12 runner image. For macOS Xlarge, the `-latest` runner label uses the macOS 13 runner image - -{% endnote %} +> [!NOTE] +> For macOS {% data variables.actions.hosted_runner %}s, the `-latest` runner label uses the macOS 12 runner image. For macOS Xlarge, the `-latest` runner label uses the macOS 13 runner image {% endmac %} @@ -197,10 +194,7 @@ jobs: Because macOS arm64 does not support Node 12, macOS {% data variables.actions.hosted_runner %}s automatically use Node 16 to execute any JavaScript action written for Node 12. Some community actions may not be compatible with Node 16. If you use an action that requires a different Node version, you may need to manually install a specific version at runtime. -{% note %} - -**Note:** ARM-powered runners are currently in {% data variables.release-phases.public_preview %} and are subject to change. - -{% endnote %} +> [!NOTE] +> ARM-powered runners are currently in {% data variables.release-phases.public_preview %} and are subject to change. {% endmac %} diff --git a/content/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs.md b/content/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs.md index 57e2ee1d9ff5..39bfd2dd44c3 100644 --- a/content/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs.md +++ b/content/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs.md @@ -365,11 +365,8 @@ The contents of the `vars` context is a mapping of configuration variable names This example workflow shows how configuration variables set at the repository, environment, or organization levels are automatically available using the `vars` context. -{% note %} - -Note: Configuration variables at the environment level are automatically available after their environment is declared by the runner. - -{% endnote %} +> [!NOTE] +> Configuration variables at the environment level are automatically available after their environment is declared by the runner. {% data reusables.actions.actions-vars-context-example-usage %} diff --git a/content/actions/writing-workflows/choosing-what-your-workflow-does/caching-dependencies-to-speed-up-workflows.md b/content/actions/writing-workflows/choosing-what-your-workflow-does/caching-dependencies-to-speed-up-workflows.md index 5d86df877e56..3af5e6b5c8b2 100644 --- a/content/actions/writing-workflows/choosing-what-your-workflow-does/caching-dependencies-to-speed-up-workflows.md +++ b/content/actions/writing-workflows/choosing-what-your-workflow-does/caching-dependencies-to-speed-up-workflows.md @@ -35,17 +35,14 @@ To cache dependencies for a job, you can use {% data variables.product.prodname_ | Go `go.sum` | [setup-go](https://github.com/actions/setup-go#caching-dependency-files-and-build-outputs) | | .NET NuGet | [setup-dotnet](https://github.com/actions/setup-dotnet?tab=readme-ov-file#caching-nuget-packages) | -{% warning %} - -**Warning**: {% ifversion fpt or ghec %}Be mindful of the following when using caching with {% data variables.product.prodname_actions %}: - -* {% endif %}We recommend that you don't store any sensitive information in the cache. For example, sensitive information can include access tokens or login credentials stored in a file in the cache path. Also, command line interface (CLI) programs like `docker login` can save access credentials in a configuration file. Anyone with read access can create a pull request on a repository and access the contents of a cache. Forks of a repository can also create pull requests on the base branch and access caches on the base branch. +> [!WARNING] +> {% ifversion fpt or ghec %}Be mindful of the following when using caching with {% data variables.product.prodname_actions %}: +> +> * {% endif %}We recommend that you don't store any sensitive information in the cache. For example, sensitive information can include access tokens or login credentials stored in a file in the cache path. Also, command line interface (CLI) programs like `docker login` can save access credentials in a configuration file. Anyone with read access can create a pull request on a repository and access the contents of a cache. Forks of a repository can also create pull requests on the base branch and access caches on the base branch. {%- ifversion fpt or ghec %} -* When using self-hosted runners, caches from workflow runs are stored on {% data variables.product.company_short %}-owned cloud storage. A customer-owned storage solution is only available with {% data variables.product.prodname_ghe_server %}. +> * When using self-hosted runners, caches from workflow runs are stored on {% data variables.product.company_short %}-owned cloud storage. A customer-owned storage solution is only available with {% data variables.product.prodname_ghe_server %}. {%- endif %} -{% endwarning %} - {% data reusables.actions.comparing-artifacts-caching %} For more information on workflow run artifacts, see "[AUTOTITLE](/actions/using-workflows/storing-workflow-data-as-artifacts)." @@ -289,11 +286,8 @@ There are multiple ways to manage caches for your repositories: * Using the REST API. For more information, see "[AUTOTITLE](/rest/actions/cache)." * Installing the `gh cache` subcommand to manage your caches from the command line. For more information, see the [GitHub CLI documentation](https://cli.github.com/manual/gh_cache). - {% note %} - - **Note:** If you are doing this manually, ensure you have version 2.32.0 or higher of the CLI installed. - - {% endnote %} + > [!NOTE] + > If you are doing this manually, ensure you have version 2.32.0 or higher of the CLI installed. {% else %} diff --git a/content/actions/writing-workflows/choosing-what-your-workflow-does/evaluate-expressions-in-workflows-and-actions.md b/content/actions/writing-workflows/choosing-what-your-workflow-does/evaluate-expressions-in-workflows-and-actions.md index ad66d9d33346..874cc243ce57 100644 --- a/content/actions/writing-workflows/choosing-what-your-workflow-does/evaluate-expressions-in-workflows-and-actions.md +++ b/content/actions/writing-workflows/choosing-what-your-workflow-does/evaluate-expressions-in-workflows-and-actions.md @@ -25,11 +25,8 @@ Expressions are commonly used with the conditional `if` keyword in a workflow fi `${{ }}` {% endraw %} -{% note %} - -**Note**: The exception to this rule is when you are using expressions in an `if` clause, where, optionally, you can usually omit {% raw %}`${{`{% endraw %} and {% raw %}`}}`{% endraw %}. For more information about `if` conditionals, see "[AUTOTITLE](/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idif)." - -{% endnote %} +> [!NOTE] +> The exception to this rule is when you are using expressions in an `if` clause, where, optionally, you can usually omit {% raw %}`${{`{% endraw %} and {% raw %}`}}`{% endraw %}. For more information about `if` conditionals, see "[AUTOTITLE](/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idif)." {% data reusables.actions.context-injection-warning %} @@ -92,14 +89,10 @@ env: | `&&` | And | | \|\| | Or | - {% note %} - - **Notes:** - * {% data variables.product.company_short %} ignores case when comparing strings. - * `steps..outputs.` evaluates as a string. {% data reusables.actions.expressions-syntax-evaluation %} For more information, see "[AUTOTITLE](/actions/learn-github-actions/contexts#steps-context)." - * For numerical comparison, the `fromJSON()` function can be used to convert a string to a number. For more information on the `fromJSON()` function, see "[fromJSON](#fromjson)." - - {% endnote %} + > [!NOTE] + > * {% data variables.product.company_short %} ignores case when comparing strings. + > * `steps..outputs.` evaluates as a string. {% data reusables.actions.expressions-syntax-evaluation %} For more information, see "[AUTOTITLE](/actions/learn-github-actions/contexts#steps-context)." + > * For numerical comparison, the `fromJSON()` function can be used to convert a string to a number. For more information on the `fromJSON()` function, see "[fromJSON](#fromjson)." {% data variables.product.prodname_dotcom %} performs loose equality comparisons. @@ -332,11 +325,8 @@ steps: Causes the step to always execute, and returns `true`, even when canceled. The `always` expression is best used at the step level or on tasks that you expect to run even when a job is canceled. For example, you can use `always` to send logs even when a job is canceled. -{% warning %} - -**Warning:** Avoid using `always` for any task that could suffer from a critical failure, for example: getting sources, otherwise the workflow may hang until it times out. If you want to run a job or step regardless of its success or failure, use the recommended alternative: `if: {% raw %}${{ !cancelled() }}{% endraw %}` - -{% endwarning %} +> [!WARNING] +> Avoid using `always` for any task that could suffer from a critical failure, for example: getting sources, otherwise the workflow may hang until it times out. If you want to run a job or step regardless of its success or failure, use the recommended alternative: `if: {% raw %}${{ !cancelled() }}{% endraw %}` #### Example of `always` diff --git a/content/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables.md b/content/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables.md index e1188a088c7a..f076144d3bec 100644 --- a/content/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables.md +++ b/content/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables.md @@ -29,11 +29,8 @@ You can set a custom variable in two ways. * To define an environment variable for use in a single workflow, you can use the `env` key in the workflow file. For more information, see "[Defining environment variables for a single workflow](#defining-environment-variables-for-a-single-workflow)". * To define a configuration variable across multiple workflows, you can define it at the organization, repository, or environment level. For more information, see "[Defining configuration variables for multiple workflows](#defining-configuration-variables-for-multiple-workflows)". -{% warning %} - -**Warning:** By default, variables render unmasked in your build outputs. If you need greater security for sensitive information, such as passwords, use secrets instead. For more information, see "[AUTOTITLE](/actions/security-guides/using-secrets-in-github-actions)". - -{% endwarning %} +> [!WARNING] +> By default, variables render unmasked in your build outputs. If you need greater security for sensitive information, such as passwords, use secrets instead. For more information, see "[AUTOTITLE](/actions/security-guides/using-secrets-in-github-actions)". ## Defining environment variables for a single workflow @@ -78,11 +75,8 @@ Because runner environment variable interpolation is done after a workflow job i When you set an environment variable, you cannot use any of the default environment variable names. For a complete list of default environment variables, see "[Default environment variables](#default-environment-variables)" below. If you attempt to override the value of one of these default variables, the assignment is ignored. -{% note %} - -**Note**: You can list the entire set of environment variables that are available to a workflow step by using `run: env` in a step and then examining the output for the step. - -{% endnote %} +> [!NOTE] +> You can list the entire set of environment variables that are available to a workflow step by using `run: env` in a step and then examining the output for the step. ## Defining configuration variables for multiple workflows @@ -164,11 +158,8 @@ A workflow created in a repository can access the following number of variables: * Up to 1,000 organization variables, if the total combined size of repository and organization variables is less than 10 MB. If the total combined size of organization and repository variables exceeds 10 MB, only the organization variables that fall below that limit will be available (after accounting for repository variables and as sorted alphabetically by variable name). * Up to 100 environment-level variables. -{% note %} - -**Note**: Environment-level variables do not count toward the 10 MB total size limit. If you exceed the combined size limit for repository and organization variables and still need additional variables, you can use an environment and define additional variables in the environment. - -{% endnote %} +> [!NOTE] +> Environment-level variables do not count toward the 10 MB total size limit. If you exceed the combined size limit for repository and organization variables and still need additional variables, you can use an environment and define additional variables in the environment. {% else %} @@ -182,11 +173,8 @@ A workflow created in a repository can access the following number of variables: * Up to 1,000 organization variables, if the total combined size of repository and organization variables is less than 256 KB. If the total combined size of organization and repository variables exceeds 256 KB, only the organization variables that fall below that limit will be available (after accounting for repository variables and as sorted alphabetically by variable name). * Up to 100 environment-level variables. -{% note %} - -**Note**: Environment-level variables do not count toward the 256 KB total size limit. If you exceed the combined size limit for repository and organization variables and still need additional variables, you can use an environment and define additional variables in the environment. - -{% endnote %} +> [!NOTE] +> Environment-level variables do not count toward the 256 KB total size limit. If you exceed the combined size limit for repository and organization variables and still need additional variables, you can use an environment and define additional variables in the environment. {% endif %} @@ -239,11 +227,8 @@ run: echo "${{ env.Greeting }} ${{ env.First_Name }}. Today is ${{ env.DAY_OF_WE {% endraw %} -{% note %} - -**Note**: Contexts are usually denoted using the dollar sign and curly braces, as {% raw %}`${{ context.property }}`{% endraw %}. In an `if` conditional, the {% raw %}`${{` and `}}`{% endraw %} are optional, but if you use them they must enclose the entire comparison statement, as shown above. - -{% endnote %} +> [!NOTE] +> Contexts are usually denoted using the dollar sign and curly braces, as {% raw %}`${{ context.property }}`{% endraw %}. In an `if` conditional, the {% raw %}`${{` and `}}`{% endraw %} are optional, but if you use them they must enclose the entire comparison statement, as shown above. You will commonly use either the `env` or `github` context to access variable values in parts of the workflow that are processed before jobs are sent to runners. @@ -317,11 +302,8 @@ We strongly recommend that actions use variables to access the filesystem rather | `RUNNER_TEMP` | {% data reusables.actions.runner-temp-directory-description %} For example, `D:\a\_temp` | | `RUNNER_TOOL_CACHE` | {% data reusables.actions.runner-tool-cache-description %} For example, `C:\hostedtoolcache\windows` | -{% note %} - -**Note:** If you need to use a workflow run's URL from within a job, you can combine these variables: `$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID` - -{% endnote %} +> [!NOTE] +> If you need to use a workflow run's URL from within a job, you can combine these variables: `$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID` ## Detecting the operating system diff --git a/content/actions/writing-workflows/choosing-what-your-workflow-does/storing-and-sharing-data-from-a-workflow.md b/content/actions/writing-workflows/choosing-what-your-workflow-does/storing-and-sharing-data-from-a-workflow.md index 72a910a365e0..86741403e76e 100644 --- a/content/actions/writing-workflows/choosing-what-your-workflow-does/storing-and-sharing-data-from-a-workflow.md +++ b/content/actions/writing-workflows/choosing-what-your-workflow-does/storing-and-sharing-data-from-a-workflow.md @@ -155,11 +155,8 @@ After a workflow run has been completed, you can download or delete artifacts on The [`actions/download-artifact`](https://github.com/actions/download-artifact) action can be used to download previously uploaded artifacts during a workflow run. -{% note %} - -**Note:** You can only download artifacts in a workflow that were uploaded during the same workflow run. - -{% endnote %} +> [!NOTE] +> You can only download artifacts in a workflow that were uploaded during the same workflow run. Specify an artifact's name to download an individual artifact. If you uploaded an artifact without specifying a name, the default name is `artifact`. diff --git a/content/actions/writing-workflows/choosing-what-your-workflow-does/using-pre-written-building-blocks-in-your-workflow.md b/content/actions/writing-workflows/choosing-what-your-workflow-does/using-pre-written-building-blocks-in-your-workflow.md index ef9c9203dd3a..10d98b646f5c 100644 --- a/content/actions/writing-workflows/choosing-what-your-workflow-does/using-pre-written-building-blocks-in-your-workflow.md +++ b/content/actions/writing-workflows/choosing-what-your-workflow-does/using-pre-written-building-blocks-in-your-workflow.md @@ -129,11 +129,8 @@ The creators of a community action have the option to use tags, branches, or SHA You will designate the version of the action in your workflow file. Check the action's documentation for information on their approach to release management, and to see which tag, branch, or SHA value to use. -{% note %} - -**Note:** We recommend that you use a SHA value when using third-party actions. However, it's important to note {% data variables.product.prodname_dependabot %} will only create {% data variables.product.prodname_dependabot_alerts %} for vulnerable {% data variables.product.prodname_actions %} that use semantic versioning. For more information, see "[AUTOTITLE](/actions/security-guides/security-hardening-for-github-actions#using-third-party-actions)" and "[AUTOTITLE](/code-security/dependabot/dependabot-alerts/about-dependabot-alerts)." - -{% endnote %} +> [!NOTE] +> We recommend that you use a SHA value when using third-party actions. However, it's important to note {% data variables.product.prodname_dependabot %} will only create {% data variables.product.prodname_dependabot_alerts %} for vulnerable {% data variables.product.prodname_actions %} that use semantic versioning. For more information, see "[AUTOTITLE](/actions/security-guides/security-hardening-for-github-actions#using-third-party-actions)" and "[AUTOTITLE](/code-security/dependabot/dependabot-alerts/about-dependabot-alerts)." ### Using tags diff --git a/content/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions.md b/content/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions.md index 8bdd35f4a27d..6eb898aeb189 100644 --- a/content/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions.md +++ b/content/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions.md @@ -44,17 +44,11 @@ Write-Output "::workflow-command parameter1={data},parameter2={data}::{command v {% endpowershell %} -{% note %} +> [!NOTE] +> Workflow command and parameter names are case insensitive. -**Note:** Workflow command and parameter names are case insensitive. - -{% endnote %} - -{% warning %} - -**Warning:** If you are using Command Prompt, omit double quote characters (`"`) when using workflow commands. - -{% endwarning %} +> [!WARNING] +> If you are using Command Prompt, omit double quote characters (`"`) when using workflow commands. ## Using workflow commands to access toolkit functions @@ -293,11 +287,8 @@ Write-Output "::add-mask::Mona The Octocat" {% endpowershell %} -{% warning %} - -**Warning:** Make sure you register the secret with 'add-mask' before outputting it in the build logs or using it in any other workflow commands. - -{% endwarning %} +> [!WARNING] +> Make sure you register the secret with 'add-mask' before outputting it in the build logs or using it in any other workflow commands. ### Example: Masking an environment variable @@ -395,11 +386,8 @@ If you want to pass a masked secret between jobs or workflows, you should store #### Workflow -{% note %} - -**Note**: This workflow uses an imaginary secret store, `secret-store`, which has imaginary commands `store-secret` and `retrieve-secret`. `some/secret-store@ 27b31702a0e7fc50959f5ad993c78deac1bdfc29` is an imaginary action that installs the `secret-store` application and configures it to connect to an `instance` with `credentials`. - -{% endnote %} +> [!NOTE] +> This workflow uses an imaginary secret store, `secret-store`, which has imaginary commands `store-secret` and `retrieve-secret`. `some/secret-store@ 27b31702a0e7fc50959f5ad993c78deac1bdfc29` is an imaginary action that installs the `secret-store` application and configures it to connect to an `instance` with `credentials`. {% bash %} @@ -492,11 +480,8 @@ Stops processing any workflow commands. This special command allows you to log a To stop the processing of workflow commands, pass a unique token to `stop-commands`. To resume processing workflow commands, pass the same token that you used to stop workflow commands. -{% warning %} - -**Warning:** Make sure the token you're using is randomly generated and unique for each run. - -{% endwarning %} +> [!WARNING] +> Make sure the token you're using is randomly generated and unique for each run. ```text copy ::{endtoken}:: @@ -605,33 +590,30 @@ steps: {% powershell %} -{% note %} - -**Note:** PowerShell versions 5.1 and below (`shell: powershell`) do not use UTF-8 by default, so you must specify the UTF-8 encoding. For example: - -```yaml copy -jobs: - legacy-powershell-example: - runs-on: windows-latest - steps: - - shell: powershell - run: | - "mypath" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append -``` - -PowerShell Core versions 6 and higher (`shell: pwsh`) use UTF-8 by default. For example: - -```yaml copy -jobs: - powershell-core-example: - runs-on: windows-latest - steps: - - shell: pwsh - run: | - "mypath" >> $env:GITHUB_PATH -``` - -{% endnote %} +> [!NOTE] +> PowerShell versions 5.1 and below (`shell: powershell`) do not use UTF-8 by default, so you must specify the UTF-8 encoding. For example: +> +> ```yaml copy +> jobs: +> legacy-powershell-example: +> runs-on: windows-latest +> steps: +> - shell: powershell +> run: | +> "mypath" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append +> ``` +> +> PowerShell Core versions 6 and higher (`shell: pwsh`) use UTF-8 by default. For example: +> +> ```yaml copy +> jobs: +> powershell-core-example: +> runs-on: windows-latest +> steps: +> - shell: pwsh +> run: | +> "mypath" >> $env:GITHUB_PATH +> ``` {% endpowershell %} @@ -667,11 +649,12 @@ You can make an environment variable available to any subsequent steps in a work {% data reusables.actions.environment-variables-are-fixed %} For more information about the default environment variables, see "[AUTOTITLE](/actions/learn-github-actions/environment-variables#default-environment-variables)." -{% ifversion github-env-node-options %}{% note %} +{% ifversion github-env-node-options %} -**Note:** Due to security restrictions, `GITHUB_ENV` cannot be used to set the `NODE_OPTIONS` environment variable. +> [!NOTE] +> Due to security restrictions, `GITHUB_ENV` cannot be used to set the `NODE_OPTIONS` environment variable. -{% endnote %}{% endif %} +{% endif %} ### Example of writing an environment variable to `GITHUB_ENV` @@ -717,11 +700,8 @@ For multiline strings, you may use a delimiter with the following syntax. {delimiter} ``` -{% warning %} - -**Warning:** Make sure the delimiter you're using won't occur on a line of its own within the value. If the value is completely arbitrary then you shouldn't use this format. Write the value to a file instead. - -{% endwarning %} +> [!WARNING] +> Make sure the delimiter you're using won't occur on a line of its own within the value. If the value is completely arbitrary then you shouldn't use this format. Write the value to a file instead. #### Example of a multiline string diff --git a/content/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows.md b/content/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows.md index 657f254eac25..e5fc94f31f5c 100644 --- a/content/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows.md +++ b/content/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows.md @@ -21,11 +21,8 @@ Workflow triggers are events that cause a workflow to run. For more information Some events have multiple activity types. For these events, you can specify which activity types will trigger a workflow run. For more information about what each activity type means, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads)." -{% note %} - -**Note:** Not all webhook events trigger workflows. - -{% endnote %} +> [!NOTE] +> Not all webhook events trigger workflows. ## `branch_protection_rule` @@ -33,11 +30,8 @@ Some events have multiple activity types. For these events, you can specify whic | --------------------- | -------------- | ------------ | -------------| | [`branch_protection_rule`](/webhooks-and-events/webhooks/webhook-events-and-payloads#branch_protection_rule) | - `created`
- `edited`
- `deleted` | Last commit on default branch | Default branch | -{% note %} - -**Note**: {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#branch_protection_rule)." {% data reusables.developer-site.limit_workflow_to_activity_types %} - -{% endnote %} +> [!NOTE] +> {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#branch_protection_rule)." {% data reusables.developer-site.limit_workflow_to_activity_types %} {% data reusables.actions.branch-requirement %} @@ -57,11 +51,8 @@ on: | --------------------- | -------------- | ------------ | -------------| | [`check_run`](/webhooks-and-events/webhooks/webhook-events-and-payloads#check_run) | - `created`
- `rerequested`
- `completed`
- `requested_action` | Last commit on default branch | Default branch | -{% note %} - -**Note**: {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#check_run)." {% data reusables.developer-site.limit_workflow_to_activity_types %} - -{% endnote %} +> [!NOTE] +> {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#check_run)." {% data reusables.developer-site.limit_workflow_to_activity_types %} {% data reusables.actions.branch-requirement %} @@ -81,19 +72,13 @@ on: | --------------------- | -------------- | ------------ | -------------| | [`check_suite`](/webhooks-and-events/webhooks/webhook-events-and-payloads#check_suite) | - `completed` | Last commit on default branch | Default branch | -{% note %} - -**Note**: {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#check_suite)." Although only the `completed` activity type is supported, specifying the activity type will keep your workflow specific if more activity types are added in the future. {% data reusables.developer-site.limit_workflow_to_activity_types %} - -{% endnote %} +> [!NOTE] +> {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#check_suite)." Although only the `completed` activity type is supported, specifying the activity type will keep your workflow specific if more activity types are added in the future. {% data reusables.developer-site.limit_workflow_to_activity_types %} {% data reusables.actions.branch-requirement %} -{% note %} - -**Note:** To prevent recursive workflows, this event does not trigger workflows if the check suite was created by {% data variables.product.prodname_actions %}. - -{% endnote %} +> [!NOTE] +> To prevent recursive workflows, this event does not trigger workflows if the check suite was created by {% data variables.product.prodname_actions %}. Runs your workflow when check suite activity occurs. A check suite is a collection of the check runs created for a specific commit. Check suites summarize the status and conclusion of the check runs that are in the suite. For information, see "[AUTOTITLE](/rest/guides/using-the-rest-api-to-interact-with-checks)." For information about the check suite APIs, see "[AUTOTITLE](/graphql/reference/objects#checksuite)" in the GraphQL API documentation or "[AUTOTITLE](/rest/checks/suites)." @@ -111,11 +96,8 @@ on: | --------------------- | -------------- | ------------ | -------------| | [`create`](/webhooks-and-events/webhooks/webhook-events-and-payloads#create) | Not applicable | Last commit on the created branch or tag | Branch or tag created | -{% note %} - -**Note**: An event will not be created when you create more than three tags at once. - -{% endnote %} +> [!NOTE] +> An event will not be created when you create more than three tags at once. Runs your workflow when someone creates a Git reference (Git branch or tag) in the workflow's repository. For information about the APIs to create a Git reference, see "[AUTOTITLE](/graphql/reference/mutations#createref)" in the GraphQL API documentation or "[AUTOTITLE](/rest/git/refs#create-a-reference)." @@ -134,11 +116,8 @@ on: {% data reusables.actions.branch-requirement %} -{% note %} - -**Note**: An event will not be created when you delete more than three tags at once. - -{% endnote %} +> [!NOTE] +> An event will not be created when you delete more than three tags at once. Runs your workflow when someone deletes a Git reference (Git branch or tag) in the workflow's repository. For information about the APIs to delete a Git reference, see "[AUTOTITLE](/graphql/reference/mutations#deleteref)" in the GraphQL API documentation or "[AUTOTITLE](/rest/git/refs#delete-a-reference)." @@ -170,11 +149,8 @@ on: | --------------------- | -------------- | ------------ | -------------| | [`deployment_status`](/webhooks-and-events/webhooks/webhook-events-and-payloads#deployment_status) | Not applicable | Commit to be deployed | Branch or tag to be deployed (empty if commit)| -{% note %} - -**Note:** When a deployment status's state is set to `inactive`, a workflow run will not be triggered. - -{% endnote %} +> [!NOTE] +> When a deployment status's state is set to `inactive`, a workflow run will not be triggered. Runs your workflow when a third party provides a deployment status. Deployments created with a commit SHA may not have a Git ref. For information about the APIs to create a deployment status, see "[AUTOTITLE](/graphql/reference/mutations#createdeploymentstatus)" in the GraphQL API documentation or "[AUTOTITLE](/rest/deployments#create-a-deployment-status)." @@ -191,11 +167,8 @@ on: | --------------------- | -------------- | ------------ | -------------| | [`discussion`](/webhooks-and-events/webhooks/webhook-events-and-payloads#discussion) | - `created`
- `edited`
- `deleted`
- `transferred`
- `pinned`
- `unpinned`
- `labeled`
- `unlabeled`
- `locked`
- `unlocked`
- `category_changed`
- `answered`
- `unanswered` | Last commit on default branch | Default branch | -{% note %} - -**Note**: {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#discussion)." {% data reusables.developer-site.limit_workflow_to_activity_types %} - -{% endnote %} +> [!NOTE] +> {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#discussion)." {% data reusables.developer-site.limit_workflow_to_activity_types %} {% data reusables.actions.branch-requirement %} @@ -217,11 +190,8 @@ on: | --------------------- | -------------- | ------------ | -------------| | [`discussion_comment`](/webhooks-and-events/webhooks/webhook-events-and-payloads#discussion_comment) | - `created`
- `edited`
- `deleted`
| Last commit on default branch | Default branch | -{% note %} - -**Note**: {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#discussion_comment)." {% data reusables.developer-site.limit_workflow_to_activity_types %} - -{% endnote %} +> [!NOTE] +> {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#discussion_comment)." {% data reusables.developer-site.limit_workflow_to_activity_types %} {% data reusables.actions.branch-requirement %} @@ -277,11 +247,8 @@ on: | --------------------- | -------------- | ------------ | -------------| | [`issue_comment`](/webhooks-and-events/webhooks/webhook-events-and-payloads#issue_comment) | - `created`
- `edited`
- `deleted`
| Last commit on default branch | Default branch | -{% note %} - -**Note**: {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#issue_comment)." {% data reusables.developer-site.limit_workflow_to_activity_types %} - -{% endnote %} +> [!NOTE] +> {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#issue_comment)." {% data reusables.developer-site.limit_workflow_to_activity_types %} {% data reusables.actions.branch-requirement %} @@ -334,11 +301,8 @@ jobs: | --------------------- | -------------- | ------------ | -------------| | [`issues`](/webhooks-and-events/webhooks/webhook-events-and-payloads#issues) | - `opened`
- `edited`
- `deleted`
- `transferred`
- `pinned`
- `unpinned`
- `closed`
- `reopened`
- `assigned`
- `unassigned`
- `labeled`
- `unlabeled`
- `locked`
- `unlocked`
- `milestoned`
- `demilestoned` | Last commit on default branch | Default branch | -{% note %} - -**Note**: {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#issues)." {% data reusables.developer-site.limit_workflow_to_activity_types %} - -{% endnote %} +> [!NOTE] +> {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#issues)." {% data reusables.developer-site.limit_workflow_to_activity_types %} {% data reusables.actions.branch-requirement %} @@ -358,11 +322,8 @@ on: | --------------------- | -------------- | ------------ | -------------| | [`label`](/webhooks-and-events/webhooks/webhook-events-and-payloads#label) | - `created`
- `edited`
- `deleted`
| Last commit on default branch | Default branch | -{% note %} - -**Note**: {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#label)." {% data reusables.developer-site.limit_workflow_to_activity_types %} - -{% endnote %} +> [!NOTE] +> {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#label)." {% data reusables.developer-site.limit_workflow_to_activity_types %} {% data reusables.actions.branch-requirement %} @@ -386,14 +347,9 @@ on: | --------------------- | -------------- | ------------ | -------------| | [`merge_group`](/webhooks-and-events/webhooks/webhook-events-and-payloads#merge_group) | `checks_requested` | SHA of the merge group | Ref of the merge group | -{% note %} - -**Notes**: - -* {% data reusables.developer-site.multiple_activity_types %} Although only the `checks_requested` activity type is supported, specifying the activity type will keep your workflow specific if more activity types are added in the future. For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#merge_group)." {% data reusables.developer-site.limit_workflow_to_activity_types %} -* {% data reusables.actions.merge-group-event-with-required-checks %} - -{% endnote %} +> [!NOTE] +> * {% data reusables.developer-site.multiple_activity_types %} Although only the `checks_requested` activity type is supported, specifying the activity type will keep your workflow specific if more activity types are added in the future. For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#merge_group)." {% data reusables.developer-site.limit_workflow_to_activity_types %} +> * {% data reusables.actions.merge-group-event-with-required-checks %} Runs your workflow when a pull request is added to a merge queue, which adds the pull request to a merge group. For more information see "[AUTOTITLE](/pull-requests/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/merging-a-pull-request-with-a-merge-queue)". @@ -415,11 +371,8 @@ on: | --------------------- | -------------- | ------------ | -------------| | [`milestone`](/webhooks-and-events/webhooks/webhook-events-and-payloads#milestone) | - `created`
- `closed`
- `opened`
- `edited`
- `deleted`
| Last commit on default branch | Default branch | -{% note %} - -**Note**: {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#milestone)." {% data reusables.developer-site.limit_workflow_to_activity_types %} - -{% endnote %} +> [!NOTE] +> {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#milestone)." {% data reusables.developer-site.limit_workflow_to_activity_types %} {% data reusables.actions.branch-requirement %} @@ -460,26 +413,19 @@ on: | --------------------- | -------------- | ------------ | -------------| | [`project`](/webhooks-and-events/webhooks/webhook-events-and-payloads#project) | - `created`
- `closed`
- `reopened`
- `edited`
- `deleted`
| Last commit on default branch | Default branch | -{% note %} - -**Note**: {% data reusables.developer-site.multiple_activity_types %} The `edited` activity type refers to when a {% data variables.projects.projects_v1_board %}, not a column or card on the {% data variables.projects.projects_v1_board %}, is edited. For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#project)." {% data reusables.developer-site.limit_workflow_to_activity_types %} - -{% endnote %} +> [!NOTE] +> {% data reusables.developer-site.multiple_activity_types %} The `edited` activity type refers to when a {% data variables.projects.projects_v1_board %}, not a column or card on the {% data variables.projects.projects_v1_board %}, is edited. For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#project)." {% data reusables.developer-site.limit_workflow_to_activity_types %} {% data reusables.actions.branch-requirement %} -{% note %} - -**Note**: This event only occurs for projects owned by the workflow's repository, not for organization-owned or user-owned projects or for projects owned by another repository. - -{% endnote %} +> [!NOTE] +> This event only occurs for projects owned by the workflow's repository, not for organization-owned or user-owned projects or for projects owned by another repository. {% ifversion fpt or ghec %} -{% note %} -**Note**: This event only occurs for {% data variables.product.prodname_projects_v1 %}. +> [!NOTE] +> This event only occurs for {% data variables.product.prodname_projects_v1 %}. -{% endnote %} {% endif %} Runs your workflow when a {% data variables.projects.projects_v1_board %} is created or modified. For activity related to cards or columns in a {% data variables.projects.projects_v1_board %}, use the [`project_card`](#project_card) or [`project_column`](#project_column) events instead. For more information about {% data variables.projects.projects_v1_boards %}, see "[AUTOTITLE](/issues/organizing-your-work-with-project-boards/managing-project-boards/about-project-boards)." For information about the {% data variables.projects.projects_v1_board %} APIs, see "[AUTOTITLE](/graphql/reference/objects#project)" in the GraphQL API documentation or "[AUTOTITLE](/rest/projects)." @@ -498,26 +444,19 @@ on: | --------------------- | -------------- | ------------ | -------------| | [`project_card`](/webhooks-and-events/webhooks/webhook-events-and-payloads#project_card) | - `created`
- `moved`
- `converted` to an issue
- `edited`
- `deleted` | Last commit on default branch | Default branch | -{% note %} - -**Note**: {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#project_card)." {% data reusables.developer-site.limit_workflow_to_activity_types %} - -{% endnote %} +> [!NOTE] +> {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#project_card)." {% data reusables.developer-site.limit_workflow_to_activity_types %} {% data reusables.actions.branch-requirement %} -{% note %} - -**Note**: This event only occurs for projects owned by the workflow's repository, not for organization-owned or user-owned projects or for projects owned by another repository. - -{% endnote %} +> [!NOTE] +> This event only occurs for projects owned by the workflow's repository, not for organization-owned or user-owned projects or for projects owned by another repository. {% ifversion fpt or ghec %} -{% note %} -**Note**: This event only occurs for {% data variables.product.prodname_projects_v1 %}. +> [!NOTE] +> This event only occurs for {% data variables.product.prodname_projects_v1 %}. -{% endnote %} {% endif %} Runs your workflow when a card on a {% data variables.projects.projects_v1_board %} is created or modified. For activity related to {% data variables.projects.projects_v1_boards %} or columns in a {% data variables.projects.projects_v1_board %}, use the [`project`](#project) or [`project_column`](#project_column) event instead. For more information about {% data variables.projects.projects_v1_boards %}, see "[AUTOTITLE](/issues/organizing-your-work-with-project-boards/managing-project-boards/about-project-boards)." For information about the project card APIs, see "[AUTOTITLE](/graphql/reference/objects#projectcard)" in the GraphQL API documentation or "[AUTOTITLE](/rest/projects/cards)." @@ -536,26 +475,19 @@ on: | --------------------- | -------------- | ------------ | -------------| | [`project_column`](/webhooks-and-events/webhooks/webhook-events-and-payloads#project_column) | - `created`
- `updated`
- `moved`
- `deleted` | Last commit on default branch | Default branch | -{% note %} - -**Note**: {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#project_column)." {% data reusables.developer-site.limit_workflow_to_activity_types %} - -{% endnote %} +> [!NOTE] +> {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#project_column)." {% data reusables.developer-site.limit_workflow_to_activity_types %} {% data reusables.actions.branch-requirement %} -{% note %} - -**Note**: This event only occurs for projects owned by the workflow's repository, not for organization-owned or user-owned projects or for projects owned by another repository. - -{% endnote %} +> [!NOTE] +> This event only occurs for projects owned by the workflow's repository, not for organization-owned or user-owned projects or for projects owned by another repository. {% ifversion fpt or ghec %} -{% note %} -**Note**: This event only occurs for {% data variables.product.prodname_projects_v1 %}. +> [!NOTE] +> This event only occurs for {% data variables.product.prodname_projects_v1 %}. -{% endnote %} {% endif %} Runs your workflow when a column on a {% data variables.projects.projects_v1_board %} is created or modified. For activity related to {% data variables.projects.projects_v1_boards %} or cards in a {% data variables.projects.projects_v1_board %}, use the [`project`](#project) or [`project_card`](#project_card) event instead. For more information about {% data variables.projects.projects_v1_boards %}, see "[AUTOTITLE](/issues/organizing-your-work-with-project-boards/managing-project-boards/about-project-boards)." For information about the project column APIs, see "[AUTOTITLE](/graphql/reference/objects#projectcolumn)" in the GraphQL API documentation or "[AUTOTITLE](/rest/projects#columns)." @@ -593,20 +525,12 @@ on: | --------------------- | -------------- | ------------ | -------------| | [`pull_request`](/webhooks-and-events/webhooks/webhook-events-and-payloads#pull_request) | - `assigned`
- `unassigned`
- `labeled`
- `unlabeled`
- `opened`
- `edited`
- `closed`
- `reopened`
- `synchronize`
- `converted_to_draft`
- `locked`
- `unlocked`
{% ifversion fpt or ghec %}- `enqueued`
- `dequeued`
{% endif %}- `milestoned`
- `demilestoned`
- `ready_for_review`
- `review_requested`
- `review_request_removed`
- `auto_merge_enabled`
- `auto_merge_disabled` | Last merge commit on the `GITHUB_REF` branch | PR merge branch `refs/pull/PULL_REQUEST_NUMBER/merge` | -{% note %} - -**Notes**: -* {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#pull_request)." By default, a workflow only runs when a `pull_request` event's activity type is `opened`, `synchronize`, or `reopened`. To trigger workflows by different activity types, use the `types` keyword. For more information, see "[AUTOTITLE](/actions/using-workflows/workflow-syntax-for-github-actions#onevent_nametypes)." - -* Workflows will not run on `pull_request` activity if the pull request has a merge conflict. The merge conflict must be resolved first. - - Conversely, workflows with the `pull_request_target` event will run even if the pull request has a merge conflict. Before using the `pull_request_target` trigger, you should be aware of the security risks. For more information, see [`pull_request_target`](#pull_request_target). - -* The `pull_request` webhook event payload is empty for merged pull requests and pull requests that come from forked repositories. - -* The value of `GITHUB_REF` varies for a closed pull request depending on whether the pull request has been merged or not. If a pull request was closed but not merged, it will be `refs/pull/PULL_REQUEST_NUMBER/merge`. If a pull request was closed as a result of being merged, it will be the fully qualified `ref` of the branch it was merged into, for example `/refs/heads/main`. - -{% endnote %} +> [!NOTE] +> * {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#pull_request)." By default, a workflow only runs when a `pull_request` event's activity type is `opened`, `synchronize`, or `reopened`. To trigger workflows by different activity types, use the `types` keyword. For more information, see "[AUTOTITLE](/actions/using-workflows/workflow-syntax-for-github-actions#onevent_nametypes)." +> * Workflows will not run on `pull_request` activity if the pull request has a merge conflict. The merge conflict must be resolved first. +> Conversely, workflows with the `pull_request_target` event will run even if the pull request has a merge conflict. Before using the `pull_request_target` trigger, you should be aware of the security risks. For more information, see [`pull_request_target`](#pull_request_target). +> * The `pull_request` webhook event payload is empty for merged pull requests and pull requests that come from forked repositories. +> * The value of `GITHUB_REF` varies for a closed pull request depending on whether the pull request has been merged or not. If a pull request was closed but not merged, it will be `refs/pull/PULL_REQUEST_NUMBER/merge`. If a pull request was closed as a result of being merged, it will be the fully qualified `ref` of the branch it was merged into, for example `/refs/heads/main`. Runs your workflow when activity on a pull request in the workflow's repository occurs. For example, if no activity types are specified, the workflow runs when a pull request is opened or reopened or when the head branch of the pull request is updated. For activity related to pull request reviews, pull request review comments, or pull request comments, use the [`pull_request_review`](#pull_request_review), [`pull_request_review_comment`](#pull_request_review_comment), or [`issue_comment`](#issue_comment) events instead. For information about the pull request APIs, see "[AUTOTITLE](/graphql/reference/objects#pullrequest)" in the GraphQL API documentation or "[AUTOTITLE](/rest/pulls)." @@ -649,22 +573,19 @@ on: - 'releases/**' ``` -{% note %} - -**Note:** {% data reusables.actions.branch-paths-filter %} For example, the following workflow will only run when a pull request that includes a change to a JavaScript (`.js`) file is opened on a branch whose name starts with `releases/`: - -```yaml -on: - pull_request: - types: - - opened - branches: - - 'releases/**' - paths: - - '**.js' -``` - -{% endnote %} +> [!NOTE] +> {% data reusables.actions.branch-paths-filter %} For example, the following workflow will only run when a pull request that includes a change to a JavaScript (`.js`) file is opened on a branch whose name starts with `releases/`: +> +> ```yaml +> on: +> pull_request: +> types: +> - opened +> branches: +> - 'releases/**' +> paths: +> - '**.js' +> ``` To run a job based on the pull request's head branch name (as opposed to the pull request's base branch name), use the `github.head_ref` context in a conditional. For example, this workflow will run whenever a pull request is opened, but the `run_if` job will only execute if the head of the pull request is a branch whose name starts with `releases/`: @@ -694,22 +615,19 @@ on: - '**.js' ``` -{% note %} - -**Note:** {% data reusables.actions.branch-paths-filter %} For example, the following workflow will only run when a pull request that includes a change to a JavaScript (`.js`) file is opened on a branch whose name starts with `releases/`: - -```yaml -on: - pull_request: - types: - - opened - branches: - - 'releases/**' - paths: - - '**.js' -``` - -{% endnote %} +> [!NOTE] +> {% data reusables.actions.branch-paths-filter %} For example, the following workflow will only run when a pull request that includes a change to a JavaScript (`.js`) file is opened on a branch whose name starts with `releases/`: +> +> ```yaml +> on: +> pull_request: +> types: +> - opened +> branches: +> - 'releases/**' +> paths: +> - '**.js' +> ``` ### Running your `pull_request` workflow when a pull request merges @@ -742,11 +660,8 @@ To run your workflow when a comment on a pull request (not on a pull request's d | --------------------- | -------------- | ------------ | -------------| | [`pull_request_review`](/webhooks-and-events/webhooks/webhook-events-and-payloads#pull_request_review) | - `submitted`
- `edited`
- `dismissed` | Last merge commit on the `GITHUB_REF` branch | PR merge branch `refs/pull/PULL_REQUEST_NUMBER/merge` | -{% note %} - -**Note**: {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#pull_request_review)." {% data reusables.developer-site.limit_workflow_to_activity_types %} - -{% endnote %} +> [!NOTE] +> {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#pull_request_review)." {% data reusables.developer-site.limit_workflow_to_activity_types %} Runs your workflow when a pull request review is submitted, edited, or dismissed. A pull request review is a group of pull request review comments in addition to a body comment and a state. For activity related to pull request review comments or pull request comments, use the [`pull_request_review_comment`](#pull_request_review_comment) or [`issue_comment`](#issue_comment) events instead. For information about the pull request review APIs, see "[AUTOTITLE](/graphql/reference/objects#pullrequest)" in the GraphQL API documentation or "[AUTOTITLE](/rest/pulls#reviews)." @@ -783,11 +698,8 @@ jobs: | --------------------- | -------------- | ------------ | -------------| | [`pull_request_review_comment`](/webhooks-and-events/webhooks/webhook-events-and-payloads#pull_request_review_comment) | - `created`
- `edited`
- `deleted`| Last merge commit on the `GITHUB_REF` branch | PR merge branch `refs/pull/PULL_REQUEST_NUMBER/merge` | -{% note %} - -**Note**: {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#pull_request_review_comment)." {% data reusables.developer-site.limit_workflow_to_activity_types %} - -{% endnote %} +> [!NOTE] +> {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#pull_request_review_comment)." {% data reusables.developer-site.limit_workflow_to_activity_types %} Runs your workflow when a pull request review comment is modified. A pull request review comment is a comment on a pull request's diff. For activity related to pull request reviews or pull request comments, use the [`pull_request_review`](#pull_request_review) or [`issue_comment`](#issue_comment) events instead. For information about the pull request review comment APIs, see "[AUTOTITLE](/graphql/reference/objects#pullrequestreviewcomment)" in the GraphQL API documentation or "[AUTOTITLE](/rest/pulls#comments)." @@ -807,11 +719,8 @@ on: | --------------------- | -------------- | ------------ | -------------| | [`pull_request`](/webhooks-and-events/webhooks/webhook-events-and-payloads#pull_request) | - `assigned`
- `unassigned`
- `labeled`
- `unlabeled`
- `opened`
- `edited`
- `closed`
- `reopened`
- `synchronize`
- `converted_to_draft`
- `ready_for_review`
- `locked`
- `unlocked`
- `review_requested`
- `review_request_removed`
- `auto_merge_enabled`
- `auto_merge_disabled` | Last commit on the PR base branch | PR base branch | -{% note %} - -**Note**: {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#pull_request)." By default, a workflow only runs when a `pull_request_target` event's activity type is `opened`, `synchronize`, or `reopened`. To trigger workflows by different activity types, use the `types` keyword. For more information, see "[AUTOTITLE](/actions/using-workflows/workflow-syntax-for-github-actions#onevent_nametypes)." - -{% endnote %} +> [!NOTE] +> {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#pull_request)." By default, a workflow only runs when a `pull_request_target` event's activity type is `opened`, `synchronize`, or `reopened`. To trigger workflows by different activity types, use the `types` keyword. For more information, see "[AUTOTITLE](/actions/using-workflows/workflow-syntax-for-github-actions#onevent_nametypes)." Runs your workflow when activity on a pull request in the workflow's repository occurs. For example, if no activity types are specified, the workflow runs when a pull request is opened or reopened or when the head branch of the pull request is updated. @@ -844,22 +753,19 @@ on: - 'releases/**' ``` -{% note %} - -**Note:** {% data reusables.actions.branch-paths-filter %} For example, the following workflow will only run when a pull request that includes a change to a JavaScript (`.js`) file is opened on a branch whose name starts with `releases/`: - -```yaml -on: - pull_request_target: - types: - - opened - branches: - - 'releases/**' - paths: - - '**.js' -``` - -{% endnote %} +> [!NOTE] +> {% data reusables.actions.branch-paths-filter %} For example, the following workflow will only run when a pull request that includes a change to a JavaScript (`.js`) file is opened on a branch whose name starts with `releases/`: +> +> ```yaml +> on: +> pull_request_target: +> types: +> - opened +> branches: +> - 'releases/**' +> paths: +> - '**.js' +> ``` To run a job based on the pull request's head branch name (as opposed to the pull request's base branch name), use the `github.head_ref` context in a conditional. For example, this workflow will run whenever a pull request is opened, but the `run_if` job will only execute if the head of the pull request is a branch whose name starts with `releases/`: @@ -889,22 +795,19 @@ on: - '**.js' ``` -{% note %} - -**Note:** {% data reusables.actions.branch-paths-filter %} For example, the following workflow will only run when a pull request that includes a change to a JavaScript (`.js`) file is opened on a branch whose name starts with `releases/`: - -```yaml -on: - pull_request_target: - types: - - opened - branches: - - 'releases/**' - paths: - - '**.js' -``` - -{% endnote %} +> [!NOTE] +> {% data reusables.actions.branch-paths-filter %} For example, the following workflow will only run when a pull request that includes a change to a JavaScript (`.js`) file is opened on a branch whose name starts with `releases/`: +> +> ```yaml +> on: +> pull_request_target: +> types: +> - opened +> branches: +> - 'releases/**' +> paths: +> - '**.js' +> ``` ### Running your `pull_request_target` workflow when a pull request merges @@ -931,17 +834,11 @@ jobs: | --------------------- | -------------- | ------------ | -------------| | [`push`](/webhooks-and-events/webhooks/webhook-events-and-payloads#push) | Not applicable | Tip commit pushed to the ref. When you delete a branch, the SHA in the workflow run (and its associated refs) reverts to the default branch of the repository. | Updated ref | -{% note %} - -**Note:** The webhook payload available to GitHub Actions does not include the `added`, `removed`, and `modified` attributes in the `commit` object. You can retrieve the full commit object using the API. For information, see "[AUTOTITLE](/graphql/reference/objects#commit)" in the GraphQL API documentation or "[AUTOTITLE](/rest/commits#get-a-commit)." - -{% endnote %} +> [!NOTE] +> The webhook payload available to GitHub Actions does not include the `added`, `removed`, and `modified` attributes in the `commit` object. You can retrieve the full commit object using the API. For information, see "[AUTOTITLE](/graphql/reference/objects#commit)" in the GraphQL API documentation or "[AUTOTITLE](/rest/commits#get-a-commit)." -{% note %} - -**Note**: {% ifversion fpt or ghec or ghes > 3.13 %}Events will not be created if more than 5,000 branches are pushed at once. {% endif %}Events will not be created for tags when more than three tags are pushed at once. - -{% endnote %} +> [!NOTE] +> {% ifversion fpt or ghec or ghes > 3.13 %}Events will not be created if more than 5,000 branches are pushed at once. {% endif %}Events will not be created for tags when more than three tags are pushed at once. Runs your workflow when you push a commit or tag, or when you create a repository from a template. @@ -952,11 +849,8 @@ on: push ``` -{% note %} - -**Note**: When a `push` webhook event triggers a workflow run, the Actions UI's "pushed by" field shows the account of the pusher and not the author or committer. However, if the changes are pushed to a repository using SSH authentication with a deploy key, then the "pushed by" field will be the repository admin who verified the deploy key when it was added it to a repository. - -{% endnote %} +> [!NOTE] +> When a `push` webhook event triggers a workflow run, the Actions UI's "pushed by" field shows the account of the pusher and not the author or committer. However, if the changes are pushed to a repository using SSH authentication with a deploy key, then the "pushed by" field will be the repository admin who verified the deploy key when it was added it to a repository. ### Running your workflow only when a push to specific branches occurs @@ -972,20 +866,17 @@ on: - 'releases/**' ``` -{% note %} - -**Note:** {% data reusables.actions.branch-paths-filter %} For example, the following workflow will only run when a push that includes a change to a JavaScript (`.js`) file is made to a branch whose name starts with `releases/`: - -```yaml -on: - push: - branches: - - 'releases/**' - paths: - - '**.js' -``` - -{% endnote %} +> [!NOTE] +> {% data reusables.actions.branch-paths-filter %} For example, the following workflow will only run when a push that includes a change to a JavaScript (`.js`) file is made to a branch whose name starts with `releases/`: +> +> ```yaml +> on: +> push: +> branches: +> - 'releases/**' +> paths: +> - '**.js' +> ``` ### Running your workflow only when a push of specific tags occurs @@ -1013,20 +904,17 @@ on: - '**.js' ``` -{% note %} - -**Note:** {% data reusables.actions.branch-paths-filter %} For example, the following workflow will only run when a push that includes a change to a JavaScript (`.js`) file is made to a branch whose name starts with `releases/`: - -```yaml -on: - push: - branches: - - 'releases/**' - paths: - - '**.js' -``` - -{% endnote %} +> [!NOTE] +> {% data reusables.actions.branch-paths-filter %} For example, the following workflow will only run when a push that includes a change to a JavaScript (`.js`) file is made to a branch whose name starts with `releases/`: +> +> ```yaml +> on: +> push: +> branches: +> - 'releases/**' +> paths: +> - '**.js' +> ``` ## `registry_package` @@ -1034,29 +922,19 @@ on: | --------------------- | -------------- | ------------ | -------------| | [`registry_package`](/webhooks-and-events/webhooks/webhook-events-and-payloads#package) | - `published`
- `updated` | Commit of the published package | Branch or tag of the published package | -{% note %} - -**Note**: {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#registry_package)." {% data reusables.developer-site.limit_workflow_to_activity_types %} - -{% endnote %} +> [!NOTE] +> {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#registry_package)." {% data reusables.developer-site.limit_workflow_to_activity_types %} {% data reusables.actions.branch-requirement %} -{% note %} - -**Note**: When pushing multi-architecture container images, this event occurs once per manifest, so you might observe your workflow triggering multiple times. To mitigate this, and only run your workflow job for the event that contains the actual image tag information, use a conditional: - -{% raw %} - -```yaml -jobs: - job_name: - if: ${{ github.event.registry_package.package_version.container_metadata.tag.name != '' }} -``` - -{% endraw %} - -{% endnote %} +> [!NOTE] +> When pushing multi-architecture container images, this event occurs once per manifest, so you might observe your workflow triggering multiple times. To mitigate this, and only run your workflow job for the event that contains the actual image tag information, use a conditional: +> +> ```yaml +> jobs: +> job_name: +> if: ${{ github.event.registry_package.package_version.container_metadata.tag.name != '' }} +> ``` Runs your workflow when activity related to {% data variables.product.prodname_registry %} occurs in your repository. For more information, see "[{% data variables.product.prodname_registry %} Documentation](/packages)." @@ -1074,23 +952,14 @@ on: | --------------------- | -------------- | ------------ | -------------| | [`release`](/webhooks-and-events/webhooks/webhook-events-and-payloads#release) | - `published`
- `unpublished`
- `created`
- `edited`
- `deleted`
- `prereleased`
- `released` | Last commit in the tagged release | Tag ref of release `refs/tags/` | -{% note %} - -**Note**: {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#release)." {% data reusables.developer-site.limit_workflow_to_activity_types %} - -{% endnote %} - -{% note %} +> [!NOTE] +> {% data reusables.developer-site.multiple_activity_types %} For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#release)." {% data reusables.developer-site.limit_workflow_to_activity_types %} -**Note:** Workflows are not triggered for the `created`, `edited`, or `deleted` activity types for draft releases. When you create your release through the {% data variables.product.product_name %} browser UI, your release may automatically be saved as a draft. +> [!NOTE] +> Workflows are not triggered for the `created`, `edited`, or `deleted` activity types for draft releases. When you create your release through the {% data variables.product.product_name %} browser UI, your release may automatically be saved as a draft. -{% endnote %} - -{% note %} - -**Note:** The `prereleased` type will not trigger for pre-releases published from draft releases, but the `published` type will trigger. If you want a workflow to run when stable _and_ pre-releases publish, subscribe to `published` instead of `released` and `prereleased`. - -{% endnote %} +> [!NOTE] +> The `prereleased` type will not trigger for pre-releases published from draft releases, but the `published` type will trigger. If you want a workflow to run when stable _and_ pre-releases publish, subscribe to `published` instead of `released` and `prereleased`. Runs your workflow when release activity in your repository occurs. For information about the release APIs, see "[AUTOTITLE](/graphql/reference/objects#release)" in the GraphQL API documentation or "[AUTOTITLE](/rest/releases)" in the REST API documentation. @@ -1120,11 +989,8 @@ on: types: [test_result] ``` -{% note %} - -**Note:** The `event_type` value is limited to 100 characters. - -{% endnote %} +> [!NOTE] +> The `event_type` value is limited to 100 characters. Any data that you send through the `client_payload` parameter will be available in the `github.event` context in your workflow. For example, if you send this request body when you create a repository dispatch event: @@ -1155,14 +1021,9 @@ jobs: run: echo $MESSAGE ``` -{% note %} - -**Notes**: - -* The maximum number of top-level properties in `client_payload` is 10. -* The payload can contain a maximum of 65,535 characters. - -{% endnote %} +> [!NOTE] +> * The maximum number of top-level properties in `client_payload` is 10. +> * The payload can contain a maximum of 65,535 characters. ## `schedule` @@ -1170,25 +1031,20 @@ jobs: | --------------------- | -------------- | ------------ | -------------| | Not applicable | Not applicable | Last commit on default branch | Default branch | When the scheduled workflow is set to run. A scheduled workflow uses [POSIX cron syntax](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/crontab.html#tag_20_25_07). For more information, see "[AUTOTITLE](/actions/using-workflows#triggering-a-workflow-with-events)." | -{% note %} - -**Notes:** - -* {% data reusables.actions.schedule-delay %} -* This event will only trigger a workflow run if the workflow file is on the default branch. -* Scheduled workflows will only run on the default branch. -* In a public repository, scheduled workflows are automatically disabled when no repository activity has occurred in 60 days. For information on re-enabling a disabled workflow, see "[AUTOTITLE](/enterprise-server@3.12/actions/using-workflows/disabling-and-enabling-a-workflow#enabling-a-workflow)." -* When the last user to commit to the cron schedule of a workflow is removed from the organization, the scheduled workflow will be disabled. If a user with `write` permissions to the repository makes a commit that changes the cron schedule, the scheduled workflow will be reactivated. Note that, in this situation, the workflow is not reactivated by any change to the workflow file; you must alter the `cron` value and commit this change. - - **Example:** - - ```yaml - on: - schedule: - - cron: "15 4,5 * * *" # <=== Change this value - ``` - -{% endnote %} +> [!NOTE] +> * {% data reusables.actions.schedule-delay %} +> * This event will only trigger a workflow run if the workflow file is on the default branch. +> * Scheduled workflows will only run on the default branch. +> * In a public repository, scheduled workflows are automatically disabled when no repository activity has occurred in 60 days. For information on re-enabling a disabled workflow, see "[AUTOTITLE](/enterprise-server@3.12/actions/using-workflows/disabling-and-enabling-a-workflow#enabling-a-workflow)." +> * When the last user to commit to the cron schedule of a workflow is removed from the organization, the scheduled workflow will be disabled. If a user with `write` permissions to the repository makes a commit that changes the cron schedule, the scheduled workflow will be reactivated. Note that, in this situation, the workflow is not reactivated by any change to the workflow file; you must alter the `cron` value and commit this change. +> +> **Example:** +> +> ```yaml +> on: +> schedule: +> - cron: "15 4,5 * * *" # <=== Change this value +> ``` The `schedule` event allows you to trigger a workflow at a scheduled time. @@ -1217,11 +1073,8 @@ You can use these operators in any of the five fields: | - | Range of values | `30 4-6 * * *` runs at minute 30 of the 4th, 5th, and 6th hour. | | / | Step values | `20/15 * * * *` runs every 15 minutes starting from minute 20 through 59 (minutes 20, 35, and 50). | -{% note %} - -**Note:** {% data variables.product.prodname_actions %} does not support the non-standard syntax `@yearly`, `@monthly`, `@weekly`, `@daily`, `@hourly`, and `@reboot`. - -{% endnote %} +> [!NOTE] +> {% data variables.product.prodname_actions %} does not support the non-standard syntax `@yearly`, `@monthly`, `@weekly`, `@daily`, `@hourly`, and `@reboot`. You can use [crontab guru](https://crontab.guru/) to help generate your cron syntax and confirm what time it will run. To help you get started, there is also a list of [crontab guru examples](https://crontab.guru/examples.html). @@ -1268,11 +1121,8 @@ jobs: | --------------------- | -------------- | ------------ | -------------| | [`watch`](/webhooks-and-events/webhooks/webhook-events-and-payloads#watch) | - `started` | Last commit on default branch | Default branch | -{% note %} - -**Note**: {% data reusables.developer-site.multiple_activity_types %} Although only the `started` activity type is supported, specifying the activity type will keep your workflow specific if more activity types are added in the future. For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#watch)." {% data reusables.developer-site.limit_workflow_to_activity_types %} - -{% endnote %} +> [!NOTE] +> {% data reusables.developer-site.multiple_activity_types %} Although only the `started` activity type is supported, specifying the activity type will keep your workflow specific if more activity types are added in the future. For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#watch)." {% data reusables.developer-site.limit_workflow_to_activity_types %} {% data reusables.actions.branch-requirement %} @@ -1376,19 +1226,13 @@ For more information, see the {% data variables.product.prodname_cli %} informat | --------------------- | -------------- | ------------ | -------------| | [`workflow_run`](/webhooks-and-events/webhooks/webhook-events-and-payloads#workflow_run) | - `completed`
- `requested`
- `in_progress` | Last commit on default branch | Default branch | -{% note %} - -**Note**: {% data reusables.developer-site.multiple_activity_types %} The `requested` activity type does not occur when a workflow is re-run. For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#workflow_run)." {% data reusables.developer-site.limit_workflow_to_activity_types %} - -{% endnote %} +> [!NOTE] +> {% data reusables.developer-site.multiple_activity_types %} The `requested` activity type does not occur when a workflow is re-run. For information about each activity type, see "[AUTOTITLE](/webhooks-and-events/webhooks/webhook-events-and-payloads#workflow_run)." {% data reusables.developer-site.limit_workflow_to_activity_types %} {% data reusables.actions.branch-requirement %} -{% note %} - -**Note:** You can't use `workflow_run` to chain together more than three levels of workflows. For example, if you attempt to trigger five workflows (named `B` to `F`) to run sequentially after an initial workflow `A` has run (that is: `A` → `B` → `C` → `D` → `E` → `F`), workflows `E` and `F` will not be run. - -{% endnote %} +> [!NOTE] +> You can't use `workflow_run` to chain together more than three levels of workflows. For example, if you attempt to trigger five workflows (named `B` to `F`) to run sequentially after an initial workflow `A` has run (that is: `A` → `B` → `C` → `D` → `E` → `F`), workflows `E` and `F` will not be run. This event occurs when a workflow run is requested or completed. It allows you to execute a workflow based on execution or completion of another workflow. The workflow started by the `workflow_run` event is able to access secrets and write tokens, even if the previous workflow was not. This is useful in cases where the previous workflow is intentionally not privileged, but you need to take a privileged action in a later workflow. diff --git a/content/actions/writing-workflows/choosing-when-your-workflow-runs/using-conditions-to-control-job-execution.md b/content/actions/writing-workflows/choosing-when-your-workflow-runs/using-conditions-to-control-job-execution.md index 601967794eaa..dfcea036231a 100644 --- a/content/actions/writing-workflows/choosing-when-your-workflow-runs/using-conditions-to-control-job-execution.md +++ b/content/actions/writing-workflows/choosing-when-your-workflow-runs/using-conditions-to-control-job-execution.md @@ -20,8 +20,5 @@ redirect_from: On a skipped job, you should see "This check was skipped." -{% note %} - -**Note:** In some parts of the workflow you cannot use environment variables. Instead you can use contexts to access the value of an environment variable. For more information, see "[AUTOTITLE](/actions/learn-github-actions/variables#using-the-env-context-to-access-environment-variable-values)." - -{% endnote %} +> [!NOTE] +> In some parts of the workflow you cannot use environment variables. Instead you can use contexts to access the value of an environment variable. For more information, see "[AUTOTITLE](/actions/learn-github-actions/variables#using-the-env-context-to-access-environment-variable-values)." diff --git a/content/actions/writing-workflows/workflow-syntax-for-github-actions.md b/content/actions/writing-workflows/workflow-syntax-for-github-actions.md index a97e4415e6d5..c7857c9e8417 100644 --- a/content/actions/writing-workflows/workflow-syntax-for-github-actions.md +++ b/content/actions/writing-workflows/workflow-syntax-for-github-actions.md @@ -187,11 +187,8 @@ A map of the secrets that can be used in the called workflow. Within the called workflow, you can use the `secrets` context to refer to a secret. -{% note %} - -**Note:** If you are passing the secret to a nested reusable workflow, then you must use [`jobs..secrets`](#jobsjob_idsecrets) again to pass the secret. For more information, see "[AUTOTITLE](/actions/using-workflows/reusing-workflows#passing-secrets-to-nested-workflows)." - -{% endnote %} +> [!NOTE] +> If you are passing the secret to a nested reusable workflow, then you must use [`jobs..secrets`](#jobsjob_idsecrets) again to pass the secret. For more information, see "[AUTOTITLE](/actions/using-workflows/reusing-workflows#passing-secrets-to-nested-workflows)." If a caller workflow passes a secret that is not specified in the called workflow, this results in an error. @@ -881,11 +878,8 @@ The maximum number of minutes to let a job run before {% data variables.product. If the timeout exceeds the job execution time limit for the runner, the job will be canceled when the execution time limit is met instead. For more information about job execution time limits, see "[AUTOTITLE](/actions/learn-github-actions/usage-limits-billing-and-administration#usage-limits)" for {% data variables.product.prodname_dotcom %}-hosted runners and "[AUTOTITLE](/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#usage-limits)" for self-hosted runner usage limits. -{% note %} - -**Note:** {% data reusables.actions.github-token-expiration %} For self-hosted runners, the token may be the limiting factor if the job timeout is greater than 24 hours. For more information on the `GITHUB_TOKEN`, see "[AUTOTITLE](/actions/security-guides/automatic-token-authentication#about-the-github_token-secret)." - -{% endnote %} +> [!NOTE] +> {% data reusables.actions.github-token-expiration %} For self-hosted runners, the token may be the limiting factor if the job timeout is greater than 24 hours. For more information on the `GITHUB_TOKEN`, see "[AUTOTITLE](/actions/security-guides/automatic-token-authentication#about-the-github_token-secret)." ## `jobs..strategy` @@ -1089,11 +1083,8 @@ volumes: Additional Docker container resource options. For a list of options, see "[`docker create` options](https://docs.docker.com/engine/reference/commandline/create/#options)." -{% warning %} - -**Warning:** The `--network` option is not supported. - -{% endwarning %} +> [!WARNING] +> The `--network` option is not supported. ## `jobs..uses` diff --git a/content/admin/administering-your-instance/administering-your-instance-from-the-command-line/command-line-utilities.md b/content/admin/administering-your-instance/administering-your-instance-from-the-command-line/command-line-utilities.md index 928202f78933..935469188b0b 100644 --- a/content/admin/administering-your-instance/administering-your-instance-from-the-command-line/command-line-utilities.md +++ b/content/admin/administering-your-instance/administering-your-instance-from-the-command-line/command-line-utilities.md @@ -100,11 +100,8 @@ ghe-cleanup-caches This utility wipes all existing {% data variables.enterprise.management_console %} settings. -{% tip %} - -**Tip**: {% data reusables.enterprise_enterprise_support.support_will_ask_you_to_run_command %} - -{% endtip %} +> [!TIP] +> {% data reusables.enterprise_enterprise_support.support_will_ask_you_to_run_command %} ```shell ghe-cleanup-settings @@ -796,14 +793,9 @@ Flag | Description `-v/--verbose` | Prints additional information to the console. `-h/--help` | Displays help text for the command. -{% note %} - -**Notes:** - -* This command can only be used to remove a node from a cluster configuration. It cannot be used to remove a node from a high availability configuration. -* This command does not support parallel execution. To remove multiple nodes, you must wait until this command has finished before running it for another node. - -{% endnote %} +> [!NOTE] +> * This command can only be used to remove a node from a cluster configuration. It cannot be used to remove a node from a high availability configuration. +> * This command does not support parallel execution. To remove multiple nodes, you must wait until this command has finished before running it for another node. {% endif %} @@ -1001,11 +993,10 @@ This utility tests the blob storage configuration for {% data variables.product. For more information about the configuration of {% data variables.product.prodname_actions %}, see "[AUTOTITLE](/admin/github-actions/getting-started-with-github-actions-for-your-enterprise/getting-started-with-github-actions-for-github-enterprise-server)." {% ifversion ghes-actions-storage-oidc %} -{% note %} -**Note:** This utility only works with configurations that use a credentials-based connection to the storage provider. To test OpenID Connect (OIDC) configurations, use [`ghe-actions-test-storage-with-oidc`](#ghe-actions-test-storage-with-oidc). +> [!NOTE] +> This utility only works with configurations that use a credentials-based connection to the storage provider. To test OpenID Connect (OIDC) configurations, use [`ghe-actions-test-storage-with-oidc`](#ghe-actions-test-storage-with-oidc). -{% endnote %} {% endif %} ```shell @@ -1024,11 +1015,8 @@ All Storage tests passed This utility checks that the blob storage provider for {% data variables.product.prodname_actions %} on {% data variables.location.product_location %} is valid when OpenID Connect (OIDC) is used. -{% note %} - -**Note:** This utility only works with configurations that use an OpenID Connect (OIDC) configuration. To test credentials-based configurations, use [`ghe-actions-precheck`](#ghe-actions-precheck). - -{% endnote %} +> [!NOTE] +> This utility only works with configurations that use an OpenID Connect (OIDC) configuration. To test credentials-based configurations, use [`ghe-actions-precheck`](#ghe-actions-precheck). ```shell ghe-actions-test-storage-with-oidc -p [PROVIDER] -cs ["CONNECTION-STRING"] @@ -1040,27 +1028,17 @@ ghe-actions-test-storage-with-oidc -p [PROVIDER] -cs ["CONNECTION-STRING"] This utility stops {% data variables.product.prodname_actions %} from running on {% data variables.location.product_location %}. -{% note %} - -**Notes**: - -* {% data reusables.enterprise_enterprise_support.support_will_ask_you_to_run_command %} -* In high availability configurations, run this command from the primary. - -{% endnote %} +> [!NOTE] +> * {% data reusables.enterprise_enterprise_support.support_will_ask_you_to_run_command %} +> * In high availability configurations, run this command from the primary. ### ghe-actions-start This utility starts {% data variables.product.prodname_actions %} on {% data variables.location.product_location %} after it has been previously stopped. -{% note %} - -**Notes**: - -* {% data reusables.enterprise_enterprise_support.support_will_ask_you_to_run_command %} -* In high availability configurations, run this command from the primary. - -{% endnote %} +> [!NOTE] +> * {% data reusables.enterprise_enterprise_support.support_will_ask_you_to_run_command %} +> * In high availability configurations, run this command from the primary. If your system is configured correctly, you'll see the following output: @@ -1367,11 +1345,10 @@ In this example, `ghe-repl-status -vv` sends verbose status information from a r During an upgrade to a feature release, this utility displays the status of background jobs on {% data variables.location.product_location %}. If you're running back-to-back upgrades, you should use this utility to check that all background jobs are complete before proceeding with the next upgrade. {% ifversion ghes < 3.12 %} -{% note %} -**Note:** To use `ghe-check-background-upgrade-jobs` with {% data variables.product.product_name %} {{ allVersions[currentVersion].currentRelease }}, your instance must run version {{ allVersions[currentVersion].currentRelease }}.{% ifversion ghes = 3.10 %}4{% elsif ghes = 3.11 %}1{% endif %} or later. +> [!NOTE] +> To use `ghe-check-background-upgrade-jobs` with {% data variables.product.product_name %} {{ allVersions[currentVersion].currentRelease }}, your instance must run version {{ allVersions[currentVersion].currentRelease }}.{% ifversion ghes = 3.10 %}4{% elsif ghes = 3.11 %}1{% endif %} or later. -{% endnote %} {% endif %} ```shell diff --git a/content/admin/backing-up-and-restoring-your-instance/configuring-backups-on-your-instance.md b/content/admin/backing-up-and-restoring-your-instance/configuring-backups-on-your-instance.md index 7027d74abeef..ffe3e3f20e29 100644 --- a/content/admin/backing-up-and-restoring-your-instance/configuring-backups-on-your-instance.md +++ b/content/admin/backing-up-and-restoring-your-instance/configuring-backups-on-your-instance.md @@ -66,11 +66,8 @@ If you have an existing backup configuration file, `backup.config`, ensure you c Backup snapshots created by {% data variables.product.prodname_enterprise_backup_utilities %} are written to the disk path set by the `GHE_DATA_DIR` data directory variable in your `backup.config` file. These snapshots need to be stored on a filesystem which supports symbolic and hard links. -{% note %} - -**Note:** We recommend ensuring your snapshots are not kept in a subdirectory of the {% data variables.product.prodname_enterprise_backup_utilities %} installation directory, to avoid inadvertently overwriting your data directory when upgrading {% data variables.product.prodname_enterprise_backup_utilities %} versions. - -{% endnote %} +> [!NOTE] +> We recommend ensuring your snapshots are not kept in a subdirectory of the {% data variables.product.prodname_enterprise_backup_utilities %} installation directory, to avoid inadvertently overwriting your data directory when upgrading {% data variables.product.prodname_enterprise_backup_utilities %} versions. 1. Download the relevant {% data variables.product.prodname_enterprise_backup_utilities %} release from the [Releases](https://github.com/github/backup-utils/releases) page of the github/backup-utils repository. @@ -97,13 +94,11 @@ Backup snapshots created by {% data variables.product.prodname_enterprise_backup 1. If you previously upgraded {% data variables.product.prodname_enterprise_backup_utilities %} using Git, ensure that you copy your existing configuration from `backup.config` into the new file. For more information, see "[Upgrading {% data variables.product.prodname_enterprise_backup_utilities %}](#upgrading-github-enterprise-server-backup-utilities)." 1. Set the `GHE_HOSTNAME` value to your primary {% data variables.product.prodname_ghe_server %} instance's hostname or IP address. - {% note %} - - **Note:** If {% data variables.location.product_location %} is deployed as a cluster or in a high availability configuration using a load balancer, the `GHE_HOSTNAME` can be the load balancer hostname, as long as the load balancer allows SSH access over port 122 to {% data variables.location.product_location %}. - - To ensure a recovered instance is immediately available, perform backups targeting the primary instance even in a geo-replication configuration. + > [!NOTE] + > If {% data variables.location.product_location %} is deployed as a cluster or in a high availability configuration using a load balancer, the `GHE_HOSTNAME` can be the load balancer hostname, as long as the load balancer allows SSH access over port 122 to {% data variables.location.product_location %}. + > + > To ensure a recovered instance is immediately available, perform backups targeting the primary instance even in a geo-replication configuration. - {% endnote %} 1. Set the `GHE_DATA_DIR` value to the filesystem location where you want to store backup snapshots. We recommend choosing a location on the same filesystem as your backup host. 1. To grant your backup host access to your instance, open your primary instance's settings page at `http(s)://HOSTNAME/setup/settings` and add the backup host's SSH key to the list of authorized SSH keys. For more information, see "[AUTOTITLE](/admin/configuration/configuring-your-enterprise/accessing-the-administrative-shell-ssh#enabling-access-to-the-administrative-shell-via-ssh)." 1. On your backup host, verify SSH connectivity with {% data variables.location.product_location %} with the `ghe-host-check` command. diff --git a/content/admin/configuring-packages/configuring-package-ecosystem-support-for-your-enterprise.md b/content/admin/configuring-packages/configuring-package-ecosystem-support-for-your-enterprise.md index 59d8454cd64b..ef37730d9c55 100644 --- a/content/admin/configuring-packages/configuring-package-ecosystem-support-for-your-enterprise.md +++ b/content/admin/configuring-packages/configuring-package-ecosystem-support-for-your-enterprise.md @@ -33,9 +33,10 @@ To prevent new packages from being uploaded, you can set an ecosystem you previo {% data reusables.enterprise_site_admin_settings.management-console %} {% data reusables.enterprise_site_admin_settings.packages-tab %} 1. Under "Ecosystem Toggles", for each package type, select **Enabled**, **Read-Only**, or **Disabled**. - {%- ifversion ghes %}{% note -%} - **Note**: Subdomain isolation must be enabled to toggle the {% data variables.product.prodname_container_registry %} options. - {%- endnote %}{%- endif %} + {%- ifversion ghes %} + > [!NOTE] + > Subdomain isolation must be enabled to toggle the {% data variables.product.prodname_container_registry %} options. + {%- endif %} ![Screenshot of the "Ecosystem toggles" section on the Settings page of the Management Console.](/assets/images/enterprise/site-admin-settings/ecosystem-toggles.png) {% data reusables.enterprise_management_console.save-settings %} diff --git a/content/admin/configuring-packages/enabling-github-packages-with-aws.md b/content/admin/configuring-packages/enabling-github-packages-with-aws.md index 7178fba96755..cbb4e8295035 100644 --- a/content/admin/configuring-packages/enabling-github-packages-with-aws.md +++ b/content/admin/configuring-packages/enabling-github-packages-with-aws.md @@ -14,14 +14,10 @@ redirect_from: - /admin/packages/enabling-github-packages-with-aws --- -{% warning %} - -**Warnings:** -* It is critical that you configure any restrictive access policies you need for your storage bucket, because {% data variables.product.company_short %} does not apply specific object permissions or additional access control lists (ACLs) to your storage bucket configuration. For example, if you make your bucket public, data in the bucket will be accessible to the public internet. For more information, see "[Setting bucket and object access permissions](https://docs.aws.amazon.com/AmazonS3/latest/user-guide/set-permissions.html)" in the AWS Documentation. If restrictions by IP address have been set up, please include IP addresses for {% data variables.location.product_location_enterprise %} and the end users who will be using the {% data variables.location.product_location_enterprise %}. -* We recommend using a dedicated bucket for {% data variables.product.prodname_registry %}, separate from the bucket you use for {% data variables.product.prodname_actions %} storage. -* Make sure to configure the bucket you'll want to use in the future. We do not recommend changing your storage after you start using {% data variables.product.prodname_registry %}. - -{% endwarning %} +> [!WARNING] +> * It is critical that you configure any restrictive access policies you need for your storage bucket, because {% data variables.product.company_short %} does not apply specific object permissions or additional access control lists (ACLs) to your storage bucket configuration. For example, if you make your bucket public, data in the bucket will be accessible to the public internet. For more information, see "[Setting bucket and object access permissions](https://docs.aws.amazon.com/AmazonS3/latest/user-guide/set-permissions.html)" in the AWS Documentation. If restrictions by IP address have been set up, please include IP addresses for {% data variables.location.product_location_enterprise %} and the end users who will be using the {% data variables.location.product_location_enterprise %}. +> * We recommend using a dedicated bucket for {% data variables.product.prodname_registry %}, separate from the bucket you use for {% data variables.product.prodname_actions %} storage. +> * Make sure to configure the bucket you'll want to use in the future. We do not recommend changing your storage after you start using {% data variables.product.prodname_registry %}. ## Prerequisites diff --git a/content/admin/configuring-packages/enabling-github-packages-with-azure-blob-storage.md b/content/admin/configuring-packages/enabling-github-packages-with-azure-blob-storage.md index c1a2e066cf8b..134cd94d43c5 100644 --- a/content/admin/configuring-packages/enabling-github-packages-with-azure-blob-storage.md +++ b/content/admin/configuring-packages/enabling-github-packages-with-azure-blob-storage.md @@ -13,14 +13,10 @@ redirect_from: - /admin/packages/enabling-github-packages-with-azure-blob-storage --- -{% warning %} - -**Warnings:** -* It is critical that you set the restrictive access policies you need for your storage bucket, because {% data variables.product.company_short %} does not apply specific object permissions or additional access control lists (ACLs) to your storage bucket configuration. For example, if you make your bucket public, data in the bucket will be accessible on the public internet. If restrictions by IP address have been set up, please include IP addresses for {% data variables.location.product_location_enterprise %} and the end users who will be using the {% data variables.location.product_location_enterprise %}. -* We recommend using a dedicated bucket for {% data variables.product.prodname_registry %}, separate from the bucket you use for {% data variables.product.prodname_actions %} storage. -* Make sure to configure the bucket you'll want to use in the future. We do not recommend changing your storage after you start using {% data variables.product.prodname_registry %}. - -{% endwarning %} +> [!WARNING] +> * It is critical that you set the restrictive access policies you need for your storage bucket, because {% data variables.product.company_short %} does not apply specific object permissions or additional access control lists (ACLs) to your storage bucket configuration. For example, if you make your bucket public, data in the bucket will be accessible on the public internet. If restrictions by IP address have been set up, please include IP addresses for {% data variables.location.product_location_enterprise %} and the end users who will be using the {% data variables.location.product_location_enterprise %}. +> * We recommend using a dedicated bucket for {% data variables.product.prodname_registry %}, separate from the bucket you use for {% data variables.product.prodname_actions %} storage. +> * Make sure to configure the bucket you'll want to use in the future. We do not recommend changing your storage after you start using {% data variables.product.prodname_registry %}. ## Prerequisites @@ -36,13 +32,10 @@ Before you can enable and configure {% data variables.product.prodname_registry You must create a storage container prior to setting the container name and connection string. - {% note %} - - **Note:** You can find your Azure Connection String by navigating to the Access Key menu in your Azure storage account. - - Usage of a SAS Token or SAS URL as connection string is not currently supported. - - {% endnote %} + > [!NOTE] + > You can find your Azure Connection String by navigating to the Access Key menu in your Azure storage account. + > + > Usage of a SAS Token or SAS URL as connection string is not currently supported. {% data reusables.enterprise_management_console.save-settings %} diff --git a/content/admin/configuring-packages/enabling-github-packages-with-minio.md b/content/admin/configuring-packages/enabling-github-packages-with-minio.md index 770e6687e9b2..5325769f37b7 100644 --- a/content/admin/configuring-packages/enabling-github-packages-with-minio.md +++ b/content/admin/configuring-packages/enabling-github-packages-with-minio.md @@ -13,14 +13,11 @@ redirect_from: - /admin/packages/enabling-github-packages-with-minio --- -{% warning %} - -**Warnings:** -* It is critical that you set the restrictive access policies you need for your storage bucket, because {% data variables.product.company_short %} does not apply specific object permissions or additional access control lists (ACLs) to your storage bucket configuration. For example, if you make your bucket public, data in the bucket will be accessible on the public internet. If restrictions by IP address have been set up, please include IP addresses for {% data variables.location.product_location_enterprise %} and the end users who will be using the {% data variables.location.product_location_enterprise %}. -* We recommend using a dedicated bucket for {% data variables.product.prodname_registry %}, separate from the bucket you use for {% data variables.product.prodname_actions %} storage. -* Make sure to configure the bucket you'll want to use in the future. We do not recommend changing your storage after you start using {% data variables.product.prodname_registry %}. -* We recommend configuring the TLS for the bucket to avoid possible issues with Package Registry, for example, downloading from NuGet Registry. -{% endwarning %} +> [!WARNING] +> * It is critical that you set the restrictive access policies you need for your storage bucket, because {% data variables.product.company_short %} does not apply specific object permissions or additional access control lists (ACLs) to your storage bucket configuration. For example, if you make your bucket public, data in the bucket will be accessible on the public internet. If restrictions by IP address have been set up, please include IP addresses for {% data variables.location.product_location_enterprise %} and the end users who will be using the {% data variables.location.product_location_enterprise %}. +> * We recommend using a dedicated bucket for {% data variables.product.prodname_registry %}, separate from the bucket you use for {% data variables.product.prodname_actions %} storage. +> * Make sure to configure the bucket you'll want to use in the future. We do not recommend changing your storage after you start using {% data variables.product.prodname_registry %}. +> * We recommend configuring the TLS for the bucket to avoid possible issues with Package Registry, for example, downloading from NuGet Registry. ## Prerequisites diff --git a/content/admin/configuring-settings/configuring-github-connect/about-github-connect.md b/content/admin/configuring-settings/configuring-github-connect/about-github-connect.md index cb8635f296d2..b5dc117f6f72 100644 --- a/content/admin/configuring-settings/configuring-github-connect/about-github-connect.md +++ b/content/admin/configuring-settings/configuring-github-connect/about-github-connect.md @@ -71,11 +71,8 @@ If you're connecting to an enterprise on {% data variables.enterprise.data_resid When {% data variables.product.prodname_github_connect %} is enabled, a record on {% data variables.product.prodname_ghe_cloud %} stores information about the connection. If you enable individual features of {% data variables.product.prodname_github_connect %}, additional data is transmitted. -{% note %} - -**Note:** No repositories, issues, or pull requests are ever transmitted from {% data variables.product.product_name %} to {% data variables.product.prodname_ghe_cloud %} by {% data variables.product.prodname_github_connect %}. - -{% endnote %} +> [!NOTE] +> No repositories, issues, or pull requests are ever transmitted from {% data variables.product.product_name %} to {% data variables.product.prodname_ghe_cloud %} by {% data variables.product.prodname_github_connect %}. ### Data transmitted when {% data variables.product.prodname_github_connect %} is enabled diff --git a/content/admin/configuring-settings/configuring-github-connect/enabling-dependabot-for-your-enterprise.md b/content/admin/configuring-settings/configuring-github-connect/enabling-dependabot-for-your-enterprise.md index 69df4714912c..e2cdee276458 100644 --- a/content/admin/configuring-settings/configuring-github-connect/enabling-dependabot-for-your-enterprise.md +++ b/content/admin/configuring-settings/configuring-github-connect/enabling-dependabot-for-your-enterprise.md @@ -41,11 +41,8 @@ After you {% ifversion dependabot-alerts-ghes-enablement %} set up {% data varia You can also choose to manually sync vulnerability data at any time. For more information, see "[AUTOTITLE](/admin/code-security/managing-supply-chain-security-for-your-enterprise/viewing-the-vulnerability-data-for-your-enterprise)." -{% note %} - -**Note:** When you enable {% data variables.product.prodname_dependabot_alerts %}, no code or information about code from {% data variables.product.prodname_ghe_server %} is uploaded to {% data variables.product.prodname_dotcom_the_website %}. - -{% endnote %} +> [!NOTE] +> When you enable {% data variables.product.prodname_dependabot_alerts %}, no code or information about code from {% data variables.product.prodname_ghe_server %} is uploaded to {% data variables.product.prodname_dotcom_the_website %}. When {% data variables.product.prodname_ghe_server %} receives information about a vulnerability, it identifies repositories that use the affected version of the dependency and generates {% data variables.product.prodname_dependabot_alerts %}. You can choose whether or not to notify users automatically about new {% data variables.product.prodname_dependabot_alerts %}. @@ -55,11 +52,8 @@ For repositories with {% data variables.product.prodname_dependabot_alerts %} en After you enable {% data variables.product.prodname_dependabot_alerts %}, you can choose to enable {% data variables.product.prodname_dependabot_updates %}. When {% data variables.product.prodname_dependabot_updates %} are enabled for {% data variables.product.prodname_ghe_server %}, users can configure repositories so that their dependencies are updated and kept secure automatically. -{% note %} - -**Note:** {% data variables.product.prodname_dependabot_updates %} on {% data variables.product.product_name %} requires {% data variables.product.prodname_actions %} with self-hosted runners. - -{% endnote %} +> [!NOTE] +> {% data variables.product.prodname_dependabot_updates %} on {% data variables.product.product_name %} requires {% data variables.product.prodname_actions %} with self-hosted runners. By default, {% data variables.product.prodname_actions %} runners used by {% data variables.product.prodname_dependabot %} need access to the internet, to download updated packages from upstream package managers. For {% data variables.product.prodname_dependabot_updates %} powered by {% data variables.product.prodname_github_connect %}, internet access provides your runners with a token that allows access to dependencies and advisories hosted on {% data variables.product.prodname_dotcom_the_website %}. @@ -106,11 +100,8 @@ Before you can enable {% data variables.product.prodname_dependabot_updates %}: {% ifversion ghes %} -{% note %} - -**Note:** After you enable the dependency graph, you can use the [{% data variables.product.prodname_dependabot %} action](https://github.com/github/dependabot-action). The action will raise an error if any vulnerabilities or invalid licenses are being introduced. {% data reusables.actions.action-bundled-actions %} - -{% endnote %} +> [!NOTE] +> After you enable the dependency graph, you can use the [{% data variables.product.prodname_dependabot %} action](https://github.com/github/dependabot-action). The action will raise an error if any vulnerabilities or invalid licenses are being introduced. {% data reusables.actions.action-bundled-actions %} {% endif %} diff --git a/content/admin/configuring-settings/configuring-network-settings/changing-the-hostname-for-your-instance.md b/content/admin/configuring-settings/configuring-network-settings/changing-the-hostname-for-your-instance.md index 61ad17646723..81f1c1b14242 100644 --- a/content/admin/configuring-settings/configuring-network-settings/changing-the-hostname-for-your-instance.md +++ b/content/admin/configuring-settings/configuring-network-settings/changing-the-hostname-for-your-instance.md @@ -38,9 +38,7 @@ In this article, the term "source instance" refers to the instance with the old 1. Optionally, while the destination instance is in maintenance mode, validate the instance's configuration and verify that user data is intact. For more information, see "[AUTOTITLE](/admin/administering-your-instance/configuring-maintenance-mode/enabling-and-scheduling-maintenance-mode#validating-changes-in-maintenance-mode-using-the-ip-exception-list)." 1. To direct traffic to the destination instance, update the DNS `CNAME` record with the source instance's hostname to resolve to the IP address of the destination instance. - {% note %} + > [!NOTE] + > Restored user-generated content in the instance's web application will likely contain URLs that reference the source instance's old hostname. Optionally, to ensure that these links continue to resolve to the destination instance, you can configure a redirect using DNS. In addition to the `CNAME` record that resolves to the new instance's hostname, configure a second DNS `CNAME` record that directs traffic from the original hostname to the new hostname. For more information, see the documentation for your DNS provider. - **Note**: Restored user-generated content in the instance's web application will likely contain URLs that reference the source instance's old hostname. Optionally, to ensure that these links continue to resolve to the destination instance, you can configure a redirect using DNS. In addition to the `CNAME` record that resolves to the new instance's hostname, configure a second DNS `CNAME` record that directs traffic from the original hostname to the new hostname. For more information, see the documentation for your DNS provider. - - {% endnote %} 1. On the destination instance, disable maintenance mode. diff --git a/content/admin/configuring-settings/configuring-network-settings/configuring-an-outbound-web-proxy-server.md b/content/admin/configuring-settings/configuring-network-settings/configuring-an-outbound-web-proxy-server.md index 074f12af7c80..669796928037 100644 --- a/content/admin/configuring-settings/configuring-network-settings/configuring-an-outbound-web-proxy-server.md +++ b/content/admin/configuring-settings/configuring-network-settings/configuring-an-outbound-web-proxy-server.md @@ -23,11 +23,8 @@ shortTitle: Configure an outbound proxy When a proxy server is enabled for {% data variables.location.product_location %}, outbound messages sent by {% data variables.product.prodname_ghe_server %} are first sent through the proxy server, unless the destination host is added as an HTTP proxy exclusion. Types of outbound messages include outgoing webhooks, uploading bundles, and fetching legacy avatars. The proxy server's URL is the protocol, domain or IP address, plus the port number, for example `http://127.0.0.1:8123`. -{% note %} - -**Note:** To connect {% data variables.location.product_location %} to {% data variables.product.prodname_dotcom_the_website %}, your proxy configuration must allow connectivity to `github.com` and `api.github.com`. For more information, see "[AUTOTITLE](/admin/configuration/configuring-github-connect/managing-github-connect)." - -{% endnote %} +> [!NOTE] +> To connect {% data variables.location.product_location %} to {% data variables.product.prodname_dotcom_the_website %}, your proxy configuration must allow connectivity to `github.com` and `api.github.com`. For more information, see "[AUTOTITLE](/admin/configuration/configuring-github-connect/managing-github-connect)." {% data reusables.actions.proxy-considerations %} For more information about using {% data variables.product.prodname_actions %} with {% data variables.product.prodname_ghe_server %}, see "[AUTOTITLE](/admin/github-actions/getting-started-with-github-actions-for-your-enterprise/getting-started-with-github-actions-for-github-enterprise-server)." diff --git a/content/admin/configuring-settings/configuring-network-settings/configuring-built-in-firewall-rules.md b/content/admin/configuring-settings/configuring-network-settings/configuring-built-in-firewall-rules.md index c93e97066122..c286999766a3 100644 --- a/content/admin/configuring-settings/configuring-network-settings/configuring-built-in-firewall-rules.md +++ b/content/admin/configuring-settings/configuring-network-settings/configuring-built-in-firewall-rules.md @@ -62,11 +62,8 @@ We do not recommend customizing UFW as it can complicate some troubleshooting is ## Adding custom firewall rules -{% warning %} - -**Warning:** Before you add custom firewall rules, back up your current rules in case you need to reset to a known working state. If you're locked out of your server, visit {% data variables.contact.contact_ent_support %} and contact us to reconfigure the original firewall rules. Restoring the original firewall rules involves downtime for your server. - -{% endwarning %} +> [!WARNING] +> Before you add custom firewall rules, back up your current rules in case you need to reset to a known working state. If you're locked out of your server, visit {% data variables.contact.contact_ent_support %} and contact us to reconfigure the original firewall rules. Restoring the original firewall rules involves downtime for your server. 1. Configure a custom firewall rule. 1. Check the status of each new rule with the `status numbered` command. @@ -87,11 +84,8 @@ After you upgrade {% data variables.location.product_location %}, you must reapp If something goes wrong after you change the firewall rules, you can reset the rules from your original backup. -{% warning %} - -**Warning:** If you didn't back up the original rules before making changes to the firewall, visit {% data variables.contact.contact_ent_support %} and contact us for further assistance. - -{% endwarning %} +> [!WARNING] +> If you didn't back up the original rules before making changes to the firewall, visit {% data variables.contact.contact_ent_support %} and contact us for further assistance. {% data reusables.enterprise_installation.ssh-into-instance %} diff --git a/content/admin/configuring-settings/configuring-network-settings/configuring-the-ip-address-using-the-virtual-machine-console.md b/content/admin/configuring-settings/configuring-network-settings/configuring-the-ip-address-using-the-virtual-machine-console.md index b50d5c15c1ba..83c28aea29c1 100644 --- a/content/admin/configuring-settings/configuring-network-settings/configuring-the-ip-address-using-the-virtual-machine-console.md +++ b/content/admin/configuring-settings/configuring-network-settings/configuring-the-ip-address-using-the-virtual-machine-console.md @@ -16,11 +16,9 @@ topics: - Networking shortTitle: Set the IP using the console --- -{% note %} -**Note:** We do not support adding additional network adapters to {% data variables.product.prodname_ghe_server %}. - -{% endnote %} +> [!NOTE] +> We do not support adding additional network adapters to {% data variables.product.prodname_ghe_server %}. {% data reusables.enterprise_installation.open-vm-console-start %} 1. Choose to configure the `IPv4` or `IPv6` protocol. diff --git a/content/admin/configuring-settings/configuring-network-settings/configuring-time-synchronization.md b/content/admin/configuring-settings/configuring-network-settings/configuring-time-synchronization.md index 14f629ac0bfa..33cc6ff35767 100644 --- a/content/admin/configuring-settings/configuring-network-settings/configuring-time-synchronization.md +++ b/content/admin/configuring-settings/configuring-network-settings/configuring-time-synchronization.md @@ -35,13 +35,9 @@ shortTitle: Configure time settings The NTP protocol continuously corrects small time synchronization discrepancies. You can use the administrative shell to synchronize time immediately. -{% note %} - -**Notes:** -* You can't modify the Coordinated Universal Time (UTC) zone. -* You should prevent your hypervisor from trying to set the virtual machine's clock. For more information, see the documentation provided by the virtualization provider. - -{% endnote %} +> [!NOTE] +> * You can't modify the Coordinated Universal Time (UTC) zone. +> * You should prevent your hypervisor from trying to set the virtual machine's clock. For more information, see the documentation provided by the virtualization provider. * Use the `chronyc` command to synchronize the server with the configured NTP server. For example: diff --git a/content/admin/configuring-settings/configuring-network-settings/using-github-enterprise-server-with-a-load-balancer.md b/content/admin/configuring-settings/configuring-network-settings/using-github-enterprise-server-with-a-load-balancer.md index 8e2c4f5e2b70..31e34863986f 100644 --- a/content/admin/configuring-settings/configuring-network-settings/using-github-enterprise-server-with-a-load-balancer.md +++ b/content/admin/configuring-settings/configuring-network-settings/using-github-enterprise-server-with-a-load-balancer.md @@ -52,11 +52,8 @@ We strongly recommend enabling PROXY protocol support for both your instance and {% data reusables.enterprise_clustering.x-forwarded-for %} -{% warning %} - -**Warning**: If you configure `X-Forwarded-For` support on {% data variables.location.product_location %} and load balancer, you may not be able to connect to the {% data variables.enterprise.management_console %}. For more information, see "[AUTOTITLE](/admin/configuration/configuring-network-settings/using-github-enterprise-server-with-a-load-balancer#error-your-session-has-expired-for-connections-to-the-management-console)." - -{% endwarning %} +> [!WARNING] +> If you configure `X-Forwarded-For` support on {% data variables.location.product_location %} and load balancer, you may not be able to connect to the {% data variables.enterprise.management_console %}. For more information, see "[AUTOTITLE](/admin/configuration/configuring-network-settings/using-github-enterprise-server-with-a-load-balancer#error-your-session-has-expired-for-connections-to-the-management-console)." {% data reusables.enterprise_site_admin_settings.access-settings %} {% data reusables.enterprise_site_admin_settings.management-console %} @@ -77,11 +74,8 @@ Health checks allow a load balancer to stop sending traffic to a node that is no If you cannot connect to services on {% data variables.location.product_location %} through a load balancer, you can review the following information to troubleshoot the problem. -{% note %} - -**Note**: Always test changes to your network infrastructure and instance configuration in a staging environment. For more information, see "[AUTOTITLE](/admin/installation/setting-up-a-github-enterprise-server-instance/setting-up-a-staging-instance)." - -{% endnote %} +> [!NOTE] +> Always test changes to your network infrastructure and instance configuration in a staging environment. For more information, see "[AUTOTITLE](/admin/installation/setting-up-a-github-enterprise-server-instance/setting-up-a-staging-instance)." ### Error: "Your session has expired" for connections to the {% data variables.enterprise.management_console %} diff --git a/content/admin/configuring-settings/configuring-private-networking-for-hosted-compute-products/configuring-private-networking-for-github-hosted-runners-in-your-enterprise.md b/content/admin/configuring-settings/configuring-private-networking-for-hosted-compute-products/configuring-private-networking-for-github-hosted-runners-in-your-enterprise.md index 76a24fbd81c6..6ec038bbaf77 100644 --- a/content/admin/configuring-settings/configuring-private-networking-for-hosted-compute-products/configuring-private-networking-for-github-hosted-runners-in-your-enterprise.md +++ b/content/admin/configuring-settings/configuring-private-networking-for-hosted-compute-products/configuring-private-networking-for-github-hosted-runners-in-your-enterprise.md @@ -99,11 +99,8 @@ You can use the following {% data variables.product.prodname_cli %} commands to ### 2. Create a runner group for your enterprise -{% note %} - -**Note:** For the runner group to be accessible by repositories within your organizations, those repositories must have access to that runner group at the organization level. For more information, see "[AUTOTITLE](/actions/using-github-hosted-runners/controlling-access-to-larger-runners#changing-which-repositories-can-access-a-runner-group)." - -{% endnote %} +> [!NOTE] +> For the runner group to be accessible by repositories within your organizations, those repositories must have access to that runner group at the organization level. For more information, see "[AUTOTITLE](/actions/using-github-hosted-runners/controlling-access-to-larger-runners#changing-which-repositories-can-access-a-runner-group)." 1. Create a new runner group for your enterprise. For more information about how to create a runner group, see "[AUTOTITLE](/actions/using-github-hosted-runners/controlling-access-to-larger-runners#creating-a-runner-group-for-an-enterprise)." {% data reusables.actions.workflows.runner-groups-enterprise-organization-access %} @@ -112,11 +109,8 @@ You can use the following {% data variables.product.prodname_cli %} commands to ### 3. Add the {% data variables.product.company_short %}-hosted runner to the enterprise runner group -{% note %} - -**Note:** When adding your {% data variables.product.company_short %}-hosted runner to a runner group, select the runner group you created in the previous procedures. - -{% endnote %} +> [!NOTE] +> When adding your {% data variables.product.company_short %}-hosted runner to a runner group, select the runner group you created in the previous procedures. 1. Add the {% data variables.product.company_short %}-hosted runner to the runner group. For more information, see "[AUTOTITLE](/enterprise-cloud@latest/actions/using-github-hosted-runners/managing-larger-runners#adding-a-larger-runner-to-an-enterprise)." diff --git a/content/admin/configuring-settings/configuring-user-applications-for-your-enterprise/configuring-email-for-notifications.md b/content/admin/configuring-settings/configuring-user-applications-for-your-enterprise/configuring-email-for-notifications.md index 04baf66b5699..ec28420421c6 100644 --- a/content/admin/configuring-settings/configuring-user-applications-for-your-enterprise/configuring-email-for-notifications.md +++ b/content/admin/configuring-settings/configuring-user-applications-for-your-enterprise/configuring-email-for-notifications.md @@ -45,11 +45,8 @@ settings to allow incoming emails](#configuring-dns-and-firewall-settings-to-all 1. Under "Send test email to," type an address to send the test email to. 1. Click **Send test email**. - {% tip %} - - **Tip:** If SMTP errors occur while sending a test email—such as an immediate delivery failure or an outgoing mail configuration error—you will see them in the Test email settings dialog box. - - {% endtip %} + > [!TIP] + > If SMTP errors occur while sending a test email—such as an immediate delivery failure or an outgoing mail configuration error—you will see them in the Test email settings dialog box. 1. If the test email fails, [troubleshoot your email settings](#troubleshooting-email-delivery). 1. When the test email succeeds, under the "Settings" sidebar, click **Save settings**. diff --git a/content/admin/configuring-settings/configuring-user-applications-for-your-enterprise/configuring-github-pages-for-your-enterprise.md b/content/admin/configuring-settings/configuring-user-applications-for-your-enterprise/configuring-github-pages-for-your-enterprise.md index ae60184401a5..2e7dddcf5bb6 100644 --- a/content/admin/configuring-settings/configuring-user-applications-for-your-enterprise/configuring-github-pages-for-your-enterprise.md +++ b/content/admin/configuring-settings/configuring-user-applications-for-your-enterprise/configuring-github-pages-for-your-enterprise.md @@ -24,11 +24,8 @@ shortTitle: Configure GitHub Pages If private mode is enabled on your enterprise, the public cannot access {% data variables.product.prodname_pages %} sites hosted by your enterprise unless you enable public sites. -{% warning %} - -**Warning:** If you enable public sites for {% data variables.product.prodname_pages %}, every site in every repository on your enterprise will be accessible to the public. - -{% endwarning %} +> [!WARNING] +> If you enable public sites for {% data variables.product.prodname_pages %}, every site in every repository on your enterprise will be accessible to the public. {% data reusables.enterprise_site_admin_settings.access-settings %} {% data reusables.enterprise_site_admin_settings.management-console %} @@ -50,11 +47,8 @@ If subdomain isolation is disabled for your enterprise, you should also disable You can add or override response headers for {% data variables.product.prodname_pages %} sites hosted by {% data variables.location.product_location %}. -{% warning %} - -**Warning:** Ensure that your response headers are properly configured before saving. Improper configurations may negatively impact the security of {% data variables.location.product_location %}. - -{% endwarning %} +> [!WARNING] +> Ensure that your response headers are properly configured before saving. Improper configurations may negatively impact the security of {% data variables.location.product_location %}. {% data reusables.enterprise_site_admin_settings.access-settings %} {% data reusables.enterprise_site_admin_settings.management-console %} diff --git a/content/admin/configuring-settings/configuring-user-applications-for-your-enterprise/configuring-interactive-maps.md b/content/admin/configuring-settings/configuring-user-applications-for-your-enterprise/configuring-interactive-maps.md index e4e43707b6fc..ff0749cd8426 100644 --- a/content/admin/configuring-settings/configuring-user-applications-for-your-enterprise/configuring-interactive-maps.md +++ b/content/admin/configuring-settings/configuring-user-applications-for-your-enterprise/configuring-interactive-maps.md @@ -21,11 +21,8 @@ To enable interactive maps, you must provide authentication credentials for Azur {% ifversion azure-maps-auth-2023 %} {% ifversion ghes < 3.13 %} -{% warning %} - -**Warning**: Authentication with Azure Maps using an API token is {% data variables.release-phases.retired %} in {% data variables.product.product_name %} {{ allVersions[currentVersion].currentRelease }}.{% ifversion ghes = 3.10 %}4{% elsif ghes = 3.11 %}1{% endif %} and later. If you upgrade to the latest release of {% data variables.product.product_name %} on an instance already configured to authenticate with an API token, interactive maps will be disabled. You must reconfigure authentication using role-based access control (RBAC) for an application on a Microsoft Entra ID (previously known as Azure AD) tenant. {% data reusables.enterprise.azure-maps-auth-deprecation-link %} - -{% endwarning %} +> [!WARNING] +> Authentication with Azure Maps using an API token is {% data variables.release-phases.retired %} in {% data variables.product.product_name %} {{ allVersions[currentVersion].currentRelease }}.{% ifversion ghes = 3.10 %}4{% elsif ghes = 3.11 %}1{% endif %} and later. If you upgrade to the latest release of {% data variables.product.product_name %} on an instance already configured to authenticate with an API token, interactive maps will be disabled. You must reconfigure authentication using role-based access control (RBAC) for an application on a Microsoft Entra ID (previously known as Azure AD) tenant. {% data reusables.enterprise.azure-maps-auth-deprecation-link %} {% endif %} {% endif %} diff --git a/content/admin/configuring-settings/configuring-user-applications-for-your-enterprise/configuring-rate-limits.md b/content/admin/configuring-settings/configuring-user-applications-for-your-enterprise/configuring-rate-limits.md index 9adbd0f80cb9..5ec5be83923a 100644 --- a/content/admin/configuring-settings/configuring-user-applications-for-your-enterprise/configuring-rate-limits.md +++ b/content/admin/configuring-settings/configuring-user-applications-for-your-enterprise/configuring-rate-limits.md @@ -33,11 +33,8 @@ Excessive numbers of requests to the {% data variables.product.prodname_enterpri You can exempt a list of users from API rate limits using the `ghe-config` utility in the administrative shell. For more information, see "[AUTOTITLE](/admin/configuration/configuring-your-enterprise/command-line-utilities#ghe-config)." {% endif %} -{% note %} - -**Note:** The {% data variables.enterprise.management_console %} lists the time period (per minute or per hour) for each rate limit. - -{% endnote %} +> [!NOTE] +> The {% data variables.enterprise.management_console %} lists the time period (per minute or per hour) for each rate limit. {% data reusables.enterprise_site_admin_settings.access-settings %} {% data reusables.enterprise_site_admin_settings.management-console %} @@ -63,11 +60,8 @@ Setting secondary rate limits protects the overall level of service on {% data v If a member of {% data variables.product.company_short %}'s staff has recommended it, you can apply Git rate limits per repository network or per user ID. Git rate limits are expressed in concurrent operations per minute, and are adaptive based on the current CPU load. -{% warning %} - -**Warning:** We encourage you to leave this setting disabled unless directly recommended by a member of {% data variables.product.company_short %}'s staff. Git operations are rarely the leading driver of CPU and RAM usage. Enabling this feature can make Git operations more likely to fail under high load conditions but does not address the underlying cause of those conditions. - -{% endwarning %} +> [!WARNING] +> We encourage you to leave this setting disabled unless directly recommended by a member of {% data variables.product.company_short %}'s staff. Git operations are rarely the leading driver of CPU and RAM usage. Enabling this feature can make Git operations more likely to fail under high load conditions but does not address the underlying cause of those conditions. {% data reusables.enterprise_site_admin_settings.access-settings %} {% data reusables.enterprise_site_admin_settings.management-console %} diff --git a/content/admin/configuring-settings/configuring-user-applications-for-your-enterprise/configuring-web-commit-signing.md b/content/admin/configuring-settings/configuring-user-applications-for-your-enterprise/configuring-web-commit-signing.md index 3637844bc4b1..134ff8b81552 100644 --- a/content/admin/configuring-settings/configuring-user-applications-for-your-enterprise/configuring-web-commit-signing.md +++ b/content/admin/configuring-settings/configuring-user-applications-for-your-enterprise/configuring-web-commit-signing.md @@ -44,11 +44,9 @@ You can enable web commit signing, rotate the private key used for web commit si {% data reusables.enterprise_site_admin_settings.email-settings %} 1. Under "No-reply email address", type the same email address you used when creating the PGP key. - {% note %} + > [!NOTE] + > The "No-reply email address" field will only be displayed if you've enabled email for {% data variables.location.product_location %}. For more information, see "[AUTOTITLE](/admin/configuration/configuring-your-enterprise/configuring-email-for-notifications#configuring-smtp-for-your-enterprise)." - **Note:** The "No-reply email address" field will only be displayed if you've enabled email for {% data variables.location.product_location %}. For more information, see "[AUTOTITLE](/admin/configuration/configuring-your-enterprise/configuring-email-for-notifications#configuring-smtp-for-your-enterprise)." - - {% endnote %} {% data reusables.enterprise_management_console.save-settings %} ## Rotating the private key used for web commit signing diff --git a/content/admin/configuring-settings/hardening-security-for-your-enterprise/configuring-host-keys-for-your-instance.md b/content/admin/configuring-settings/hardening-security-for-your-enterprise/configuring-host-keys-for-your-instance.md index fc5826260095..f8b5892a6c8b 100644 --- a/content/admin/configuring-settings/hardening-security-for-your-enterprise/configuring-host-keys-for-your-instance.md +++ b/content/admin/configuring-settings/hardening-security-for-your-enterprise/configuring-host-keys-for-your-instance.md @@ -26,11 +26,8 @@ Servers that accept SSH connections advertise one or more cryptographic host key By default, {% data variables.location.product_location %} generates and advertises host keys with OpenSSH-style host key rotation. To increase the security of SSH in your environment, you can enable additional algorithms for the generation of host keys. -{% note %} - -**Note**: If you enable additional host key algorithms, clients that do not use OpenSSH for SSH connections may experience warnings during connection, or fail to connect entirely. Some SSH implementations can ignore unsupported algorithms and fall back to a different algorithm. If the client does not support fallback, the connection will fail. For example, the SSH library for Go does not support fallback to a different algorithm. - -{% endnote %} +> [!NOTE] +> If you enable additional host key algorithms, clients that do not use OpenSSH for SSH connections may experience warnings during connection, or fail to connect entirely. Some SSH implementations can ignore unsupported algorithms and fall back to a different algorithm. If the client does not support fallback, the connection will fail. For example, the SSH library for Go does not support fallback to a different algorithm. ## Managing an Ed25519 host key diff --git a/content/admin/configuring-settings/hardening-security-for-your-enterprise/configuring-the-referrer-policy-for-your-enterprise.md b/content/admin/configuring-settings/hardening-security-for-your-enterprise/configuring-the-referrer-policy-for-your-enterprise.md index 713e4819ecdf..640cc13a2118 100644 --- a/content/admin/configuring-settings/hardening-security-for-your-enterprise/configuring-the-referrer-policy-for-your-enterprise.md +++ b/content/admin/configuring-settings/hardening-security-for-your-enterprise/configuring-the-referrer-policy-for-your-enterprise.md @@ -27,11 +27,8 @@ You can control the information that {% data variables.product.product_name %} s You can enable the `same-origin` referrer policy to instruct modern browsers to exclude the hostname for {% data variables.location.product_location %} from requests to external websites. The setting applies to all links from the web interface on your instance. By default, {% data variables.product.product_name %} uses the `origin-when-cross-origin` and `strict-origin-when-cross-origin` referrer policies, which means your instance's hostname will appear in HTTP and HTTPS requests to external websites. -{% note %} - -**Note**: Changing the referrer policy to `same-origin` can affect external sites that expect a hostname in the HTTP headers for a request. - -{% endnote %} +> [!NOTE] +> Changing the referrer policy to `same-origin` can affect external sites that expect a hostname in the HTTP headers for a request. {% data reusables.enterprise-accounts.access-enterprise %} {% data reusables.enterprise-accounts.settings-tab %} diff --git a/content/admin/configuring-settings/hardening-security-for-your-enterprise/restricting-network-traffic-to-your-enterprise-with-an-ip-allow-list.md b/content/admin/configuring-settings/hardening-security-for-your-enterprise/restricting-network-traffic-to-your-enterprise-with-an-ip-allow-list.md index 185d0d92d635..09ed0f62eacb 100644 --- a/content/admin/configuring-settings/hardening-security-for-your-enterprise/restricting-network-traffic-to-your-enterprise-with-an-ip-allow-list.md +++ b/content/admin/configuring-settings/hardening-security-for-your-enterprise/restricting-network-traffic-to-your-enterprise-with-an-ip-allow-list.md @@ -119,11 +119,8 @@ To ensure seamless use of the OIDC CAP while still applying the policy to OAuth ## Using your identity provider's allow list -{% note %} - -**Note:** Using your IdP's allow list is only supported for {% data variables.product.prodname_emus %} with Entra ID and OIDC. - -{% endnote %} +> [!NOTE] +> Using your IdP's allow list is only supported for {% data variables.product.prodname_emus %} with Entra ID and OIDC. {% data reusables.enterprise-accounts.access-enterprise %} {% data reusables.profile.org_settings %} diff --git a/content/admin/configuring-settings/index.md b/content/admin/configuring-settings/index.md index e7c74900b669..28aeda3524e3 100644 --- a/content/admin/configuring-settings/index.md +++ b/content/admin/configuring-settings/index.md @@ -20,9 +20,8 @@ children: - /configuring-github-connect --- {% ifversion ghes %} -{% note %} -**Note:** To configure {% data variables.product.prodname_actions %} or {% data variables.product.prodname_registry %} for your enterprise, see "[AUTOTITLE](/admin/github-actions)" or "[AUTOTITLE](/admin/packages)." +> [!NOTE] +> To configure {% data variables.product.prodname_actions %} or {% data variables.product.prodname_registry %} for your enterprise, see "[AUTOTITLE](/admin/github-actions)" or "[AUTOTITLE](/admin/packages)." -{% endnote %} {% endif %} diff --git a/content/admin/enforcing-policies/enforcing-policies-for-your-enterprise/enforcing-policies-for-github-codespaces-in-your-enterprise.md b/content/admin/enforcing-policies/enforcing-policies-for-your-enterprise/enforcing-policies-for-github-codespaces-in-your-enterprise.md index f4257ee96689..1d48e36403ff 100644 --- a/content/admin/enforcing-policies/enforcing-policies-for-your-enterprise/enforcing-policies-for-github-codespaces-in-your-enterprise.md +++ b/content/admin/enforcing-policies/enforcing-policies-for-your-enterprise/enforcing-policies-for-github-codespaces-in-your-enterprise.md @@ -29,11 +29,8 @@ If you're an organization owner, you can enable {% data variables.product.prodna ## Enabling or disabling {% data variables.product.prodname_github_codespaces %} in your enterprise -{% note %} - -**Note:** If you remove a user's access to {% data variables.product.prodname_github_codespaces %}, the user will immediately be unable to open existing codespaces they have created from an organization's private {% ifversion ghec %}and internal {% endif %}repositories. For more information, see "[AUTOTITLE](/codespaces/managing-codespaces-for-your-organization/enabling-or-disabling-github-codespaces-for-your-organization#about-changing-your-settings)." - -{% endnote %} +> [!NOTE] +> If you remove a user's access to {% data variables.product.prodname_github_codespaces %}, the user will immediately be unable to open existing codespaces they have created from an organization's private {% ifversion ghec %}and internal {% endif %}repositories. For more information, see "[AUTOTITLE](/codespaces/managing-codespaces-for-your-organization/enabling-or-disabling-github-codespaces-for-your-organization#about-changing-your-settings)." {% data reusables.enterprise-accounts.access-enterprise %} {% data reusables.enterprise-accounts.policies-tab %} diff --git a/content/admin/enforcing-policies/enforcing-policies-for-your-enterprise/enforcing-policies-for-security-settings-in-your-enterprise.md b/content/admin/enforcing-policies/enforcing-policies-for-your-enterprise/enforcing-policies-for-security-settings-in-your-enterprise.md index f0e530bee17c..4786f64a4616 100644 --- a/content/admin/enforcing-policies/enforcing-policies-for-your-enterprise/enforcing-policies-for-security-settings-in-your-enterprise.md +++ b/content/admin/enforcing-policies/enforcing-policies-for-your-enterprise/enforcing-policies-for-security-settings-in-your-enterprise.md @@ -42,23 +42,15 @@ Before you require use of two-factor authentication, we recommend notifying orga {% data reusables.two_fa.ghes_ntp %} -{% warning %} - -**Warnings:** - -* When you require two-factor authentication for your enterprise, members, outside collaborators, and billing managers (including bot accounts) in all organizations owned by your enterprise who do not use 2FA will be removed from the organization and lose access to its repositories. They will also lose access to their forks of the organization's private repositories. You can reinstate their access privileges and settings if they enable two-factor authentication for their account within three months of their removal from your organization. For more information, see "[AUTOTITLE](/organizations/managing-membership-in-your-organization/reinstating-a-former-member-of-your-organization)." -* Any organization owner, member, billing manager, or outside collaborator in any of the organizations owned by your enterprise who disables 2FA for their account after you've enabled required two-factor authentication will automatically be removed from the organization. -* If you're the sole owner of an enterprise that requires two-factor authentication, you won't be able to disable 2FA for your user account without disabling required two-factor authentication for the enterprise. - -{% endwarning %} +> [!WARNING] +> * When you require two-factor authentication for your enterprise, members, outside collaborators, and billing managers (including bot accounts) in all organizations owned by your enterprise who do not use 2FA will be removed from the organization and lose access to its repositories. They will also lose access to their forks of the organization's private repositories. You can reinstate their access privileges and settings if they enable two-factor authentication for their account within three months of their removal from your organization. For more information, see "[AUTOTITLE](/organizations/managing-membership-in-your-organization/reinstating-a-former-member-of-your-organization)." +> * Any organization owner, member, billing manager, or outside collaborator in any of the organizations owned by your enterprise who disables 2FA for their account after you've enabled required two-factor authentication will automatically be removed from the organization. +> * If you're the sole owner of an enterprise that requires two-factor authentication, you won't be able to disable 2FA for your user account without disabling required two-factor authentication for the enterprise. {% ifversion mandatory-2fa-dotcom-contributors %} -{% note %} - -**Note**: Some of the users in your organizations may have been selected for mandatory two-factor authentication enrollment by {% data variables.product.prodname_dotcom_the_website %}, but it has no impact on how you enable the 2FA requirement for the organizations in your enterprise. If you enable the 2FA requirement for organizations in your enterprise, all users without 2FA currently enabled will be removed from the organizations, including those that are required to enable it by {% data variables.product.prodname_dotcom_the_website %}. - -{% endnote %} +> [!NOTE] +> Some of the users in your organizations may have been selected for mandatory two-factor authentication enrollment by {% data variables.product.prodname_dotcom_the_website %}, but it has no impact on how you enable the 2FA requirement for the organizations in your enterprise. If you enable the 2FA requirement for organizations in your enterprise, all users without 2FA currently enabled will be removed from the organizations, including those that are required to enable it by {% data variables.product.prodname_dotcom_the_website %}. {% endif %} @@ -138,11 +130,8 @@ By default, to hide the existence of private resources, when an unauthenticated To prevent confusion from your developers, you can change this behavior so that users are automatically redirected to single sign-on (SSO) through your identity provider (IdP). When you enable automatic redirects, anyone who visits the URL for any of your enterprise's resources will be able to see that the resource exists. However, they'll only be able to see the resource if they have appropriate access after authenticating with your IdP. -{% note %} - -**Note:** If a user is signed in to their personal account when they attempt to access any of your enterprise's resources, they'll be automatically signed out and redirected to SSO to sign in to their {% data variables.enterprise.prodname_managed_user %}. For more information, see "[AUTOTITLE](/enterprise-cloud@latest/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-personal-account/managing-multiple-accounts)." - -{% endnote %} +> [!NOTE] +> If a user is signed in to their personal account when they attempt to access any of your enterprise's resources, they'll be automatically signed out and redirected to SSO to sign in to their {% data variables.enterprise.prodname_managed_user %}. For more information, see "[AUTOTITLE](/enterprise-cloud@latest/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-personal-account/managing-multiple-accounts)." {% data reusables.enterprise-accounts.access-enterprise %} {% data reusables.enterprise-accounts.settings-tab %} diff --git a/content/admin/enforcing-policies/enforcing-policies-for-your-enterprise/enforcing-repository-management-policies-in-your-enterprise.md b/content/admin/enforcing-policies/enforcing-policies-for-your-enterprise/enforcing-repository-management-policies-in-your-enterprise.md index 869afbb872ac..9fbdd3d84966 100644 --- a/content/admin/enforcing-policies/enforcing-policies-for-your-enterprise/enforcing-repository-management-policies-in-your-enterprise.md +++ b/content/admin/enforcing-policies/enforcing-policies-for-your-enterprise/enforcing-repository-management-policies-in-your-enterprise.md @@ -112,11 +112,10 @@ People with admin permissions can set a more granular forking policy. For more i {% endif %} {% ifversion enterprise-namespace-repo-setting %} -{% note %} -**Note:** If {% ifversion ghec %}your enterprise uses {% data variables.product.prodname_emus %} and {% endif %}your "Repository creation" policy prevents enterprise members from creating repositories owned by their user accounts, members will not be allowed to fork a repository in their user accounts, regardless of your "Repository forking" policy. +> [!NOTE] +> If {% ifversion ghec %}your enterprise uses {% data variables.product.prodname_emus %} and {% endif %}your "Repository creation" policy prevents enterprise members from creating repositories owned by their user accounts, members will not be allowed to fork a repository in their user accounts, regardless of your "Repository forking" policy. -{% endnote %} {% endif %} {% data reusables.enterprise-accounts.access-enterprise %} @@ -225,11 +224,8 @@ Across all organizations owned by your enterprise, you can allow members to see ![Screenshot of an issue comment. The header says "ashtom (Thomas Dohmke) commented 1 minute ago," with "(Thomas Dohmke)" outlined in dark orange.](/assets/images/help/issues/commenter-full-name.png) -{% note %} - -**Note:** When this policy is enforced for all repositories in the enterprise, it overrides the organization setting for private repositories. For more information, see "[AUTOTITLE](/organizations/managing-organization-settings/managing-the-display-of-member-names-in-your-organization)". - -{% endnote %} +> [!NOTE] +> When this policy is enforced for all repositories in the enterprise, it overrides the organization setting for private repositories. For more information, see "[AUTOTITLE](/organizations/managing-organization-settings/managing-the-display-of-member-names-in-your-organization)". {% data reusables.enterprise-accounts.access-enterprise %} {% data reusables.enterprise-accounts.policies-tab %} diff --git a/content/admin/enforcing-policies/enforcing-policy-with-pre-receive-hooks/about-pre-receive-hooks.md b/content/admin/enforcing-policies/enforcing-policy-with-pre-receive-hooks/about-pre-receive-hooks.md index da5ed7d2b17d..269a06830904 100644 --- a/content/admin/enforcing-policies/enforcing-policy-with-pre-receive-hooks/about-pre-receive-hooks.md +++ b/content/admin/enforcing-policies/enforcing-policy-with-pre-receive-hooks/about-pre-receive-hooks.md @@ -41,8 +41,5 @@ Due to risk of failure and performance impact for all users of your instance, we * Avoid API requests within a pre-receive hook. In particular, we strongly discourage that you make requests to external services, which may take longer and can compound performance impact. * Avoid long-running Git operations within a pre-receive hook. If your pre-receive hook performs Git operations within large or busy repositories, your instance's Git and overall performance may be negatively impacted. -{% note %} - -**Note:** To avoid rejection of a push due to a timeout, all combined pre-receive hooks should run in under five seconds. - -{% endnote %} +> [!NOTE] +> To avoid rejection of a push due to a timeout, all combined pre-receive hooks should run in under five seconds. diff --git a/content/admin/enforcing-policies/enforcing-policy-with-pre-receive-hooks/creating-a-pre-receive-hook-environment.md b/content/admin/enforcing-policies/enforcing-policy-with-pre-receive-hooks/creating-a-pre-receive-hook-environment.md index c01153e6bd86..a0fc50b61c98 100644 --- a/content/admin/enforcing-policies/enforcing-policy-with-pre-receive-hooks/creating-a-pre-receive-hook-environment.md +++ b/content/admin/enforcing-policies/enforcing-policy-with-pre-receive-hooks/creating-a-pre-receive-hook-environment.md @@ -73,14 +73,10 @@ You can use a Linux container management tool to build a pre-receive hook enviro tar -czf /path/to/pre-receive-environment.tar.gz . ``` - {% note %} - - **Notes:** - * Do not include leading directory paths of files within the tar archive, such as `/path/to/chroot`. - * `/bin/sh` must exist and be executable, as the entry point into the chroot environment. - * Unlike traditional chroots, the `dev` directory is not required by the chroot environment for pre-receive hooks. - - {% endnote %} + > [!NOTE] + > * Do not include leading directory paths of files within the tar archive, such as `/path/to/chroot`. + > * `/bin/sh` must exist and be executable, as the entry point into the chroot environment. + > * Unlike traditional chroots, the `dev` directory is not required by the chroot environment for pre-receive hooks. For more information about creating a chroot environment see "[Chroot](https://wiki.debian.org/chroot)" from the _Debian Wiki_, "[BasicChroot](https://help.ubuntu.com/community/BasicChroot)" from the _Ubuntu Community Help Wiki_, or "[Installing Alpine Linux in a chroot](https://wiki.alpinelinux.org/wiki/Installing_Alpine_Linux_in_a_chroot)" from the _Alpine Linux Wiki_. diff --git a/content/admin/installing-your-enterprise-server/setting-up-a-github-enterprise-server-instance/installing-github-enterprise-server-on-aws.md b/content/admin/installing-your-enterprise-server/setting-up-a-github-enterprise-server-instance/installing-github-enterprise-server-on-aws.md index 14dfaf67f348..309cd7dddfb9 100644 --- a/content/admin/installing-your-enterprise-server/setting-up-a-github-enterprise-server-instance/installing-github-enterprise-server-on-aws.md +++ b/content/admin/installing-your-enterprise-server/setting-up-a-github-enterprise-server-instance/installing-github-enterprise-server-on-aws.md @@ -99,18 +99,11 @@ If you're setting up your AMI for the first time, you will need to create a secu To create the instance, you'll need to launch an EC2 instance with your {% data variables.product.prodname_ghe_server %} AMI and attach an additional storage volume for your instance data. For more information, see "[Hardware considerations](#hardware-considerations)." -{% note %} +> [!NOTE] +> You can encrypt the data disk to gain an extra level of security and ensure that any data you write to your instance is protected. There is a slight performance impact when using encrypted disks. If you decide to encrypt your volume, we strongly recommend doing so **before** starting your instance for the first time. For more information, see the [Amazon guide on EBS encryption](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSEncryption.html). -**Note:** You can encrypt the data disk to gain an extra level of security and ensure that any data you write to your instance is protected. There is a slight performance impact when using encrypted disks. If you decide to encrypt your volume, we strongly recommend doing so **before** starting your instance for the first time. - For more information, see the [Amazon guide on EBS encryption](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSEncryption.html). - -{% endnote %} - -{% warning %} - -**Warning:** If you decide to enable encryption after you've configured your instance, you will need to migrate your data to the encrypted volume, which will incur some downtime for your users. - -{% endwarning %} +> [!WARNING] +> If you decide to enable encryption after you've configured your instance, you will need to migrate your data to the encrypted volume, which will incur some downtime for your users. ### Launching an EC2 instance diff --git a/content/admin/installing-your-enterprise-server/setting-up-a-github-enterprise-server-instance/installing-github-enterprise-server-on-azure.md b/content/admin/installing-your-enterprise-server/setting-up-a-github-enterprise-server-instance/installing-github-enterprise-server-on-azure.md index 15a4241a6a44..ba70a5aa59d9 100644 --- a/content/admin/installing-your-enterprise-server/setting-up-a-github-enterprise-server-instance/installing-github-enterprise-server-on-azure.md +++ b/content/admin/installing-your-enterprise-server/setting-up-a-github-enterprise-server-instance/installing-github-enterprise-server-on-azure.md @@ -72,11 +72,8 @@ Before launching {% data variables.location.product_location %} on Azure, you'll az vm disk attach --vm-name VM_NAME -g RESOURCE_GROUP --sku Premium_LRS --new -z SIZE_IN_GB --name ghe-data.vhd --caching ReadWrite ``` - {% note %} - - **Note:** For non-production instances to have sufficient I/O throughput, the recommended minimum disk size is 150 GiB with read/write cache enabled (`--caching ReadWrite`). - - {% endnote %} + > [!NOTE] + > For non-production instances to have sufficient I/O throughput, the recommended minimum disk size is 150 GiB with read/write cache enabled (`--caching ReadWrite`). ## Configuring the {% data variables.product.prodname_ghe_server %} virtual machine @@ -94,11 +91,8 @@ To configure the instance, you must confirm the instance's status, upload a lice ``` - {% note %} - - **Note:** Azure does not automatically create a FQDNS entry for the VM. For more information, see the Azure guide [Create a fully qualified domain name in the Azure portal for a Linux VM](https://docs.microsoft.com/azure/virtual-machines/linux/portal-create-fqdn). - - {% endnote %} + > [!NOTE] + > Azure does not automatically create a FQDNS entry for the VM. For more information, see the Azure guide [Create a fully qualified domain name in the Azure portal for a Linux VM](https://docs.microsoft.com/azure/virtual-machines/linux/portal-create-fqdn). {% data reusables.enterprise_installation.copy-the-vm-public-dns-name %} {% data reusables.enterprise_installation.upload-a-license-file %} diff --git a/content/admin/installing-your-enterprise-server/setting-up-a-github-enterprise-server-instance/setting-up-a-staging-instance.md b/content/admin/installing-your-enterprise-server/setting-up-a-github-enterprise-server-instance/setting-up-a-staging-instance.md index 2e0106691fa2..ce559281e08a 100644 --- a/content/admin/installing-your-enterprise-server/setting-up-a-github-enterprise-server-instance/setting-up-a-staging-instance.md +++ b/content/admin/installing-your-enterprise-server/setting-up-a-github-enterprise-server-instance/setting-up-a-staging-instance.md @@ -21,11 +21,8 @@ shortTitle: Set up a staging instance For example, to protect against loss of data, you can regularly validate the backup of your production instance. You can regularly restore the backup of your production data to a separate {% data variables.product.product_name %} instance in a staging environment. On this staging instance, you could also test the upgrade to the latest feature release of {% data variables.product.product_name %}. -{% tip %} - -**Tip:** You may reuse your existing {% data variables.product.prodname_enterprise %} license file as long as the staging instance is not used in a production capacity. - -{% endtip %} +> [!TIP] +> You may reuse your existing {% data variables.product.prodname_enterprise %} license file as long as the staging instance is not used in a production capacity. ## Considerations for a staging environment @@ -55,11 +52,8 @@ Alternatively, you can create a staging instance that reflects your production c If you want to test changes on an instance that contains the same data and configuration as your production instance, back up the data and configuration from the production instance using {% data variables.product.prodname_enterprise_backup_utilities %}. For more information, see "[AUTOTITLE](/admin/configuration/configuring-your-enterprise/configuring-backups-on-your-appliance)." -{% warning %} - -**Warning**: If you use {% data variables.product.prodname_actions %} or {% data variables.product.prodname_registry %} in production, your backup will include your production configuration for external storage. To avoid potential loss of data by writing to your production storage from your staging instance, you must configure each feature in steps 3 and 4 before you restore your backup. - -{% endwarning %} +> [!WARNING] +> If you use {% data variables.product.prodname_actions %} or {% data variables.product.prodname_registry %} in production, your backup will include your production configuration for external storage. To avoid potential loss of data by writing to your production storage from your staging instance, you must configure each feature in steps 3 and 4 before you restore your backup. ### 2. Set up a staging instance @@ -71,11 +65,8 @@ If you plan to restore a backup of your production instance, continue to the nex Optionally, if you use {% data variables.product.prodname_actions %} on your production instance, configure the feature on the staging instance before restoring your production backup. If you don't use {% data variables.product.prodname_actions %}, skip to "[1. Configure {% data variables.product.prodname_registry %}](#4-configure-github-packages)." -{% warning %} - -**Warning**: If you don't configure {% data variables.product.prodname_actions %} on the staging instance before restoring your production backup, your staging instance will use your production instance's external storage, which could result in loss of data. We strongly recommended that you use different external storage for your staging instance. For more information, see "[AUTOTITLE](/admin/github-actions/advanced-configuration-and-troubleshooting/using-a-staging-environment)." - -{% endwarning %} +> [!WARNING] +> If you don't configure {% data variables.product.prodname_actions %} on the staging instance before restoring your production backup, your staging instance will use your production instance's external storage, which could result in loss of data. We strongly recommended that you use different external storage for your staging instance. For more information, see "[AUTOTITLE](/admin/github-actions/advanced-configuration-and-troubleshooting/using-a-staging-environment)." {% data reusables.enterprise_installation.ssh-into-staging-instance %} 1. To configure the staging instance to use an external storage provider for {% data variables.product.prodname_actions %}, enter one of the following commands. @@ -91,11 +82,8 @@ Optionally, if you use {% data variables.product.prodname_actions %} on your pro Optionally, if you use {% data variables.product.prodname_registry %} on your production instance, configure the feature on the staging instance before restoring your production backup. If you don't use {% data variables.product.prodname_registry %}, skip to "[1. Restore your production backup](#5-restore-your-production-backup)." -{% warning %} - -**Warning**: If you don't configure {% data variables.product.prodname_registry %} on the staging instance before restoring your production backup, your staging instance will use your production instance's external storage, which could result in loss of data. We strongly recommended that you use different external storage for your staging instance. - -{% endwarning %} +> [!WARNING] +> If you don't configure {% data variables.product.prodname_registry %} on the staging instance before restoring your production backup, your staging instance will use your production instance's external storage, which could result in loss of data. We strongly recommended that you use different external storage for your staging instance. 1. Review the backup you will restore to the staging instance. * If you took the backup with {% data variables.product.prodname_enterprise_backup_utilities %} 3.5 or later, the backup includes the configuration for {% data variables.product.prodname_registry %}. Continue to the next step. @@ -136,19 +124,13 @@ If the staging instance is already configured and you want to overwrite settings To access the staging instance using the same hostname, update your local hosts file to resolve the staging instance's hostname by IP address by editing the `/etc/hosts` file in macOS or Linux, or the `C:\Windows\system32\drivers\etc` file in Windows. -{% note %} - -**Note**: Your staging instance must be accessible from the same hostname as your production instance. Changing the hostname for {% data variables.location.product_location %} is not supported. For more information, see "[AUTOTITLE](/admin/configuration/configuring-network-settings/configuring-a-hostname)." - -{% endnote %} +> [!NOTE] +> Your staging instance must be accessible from the same hostname as your production instance. Changing the hostname for {% data variables.location.product_location %} is not supported. For more information, see "[AUTOTITLE](/admin/configuration/configuring-network-settings/configuring-a-hostname)." Then, review the staging instance's configuration in the {% data variables.enterprise.management_console %}. For more information, see "[AUTOTITLE](/admin/configuration/administering-your-instance-from-the-management-console)." -{% warning %} - -**Warning**: If you configured {% data variables.product.prodname_actions %} or {% data variables.product.prodname_registry %} for the staging instance, to avoid overwriting production data, ensure that the external storage configuration in the {% data variables.enterprise.management_console %} does not match your production instance. - -{% endwarning %} +> [!WARNING] +> If you configured {% data variables.product.prodname_actions %} or {% data variables.product.prodname_registry %} for the staging instance, to avoid overwriting production data, ensure that the external storage configuration in the {% data variables.enterprise.management_console %} does not match your production instance. ### 7. Apply the instance's configuration diff --git a/content/admin/managing-accounts-and-repositories/communicating-information-to-users-in-your-enterprise/customizing-user-messages-for-your-enterprise.md b/content/admin/managing-accounts-and-repositories/communicating-information-to-users-in-your-enterprise/customizing-user-messages-for-your-enterprise.md index e17f963ab6ef..c4bced72e7cd 100644 --- a/content/admin/managing-accounts-and-repositories/communicating-information-to-users-in-your-enterprise/customizing-user-messages-for-your-enterprise.md +++ b/content/admin/managing-accounts-and-repositories/communicating-information-to-users-in-your-enterprise/customizing-user-messages-for-your-enterprise.md @@ -32,11 +32,9 @@ There are several types of user messages. {% endif %} {% ifversion ghes %} -{% note %} -**Note:** If you are using SAML for authentication, the sign in page is presented by your identity provider and is not customizable via {% data variables.product.prodname_ghe_server %}. - -{% endnote %} +> [!NOTE] +> If you are using SAML for authentication, the sign in page is presented by your identity provider and is not customizable via {% data variables.product.prodname_ghe_server %}. {% data reusables.enterprise.user-messages-markdown %} @@ -87,11 +85,10 @@ If you include Markdown checkboxes in the message, all checkboxes must be select Each time a user sees a mandatory message, an audit log event is created. The event includes the version of the message that the user saw. For more information see "[AUTOTITLE](/admin/monitoring-activity-in-your-enterprise/reviewing-audit-logs-for-your-enterprise/audit-log-events-for-your-enterprise)." {% ifversion display-mandatory-message-again %} {% else %} -{% note %} -**Note:** If you change the mandatory message for {% data variables.location.product_location %}, users who have already acknowledged the message will not see the new message. +> [!NOTE] +> If you change the mandatory message for {% data variables.location.product_location %}, users who have already acknowledged the message will not see the new message. -{% endnote %} {% endif %} {% data reusables.enterprise-accounts.access-enterprise %} @@ -124,11 +121,11 @@ You can also set an announcement banner{% ifversion ghes %} in the administrativ 1. Under "Announcement", in the text field, type the announcement you want displayed in a banner. 1. Optionally, under "Expires on", select the calendar drop-down menu and click an expiration date. {% ifversion ghe-announce-dismiss %} - {% note %} - **Note:** Announcements must either have an expiration date, be user dismissible, or both. + > [!NOTE] + > Announcements must either have an expiration date, be user dismissible, or both. - {% endnote %}{% endif %} + {% endif %} {%- ifversion ghe-announce-dismiss %} 1. Optionally, to allow each user to dismiss the announcement, select **User dismissible**. {%- endif %}{% ifversion custom-banner-messages %} diff --git a/content/admin/managing-accounts-and-repositories/managing-organizations-in-your-enterprise/managing-projects-using-jira.md b/content/admin/managing-accounts-and-repositories/managing-organizations-in-your-enterprise/managing-projects-using-jira.md index 37dd9b8b69b3..e2807b2243eb 100644 --- a/content/admin/managing-accounts-and-repositories/managing-organizations-in-your-enterprise/managing-projects-using-jira.md +++ b/content/admin/managing-accounts-and-repositories/managing-organizations-in-your-enterprise/managing-projects-using-jira.md @@ -25,11 +25,9 @@ shortTitle: Project management with Jira {% data reusables.user-settings.oauth_apps %} 1. Click **New {% data variables.product.prodname_oauth_app %}**. - {% note %} + > [!NOTE] + > If you haven't created an app before, this button will say, **Register an application**. - **Note:** If you haven't created an app before, this button will say, **Register an application**. - - {% endnote %} 1. Fill in the application settings: * In the **Application name** field, type "Jira" or any name you would like to use to identify the Jira instance. * In the **Homepage URL** field, type the full URL of your Jira instance. diff --git a/content/admin/managing-accounts-and-repositories/managing-organizations-in-your-enterprise/managing-your-role-in-an-organization-owned-by-your-enterprise.md b/content/admin/managing-accounts-and-repositories/managing-organizations-in-your-enterprise/managing-your-role-in-an-organization-owned-by-your-enterprise.md index a740f61515bd..5930ce491362 100644 --- a/content/admin/managing-accounts-and-repositories/managing-organizations-in-your-enterprise/managing-your-role-in-an-organization-owned-by-your-enterprise.md +++ b/content/admin/managing-accounts-and-repositories/managing-organizations-in-your-enterprise/managing-your-role-in-an-organization-owned-by-your-enterprise.md @@ -19,11 +19,10 @@ shortTitle: Manage your organization roles You can choose to join an organization owned by your enterprise as a member or as an organization owner, change your role within the organization, or leave the organization. {% ifversion ghec %} -{% warning %} -**Warning**: If an organization uses SCIM to provision users, joining the organization this way could have unintended consequences. For more information, see "[AUTOTITLE](/organizations/managing-saml-single-sign-on-for-your-organization/about-scim-for-organizations)." +> [!WARNING] +> If an organization uses SCIM to provision users, joining the organization this way could have unintended consequences. For more information, see "[AUTOTITLE](/organizations/managing-saml-single-sign-on-for-your-organization/about-scim-for-organizations)." -{% endwarning %} {% endif %} For information about managing other people's roles in an organization, see "[AUTOTITLE](/organizations/managing-membership-in-your-organization)" and "[AUTOTITLE](/organizations/managing-peoples-access-to-your-organization-with-roles)." diff --git a/content/admin/managing-accounts-and-repositories/managing-organizations-in-your-enterprise/removing-organizations-from-your-enterprise.md b/content/admin/managing-accounts-and-repositories/managing-organizations-in-your-enterprise/removing-organizations-from-your-enterprise.md index 13672d088d32..1bb97146d560 100644 --- a/content/admin/managing-accounts-and-repositories/managing-organizations-in-your-enterprise/removing-organizations-from-your-enterprise.md +++ b/content/admin/managing-accounts-and-repositories/managing-organizations-in-your-enterprise/removing-organizations-from-your-enterprise.md @@ -16,15 +16,12 @@ redirect_from: You can remove an organization that is owned by your enterprise account, so the organization stands alone. -{% warning %} - -**Warning**: When you remove an organization from your enterprise: -* Billing, identity management, 2FA requirements, and other policies for the organization will no longer be governed by your enterprise. -* The organization will be downgraded to the free plan. -* The organization will be governed by our standard Terms of Service. -* Any internal repositories within the organization will be converted to private repositories. - -{% endwarning %} +> [!WARNING] +> When you remove an organization from your enterprise: +> * Billing, identity management, 2FA requirements, and other policies for the organization will no longer be governed by your enterprise. +> * The organization will be downgraded to the free plan. +> * The organization will be governed by our standard Terms of Service. +> * Any internal repositories within the organization will be converted to private repositories. ## Removing an organization from your enterprise diff --git a/content/admin/managing-accounts-and-repositories/managing-organizations-in-your-enterprise/requiring-two-factor-authentication-for-an-organization.md b/content/admin/managing-accounts-and-repositories/managing-organizations-in-your-enterprise/requiring-two-factor-authentication-for-an-organization.md index c2731968b20b..d8f817a8b1e2 100644 --- a/content/admin/managing-accounts-and-repositories/managing-organizations-in-your-enterprise/requiring-two-factor-authentication-for-an-organization.md +++ b/content/admin/managing-accounts-and-repositories/managing-organizations-in-your-enterprise/requiring-two-factor-authentication-for-an-organization.md @@ -30,15 +30,10 @@ Before you require use of two-factor authentication, we recommend notifying orga {% data reusables.two_fa.ghes_ntp %} -{% warning %} - -**Warnings:** - -* When your require two-factor authentication, members and outside collaborators (including bot accounts) who do not use 2FA will be removed from the organization and lose access to its repositories, including their forks of private repositories. If they enable 2FA for their personal account within three months of being removed from the organization, you can [reinstate their access privileges and settings](/organizations/managing-membership-in-your-organization/reinstating-a-former-member-of-your-organization). -* When 2FA is required, organization members or outside collaborators who disable 2FA will automatically be removed from the organization. -* If you're the sole owner of an organization that requires two-factor authentication, you won't be able to disable 2FA for your personal account without disabling required two-factor authentication for the organization. - -{% endwarning %} +> [!WARNING] +> * When your require two-factor authentication, members and outside collaborators (including bot accounts) who do not use 2FA will be removed from the organization and lose access to its repositories, including their forks of private repositories. If they enable 2FA for their personal account within three months of being removed from the organization, you can [reinstate their access privileges and settings](/organizations/managing-membership-in-your-organization/reinstating-a-former-member-of-your-organization). +> * When 2FA is required, organization members or outside collaborators who disable 2FA will automatically be removed from the organization. +> * If you're the sole owner of an organization that requires two-factor authentication, you won't be able to disable 2FA for your personal account without disabling required two-factor authentication for the organization. ## Requiring two-factor authentication for an organization diff --git a/content/admin/managing-accounts-and-repositories/managing-repositories-in-your-enterprise/accessing-user-owned-repositories-in-your-enterprise.md b/content/admin/managing-accounts-and-repositories/managing-repositories-in-your-enterprise/accessing-user-owned-repositories-in-your-enterprise.md index 5d73fa08392d..2907f5ffb118 100644 --- a/content/admin/managing-accounts-and-repositories/managing-repositories-in-your-enterprise/accessing-user-owned-repositories-in-your-enterprise.md +++ b/content/admin/managing-accounts-and-repositories/managing-repositories-in-your-enterprise/accessing-user-owned-repositories-in-your-enterprise.md @@ -13,11 +13,8 @@ redirect_from: - /admin/user-management/managing-repositories-in-your-enterprise/accessing-user-owned-repositories-in-your-enterprise --- -{% note %} - -**Note:** Temporarily accessing user-owned repositories is currently in {% data variables.release-phases.public_preview %} for {% data variables.product.prodname_emus %} and subject to change. - -{% endnote %} +> [!NOTE] +> Temporarily accessing user-owned repositories is currently in {% data variables.release-phases.public_preview %} for {% data variables.product.prodname_emus %} and subject to change. ## About temporary access to user-owned repositories diff --git a/content/admin/managing-accounts-and-repositories/managing-repositories-in-your-enterprise/viewing-user-owned-repositories-in-your-enterprise.md b/content/admin/managing-accounts-and-repositories/managing-repositories-in-your-enterprise/viewing-user-owned-repositories-in-your-enterprise.md index 89ceaabfa8ec..0b13ea7f2c2c 100644 --- a/content/admin/managing-accounts-and-repositories/managing-repositories-in-your-enterprise/viewing-user-owned-repositories-in-your-enterprise.md +++ b/content/admin/managing-accounts-and-repositories/managing-repositories-in-your-enterprise/viewing-user-owned-repositories-in-your-enterprise.md @@ -13,11 +13,8 @@ redirect_from: - /admin/user-management/managing-repositories-in-your-enterprise/viewing-user-owned-repositories-in-your-enterprise --- -{% note %} - -**Note:** Viewing user-owned repositories is currently in {% data variables.release-phases.public_preview %} for {% data variables.product.prodname_emus %} and subject to change. - -{% endnote %} +> [!NOTE] +> Viewing user-owned repositories is currently in {% data variables.release-phases.public_preview %} for {% data variables.product.prodname_emus %} and subject to change. If your enterprise uses {% data variables.product.prodname_emus %}, and you've allowed users to create repositories owned by their user accounts, you can view all user-owned repositories within your enterprise. diff --git a/content/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/auditing-users-across-your-enterprise.md b/content/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/auditing-users-across-your-enterprise.md index 6a13f0b0525e..0fb9eeec93f7 100644 --- a/content/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/auditing-users-across-your-enterprise.md +++ b/content/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/auditing-users-across-your-enterprise.md @@ -39,14 +39,9 @@ The audit log lists the following information about actions made within your ent * [Which country](#search-based-on-the-location) the action took place in * [The date and time](#search-based-on-the-time-of-action) the action occurred -{% warning %} - -**Notes:** - -* While you can't use text to search for audit entries, you can construct search queries using a variety of filters. {% data variables.product.product_name %} supports many operators for searching across {% data variables.product.product_name %}. For more information, see "[AUTOTITLE](/search-github/getting-started-with-searching-on-github/about-searching-on-github)." -* Audit records are available for the last 180 days. - -{% endwarning %} +> [!NOTE] +> * While you can't use text to search for audit entries, you can construct search queries using a variety of filters. {% data variables.product.product_name %} supports many operators for searching across {% data variables.product.product_name %}. For more information, see "[AUTOTITLE](/search-github/getting-started-with-searching-on-github/about-searching-on-github)." +> * Audit records are available for the last 180 days. ### Search based on the repository diff --git a/content/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/enabling-guest-collaborators.md b/content/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/enabling-guest-collaborators.md index 054039dfada5..bfc07a1f339c 100644 --- a/content/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/enabling-guest-collaborators.md +++ b/content/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/enabling-guest-collaborators.md @@ -59,11 +59,9 @@ If you use **Microsoft Entra ID** (previously known as Azure AD) or **Okta** for }, ``` - {% note %} + > [!NOTE] + > The `id` value is critical. If another `id` value is present, the update will fail. - **Note:** The `id` value is critical. If another `id` value is present, the update will fail. - - {% endnote %} 1. Click **Save**. ### Enabling guest collaborators with Okta diff --git a/content/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/exporting-membership-information-for-your-enterprise.md b/content/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/exporting-membership-information-for-your-enterprise.md index 8dc87836cb23..01cab3d2f292 100644 --- a/content/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/exporting-membership-information-for-your-enterprise.md +++ b/content/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/exporting-membership-information-for-your-enterprise.md @@ -17,11 +17,8 @@ You can export aggregated information about your enterprise's members as a membe The membership information report includes the following information. -{% note %} - -**Note:** You can only export the datetime of the user's last activity at the organization level. For more information, see "[AUTOTITLE](/organizations/managing-membership-in-your-organization/exporting-member-information-for-your-organization#about-export-of-membership-information)." - -{% endnote %} +> [!NOTE] +> You can only export the datetime of the user's last activity at the organization level. For more information, see "[AUTOTITLE](/organizations/managing-membership-in-your-organization/exporting-member-information-for-your-organization#about-export-of-membership-information)." * Username and display name details * Whether the user has two-factor authentication enabled {% ifversion mandatory-2fa-required-overview %}or is required to enable it{% endif %} diff --git a/content/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/managing-organization-members-in-your-enterprise.md b/content/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/managing-organization-members-in-your-enterprise.md index 8d3a3466777f..331d2b0f6e04 100644 --- a/content/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/managing-organization-members-in-your-enterprise.md +++ b/content/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/managing-organization-members-in-your-enterprise.md @@ -22,23 +22,16 @@ Members can also be added or removed from an organization at the organization le 1. Select the checkbox next to each user you want to add or remove. 1. At the top of the member list, select the **X user(s) selected** dropdown menu, then click **Add to organizations** or **Remove from organizations**. - {% note %} - - **Note:** - * Users will be added as organization members. If the user is already an organization member or organization owner, the privileges will not be modified. - * Organization owners cannot be removed from the organization via the bulk method. - - {% endnote %} + > [!NOTE] + > * Users will be added as organization members. If the user is already an organization member or organization owner, the privileges will not be modified. + > * Organization owners cannot be removed from the organization via the bulk method. ![Screenshot of the list of enterprise members. A dropdown menu, labeled "1 user selected...", is expanded and highlighted with an orange outline.](/assets/images/help/business-accounts/enterprise-add-or-remove-from-org.png) 1. In the popup, select the organizations you want to add or remove the user from. - {% note %} - - **Note:** You can only select organizations where you're an organization owner. - - {% endnote %} + > [!NOTE] + > You can only select organizations where you're an organization owner. 1. To confirm, click **Add user** or **Remove user**. 1. Optionally, to add or remove multiple users at the same time, select multiple checkboxes. Use the dropdown to select **Add to organizations** or **Remove from organizations**. diff --git a/content/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/managing-support-entitlements-for-your-enterprise.md b/content/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/managing-support-entitlements-for-your-enterprise.md index 187c6d8b0a7b..d97e8dd005d7 100644 --- a/content/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/managing-support-entitlements-for-your-enterprise.md +++ b/content/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/managing-support-entitlements-for-your-enterprise.md @@ -24,11 +24,8 @@ Enterprise owners and billing managers automatically have a support entitlement. To add a support entitlement to a user, the user must already be a member of an organization that is owned by your enterprise. -{% note %} - -**Note**: After you add a support entitlement, the enterprise member may need to sign out from {% data variables.contact.contact_landing_page_portal %}, then sign in again, before they can manage tickets. - -{% endnote %} +> [!NOTE] +> After you add a support entitlement, the enterprise member may need to sign out from {% data variables.contact.contact_landing_page_portal %}, then sign in again, before they can manage tickets. {% data reusables.enterprise-accounts.access-enterprise %} {% data reusables.enterprise-accounts.settings-tab %} diff --git a/content/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/removing-a-member-from-your-enterprise.md b/content/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/removing-a-member-from-your-enterprise.md index 78e470772b8d..0720e4dce789 100644 --- a/content/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/removing-a-member-from-your-enterprise.md +++ b/content/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/removing-a-member-from-your-enterprise.md @@ -24,11 +24,8 @@ If your enterprise does use {% data variables.product.prodname_emus %}, you must ## Removing a member from your enterprise -{% note %} - -**Note:** If an enterprise member uses only {% data variables.product.prodname_ghe_server %}, and not {% data variables.product.prodname_ghe_cloud %}, you cannot remove the enterprise member this way. - -{% endnote %} +> [!NOTE] +> If an enterprise member uses only {% data variables.product.prodname_ghe_server %}, and not {% data variables.product.prodname_ghe_cloud %}, you cannot remove the enterprise member this way. {% data reusables.enterprise-accounts.access-enterprise %} {% data reusables.enterprise-accounts.people-tab %} diff --git a/content/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/viewing-people-in-your-enterprise.md b/content/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/viewing-people-in-your-enterprise.md index 58b2f02255dc..a9965e779b15 100644 --- a/content/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/viewing-people-in-your-enterprise.md +++ b/content/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/viewing-people-in-your-enterprise.md @@ -153,11 +153,8 @@ You can see all the pending invitations to become members, administrators, or ou In the list of pending members, for any individual account, you can cancel all invitations to join organizations owned by your enterprise. This does not cancel any invitations for that same person to become an enterprise administrator or outside collaborator. -{% note %} - -**Note:** If an invitation was provisioned via SCIM, you must cancel the invitation via your identity provider (IdP) instead of on {% data variables.product.prodname_dotcom %}. - -{% endnote %} +> [!NOTE] +> If an invitation was provisioned via SCIM, you must cancel the invitation via your identity provider (IdP) instead of on {% data variables.product.prodname_dotcom %}. If you use {% data variables.visual_studio.prodname_vss_ghe %}, the list of pending invitations includes all {% data variables.product.prodname_vs %} subscribers that haven't joined any of your organizations on {% data variables.product.prodname_dotcom %}, even if the subscriber does not have a pending invitation to join an organization. For more information about how to get {% data variables.product.prodname_vs %} subscribers access to {% data variables.product.prodname_enterprise %}, see "[AUTOTITLE](/billing/managing-billing-for-your-products/managing-licenses-for-visual-studio-subscriptions-with-github-enterprise/setting-up-visual-studio-subscriptions-with-github-enterprise)." diff --git a/content/admin/managing-code-security/managing-github-advanced-security-for-your-enterprise/configuring-code-scanning-for-your-appliance.md b/content/admin/managing-code-security/managing-github-advanced-security-for-your-enterprise/configuring-code-scanning-for-your-appliance.md index 659c29ee8f99..ba6461a8b149 100644 --- a/content/admin/managing-code-security/managing-github-advanced-security-for-your-enterprise/configuring-code-scanning-for-your-appliance.md +++ b/content/admin/managing-code-security/managing-github-advanced-security-for-your-enterprise/configuring-code-scanning-for-your-appliance.md @@ -56,13 +56,9 @@ You can configure {% data variables.product.prodname_code_scanning %} to run {% ## Provisioning a self-hosted runner -{% note %} - -**Notes:** - * If your enterprise uses {% data variables.product.prodname_dotcom %}-hosted runners with {% data variables.product.prodname_actions %}, proceed directly to configuring {% data variables.product.prodname_code_scanning %} through {% data variables.product.prodname_dotcom_the_website %}. See "[AUTOTITLE](/code-security/code-scanning/enabling-code-scanning/configuring-default-setup-for-code-scanning)" and "[AUTOTITLE](/code-security/code-scanning/enabling-code-scanning/configuring-default-setup-for-code-scanning-at-scale)." - * With the exception of Swift analysis, default setup can now run on {% data variables.actions.hosted_runners %}. See "[AUTOTITLE](/actions/using-github-hosted-runners/about-larger-runners/about-larger-runners)" and "[AUTOTITLE](/code-security/code-scanning/managing-your-code-scanning-configuration/configuring-larger-runners-for-default-setup)." - -{% endnote %} +> [!NOTE] +> * If your enterprise uses {% data variables.product.prodname_dotcom %}-hosted runners with {% data variables.product.prodname_actions %}, proceed directly to configuring {% data variables.product.prodname_code_scanning %} through {% data variables.product.prodname_dotcom_the_website %}. See "[AUTOTITLE](/code-security/code-scanning/enabling-code-scanning/configuring-default-setup-for-code-scanning)" and "[AUTOTITLE](/code-security/code-scanning/enabling-code-scanning/configuring-default-setup-for-code-scanning-at-scale)." +> * With the exception of Swift analysis, default setup can now run on {% data variables.actions.hosted_runners %}. See "[AUTOTITLE](/actions/using-github-hosted-runners/about-larger-runners/about-larger-runners)" and "[AUTOTITLE](/code-security/code-scanning/managing-your-code-scanning-configuration/configuring-larger-runners-for-default-setup)." {% endif %} @@ -78,11 +74,10 @@ If you are using default setup for {% data variables.product.prodname_code_scann You must ensure that Git is in the PATH variable on any self-hosted runners you use to run {% data variables.product.prodname_codeql %} actions. {% ifversion default-setup-self-hosted-runners-GHEC or ghes %} -{% note %} -**Note:** If you use {% data variables.product.prodname_codeql %} {% data variables.product.prodname_code_scanning %} to analyze code written in Python in your enterprise, you must make sure that your self-hosted runner has Python 3 installed. +> [!NOTE] +> If you use {% data variables.product.prodname_codeql %} {% data variables.product.prodname_code_scanning %} to analyze code written in Python in your enterprise, you must make sure that your self-hosted runner has Python 3 installed. -{% endnote %} {% endif %} {% ifversion ghes %} diff --git a/content/admin/managing-code-security/managing-github-advanced-security-for-your-enterprise/managing-github-advanced-security-features-for-your-enterprise.md b/content/admin/managing-code-security/managing-github-advanced-security-for-your-enterprise/managing-github-advanced-security-features-for-your-enterprise.md index a03401f84fb1..b61c8587468d 100644 --- a/content/admin/managing-code-security/managing-github-advanced-security-for-your-enterprise/managing-github-advanced-security-features-for-your-enterprise.md +++ b/content/admin/managing-code-security/managing-github-advanced-security-for-your-enterprise/managing-github-advanced-security-features-for-your-enterprise.md @@ -47,10 +47,7 @@ When you enable one or more security and analysis features for existing reposito {% ifversion secret-scanning-custom-link-on-block %} 1. Optionally, to include a resource link in the message that members will see when they attempt to push a secret, select **Add a resource link in the CLI and web UI when a commit is blocked**, then type a URL, and click **Save link**. - {% note %} - - **Note**: When a custom link is configured for an organization, the organization-level value overrides the custom link set for the enterprise. See "[AUTOTITLE](/code-security/secret-scanning/protecting-pushes-with-secret-scanning)." - - {% endnote %} + > [!NOTE] + > When a custom link is configured for an organization, the organization-level value overrides the custom link set for the enterprise. See "[AUTOTITLE](/code-security/secret-scanning/protecting-pushes-with-secret-scanning)." ![Screenshot of the "Push protection" section of the settings for security and analysis features. The checkbox and the text field used for enabling a custom link are outlined in dark orange.](/assets/images/help/organizations/secret-scanning-custom-link.png){% endif %} diff --git a/content/admin/managing-code-security/managing-supply-chain-security-for-your-enterprise/configuring-dependabot-to-work-with-limited-internet-access.md b/content/admin/managing-code-security/managing-supply-chain-security-for-your-enterprise/configuring-dependabot-to-work-with-limited-internet-access.md index 9d31af05350d..6fce3c645fcf 100644 --- a/content/admin/managing-code-security/managing-supply-chain-security-for-your-enterprise/configuring-dependabot-to-work-with-limited-internet-access.md +++ b/content/admin/managing-code-security/managing-supply-chain-security-for-your-enterprise/configuring-dependabot-to-work-with-limited-internet-access.md @@ -45,11 +45,8 @@ Before configuring {% data variables.product.prodname_dependabot %}, install Doc {%- endif %} - {% note %} - - **Note:** You will need to repeat this step when you upgrade to a new minor version of {% data variables.product.product_name %}, or if you manually update the {% data variables.product.prodname_dependabot %} action from {% data variables.product.prodname_dotcom_the_website %}. For more information, see "[AUTOTITLE](/admin/github-actions/managing-access-to-actions-from-githubcom/manually-syncing-actions-from-githubcom)." - - {% endnote %} + > [!NOTE] + > You will need to repeat this step when you upgrade to a new minor version of {% data variables.product.product_name %}, or if you manually update the {% data variables.product.prodname_dependabot %} action from {% data variables.product.prodname_dotcom_the_website %}. For more information, see "[AUTOTITLE](/admin/github-actions/managing-access-to-actions-from-githubcom/manually-syncing-actions-from-githubcom)." 1. When you have finished adding these images to the runner, you are ready to restrict internet access to the {% data variables.product.prodname_dependabot %} runner, ensuring that it can still access your private registries for the required ecosystems and for {% data variables.product.prodname_ghe_server %}. diff --git a/content/admin/managing-code-security/managing-supply-chain-security-for-your-enterprise/enabling-the-dependency-graph-for-your-enterprise.md b/content/admin/managing-code-security/managing-supply-chain-security-for-your-enterprise/enabling-the-dependency-graph-for-your-enterprise.md index 2f02b222e5f2..8d7ea2a26817 100644 --- a/content/admin/managing-code-security/managing-supply-chain-security-for-your-enterprise/enabling-the-dependency-graph-for-your-enterprise.md +++ b/content/admin/managing-code-security/managing-supply-chain-security-for-your-enterprise/enabling-the-dependency-graph-for-your-enterprise.md @@ -45,11 +45,9 @@ If your instance uses clustering, you cannot enable the dependency graph with th ghe-config app.dependency-graph.enabled true ``` - {% note %} + > [!NOTE] + > For more information about enabling access to the administrative shell via SSH, see "[AUTOTITLE](/admin/configuration/configuring-your-enterprise/accessing-the-administrative-shell-ssh)." - **Note**: For more information about enabling access to the administrative shell via SSH, see "[AUTOTITLE](/admin/configuration/configuring-your-enterprise/accessing-the-administrative-shell-ssh)." - - {% endnote %} 1. Apply the configuration. ```shell diff --git a/content/admin/managing-github-actions-for-your-enterprise/advanced-configuration-and-troubleshooting/troubleshooting-github-actions-for-your-enterprise.md b/content/admin/managing-github-actions-for-your-enterprise/advanced-configuration-and-troubleshooting/troubleshooting-github-actions-for-your-enterprise.md index ccce4dd5fbfa..09ab8f6cc3f4 100644 --- a/content/admin/managing-github-actions-for-your-enterprise/advanced-configuration-and-troubleshooting/troubleshooting-github-actions-for-your-enterprise.md +++ b/content/admin/managing-github-actions-for-your-enterprise/advanced-configuration-and-troubleshooting/troubleshooting-github-actions-for-your-enterprise.md @@ -110,11 +110,9 @@ If any of these services are at or near 100% CPU utilization, or the memory is n For CPU and memory, this shows how much is allocated to the **total** of **all** services (the left value) and how much is available (the right value). In the example above, there is 23 GiB of memory allocated out of 32 GiB total. This means there is 9 GiB of memory available for allocation. - {% warning %} + > [!WARNING] + > Be careful not to allocate more than the total available resources, or services will fail to start. - **Warning:** Be careful not to allocate more than the total available resources, or services will fail to start. - - {% endwarning %} 1. Change directory to `/etc/consul-templates/etc/nomad-jobs/actions`: ```shell diff --git a/content/admin/managing-github-actions-for-your-enterprise/advanced-configuration-and-troubleshooting/using-a-staging-environment.md b/content/admin/managing-github-actions-for-your-enterprise/advanced-configuration-and-troubleshooting/using-a-staging-environment.md index 0d84cb19db01..3c76ba002ddb 100644 --- a/content/admin/managing-github-actions-for-your-enterprise/advanced-configuration-and-troubleshooting/using-a-staging-environment.md +++ b/content/admin/managing-github-actions-for-your-enterprise/advanced-configuration-and-troubleshooting/using-a-staging-environment.md @@ -29,11 +29,8 @@ After you restore a {% data variables.product.prodname_ghe_server %} backup to t When you set up a staging environment that includes a {% data variables.product.product_name %} instance with {% data variables.product.prodname_actions %} enabled, you must use a different external storage configuration for {% data variables.product.prodname_actions %} storage than your production environment. -{% warning %} - -**Warning**: If you don't change the storage configuration, your staging instance may be able to write to the same external storage that you use for production, which could result in loss of data. - -{% endwarning %} +> [!WARNING] +> If you don't change the storage configuration, your staging instance may be able to write to the same external storage that you use for production, which could result in loss of data. For more information about storage configuration for {% data variables.product.prodname_actions %}, see "[AUTOTITLE](/admin/github-actions/getting-started-with-github-actions-for-your-enterprise/getting-started-with-github-actions-for-github-enterprise-server#enabling-github-actions-with-your-storage-provider)." diff --git a/content/admin/managing-github-actions-for-your-enterprise/enabling-github-actions-for-github-enterprise-server/enabling-github-actions-with-amazon-s3-storage.md b/content/admin/managing-github-actions-for-your-enterprise/enabling-github-actions-for-github-enterprise-server/enabling-github-actions-with-amazon-s3-storage.md index e17ca79a8fd0..12599182d505 100644 --- a/content/admin/managing-github-actions-for-your-enterprise/enabling-github-actions-for-github-enterprise-server/enabling-github-actions-with-amazon-s3-storage.md +++ b/content/admin/managing-github-actions-for-your-enterprise/enabling-github-actions-for-github-enterprise-server/enabling-github-actions-with-amazon-s3-storage.md @@ -24,13 +24,10 @@ shortTitle: Amazon S3 storage ## Prerequisites -{% note %} - -**Note:** The only {% data variables.product.prodname_dotcom %}-supported S3 storage providers are Amazon S3 and MinIO Gateway for NAS. - -{% data reusables.actions.enterprise-s3-tech-partners %} - -{% endnote %} +> [!NOTE] +> The only {% data variables.product.prodname_dotcom %}-supported S3 storage providers are Amazon S3 and MinIO Gateway for NAS. +> +> {% data reusables.actions.enterprise-s3-tech-partners %} Before enabling {% data variables.product.prodname_actions %}, make sure you have completed the following steps: @@ -93,11 +90,8 @@ To configure {% data variables.product.prodname_ghe_server %} to use OIDC with a For more information on installing the AWS CLI, see the [Amazon documentation](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html). - {% warning %} - - **Warning:** If the certificate for {% data variables.location.product_location_enterprise %} changes in the future, you must update the thumbprint value in the Amazon OIDC provider for the OIDC trust to continue to work. - - {% endwarning %} + > [!WARNING] + > If the certificate for {% data variables.location.product_location_enterprise %} changes in the future, you must update the thumbprint value in the Amazon OIDC provider for the OIDC trust to continue to work. ### 2. Create an IAM role diff --git a/content/admin/managing-github-actions-for-your-enterprise/enabling-github-actions-for-github-enterprise-server/enabling-github-actions-with-azure-blob-storage.md b/content/admin/managing-github-actions-for-your-enterprise/enabling-github-actions-for-github-enterprise-server/enabling-github-actions-with-azure-blob-storage.md index 9447c556601c..a21d5b251c5c 100644 --- a/content/admin/managing-github-actions-for-your-enterprise/enabling-github-actions-for-github-enterprise-server/enabling-github-actions-with-azure-blob-storage.md +++ b/content/admin/managing-github-actions-for-your-enterprise/enabling-github-actions-for-github-enterprise-server/enabling-github-actions-with-azure-blob-storage.md @@ -29,11 +29,9 @@ Before enabling {% data variables.product.prodname_actions %}, make sure you hav * Create your Azure storage account for storing workflow data. {% data variables.product.prodname_actions %} stores its data as block blobs, and two storage account types are supported: * A **general-purpose** storage account (also known as `general-purpose v1` or `general-purpose v2`) using the **standard** performance tier. - {% warning %} + > [!WARNING] + > Using the **premium** performance tier with a general-purpose storage account is not supported. The **standard** performance tier must be selected when creating the storage account, and it cannot be changed later. - **Warning:** Using the **premium** performance tier with a general-purpose storage account is not supported. The **standard** performance tier must be selected when creating the storage account, and it cannot be changed later. - - {% endwarning %} * A **BlockBlobStorage** storage account, which uses the **premium** performance tier. For more information on Azure storage account types and performance tiers, see the [Azure documentation](https://docs.microsoft.com/en-us/azure/storage/common/storage-account-overview?toc=/azure/storage/blobs/toc.json#types-of-storage-accounts). @@ -61,11 +59,9 @@ To configure {% data variables.product.prodname_ghe_server %} to use OIDC with a 1. For "Issuer", enter `https://HOSTNAME/_services/token`, where `HOSTNAME` is the public hostname for {% data variables.location.product_location_enterprise %}. For example, `https://my-ghes-host.example.com/_services/token`. 1. For "Subject identifier", enter the public hostname for {% data variables.location.product_location_enterprise %}. For example, `my-ghes-host.example.com`. - {% note %} - - **Note:** The subject identifier must only have the hostname of {% data variables.location.product_location_enterprise %}, and _must not_ include the protocol. + > [!NOTE] + > The subject identifier must only have the hostname of {% data variables.location.product_location_enterprise %}, and _must not_ include the protocol. - {% endnote %} 1. For "Name", enter a name for the credential. 1. Click **Add**. diff --git a/content/admin/managing-github-actions-for-your-enterprise/enabling-github-actions-for-github-enterprise-server/enabling-github-actions-with-google-cloud-storage.md b/content/admin/managing-github-actions-for-your-enterprise/enabling-github-actions-for-github-enterprise-server/enabling-github-actions-with-google-cloud-storage.md index f4b48b62922b..48d36d6c71c7 100644 --- a/content/admin/managing-github-actions-for-your-enterprise/enabling-github-actions-for-github-enterprise-server/enabling-github-actions-with-google-cloud-storage.md +++ b/content/admin/managing-github-actions-for-your-enterprise/enabling-github-actions-for-github-enterprise-server/enabling-github-actions-with-google-cloud-storage.md @@ -15,11 +15,8 @@ redirect_from: - /admin/github-actions/enabling-github-actions-for-github-enterprise-server/enabling-github-actions-with-google-cloud-storage --- -{% note %} - -**Note:** {% data variables.product.prodname_actions %} support for Google Cloud Storage is currently in {% data variables.release-phases.public_preview %} and subject to change. - -{% endnote %} +> [!NOTE] +> {% data variables.product.prodname_actions %} support for Google Cloud Storage is currently in {% data variables.release-phases.public_preview %} and subject to change. {% data reusables.actions.enterprise-storage-about %} @@ -91,11 +88,9 @@ To configure {% data variables.product.prodname_ghe_server %} to use OIDC with G google.subject == "my-ghes-host.example.com" ``` - {% note %} - - **Note:** The hostname of {% data variables.location.product_location_enterprise %} used here _must not_ include the protocol. + > [!NOTE] + > The hostname of {% data variables.location.product_location_enterprise %} used here _must not_ include the protocol. - {% endnote %} * Click **Save**. 1. After creating the identity pool, at the top of the identity pool's page, click **Grant access**. * Under "Select service account", select the service account that you created in the previous procedure. diff --git a/content/admin/managing-github-actions-for-your-enterprise/enabling-github-actions-for-github-enterprise-server/enabling-github-actions-with-minio-storage.md b/content/admin/managing-github-actions-for-your-enterprise/enabling-github-actions-for-github-enterprise-server/enabling-github-actions-with-minio-storage.md index 8052d6e66482..45704ffc7e0d 100644 --- a/content/admin/managing-github-actions-for-your-enterprise/enabling-github-actions-for-github-enterprise-server/enabling-github-actions-with-minio-storage.md +++ b/content/admin/managing-github-actions-for-your-enterprise/enabling-github-actions-for-github-enterprise-server/enabling-github-actions-with-minio-storage.md @@ -41,11 +41,8 @@ Before enabling {% data variables.product.prodname_actions %}, make sure you hav {% data reusables.actions.enterprise-s3-storage-setup %} 1. Under "Authentication", select **Credentials-based**, and enter your storage bucket's details: - {% note %} - - **Note:** For MinIO, you cannot use OpenID Connect (OIDC) authentication. You must use credentials-based authentication. - - {% endnote %} + > [!NOTE] + > For MinIO, you cannot use OpenID Connect (OIDC) authentication. You must use credentials-based authentication. {% data reusables.actions.enterprise-minio-storage-credential-fields %} {%- else %} diff --git a/content/admin/managing-github-actions-for-your-enterprise/getting-started-with-github-actions-for-your-enterprise/getting-started-with-github-actions-for-github-enterprise-server.md b/content/admin/managing-github-actions-for-your-enterprise/getting-started-with-github-actions-for-your-enterprise/getting-started-with-github-actions-for-github-enterprise-server.md index 3aeff00439db..559fa5e608ea 100644 --- a/content/admin/managing-github-actions-for-your-enterprise/getting-started-with-github-actions-for-your-enterprise/getting-started-with-github-actions-for-github-enterprise-server.md +++ b/content/admin/managing-github-actions-for-your-enterprise/getting-started-with-github-actions-for-your-enterprise/getting-started-with-github-actions-for-github-enterprise-server.md @@ -44,14 +44,9 @@ The peak quantity of connected runners without performance loss depends on such {% data variables.product.company_short %} measured maximum connected runners using multiple repositories, job duration of approximately 10 minutes, and 10 MB artifact uploads. You may experience different performance depending on the overall levels of activity on your instance. -{% note %} - -**Notes:** - -* Beginning with {% data variables.product.prodname_ghe_server %} 3.6, {% data variables.product.company_short %} documents connected runners as opposed to concurrent jobs. Connected runners represents the most runners you can connect and expect to utilize. It should also be noted that connecting more runners than you can expect to utilize can negatively impact performance. - -* Beginning with {% data variables.product.prodname_ghe_server %} 3.5, {% data variables.product.company_short %}'s internal testing uses 3rd generation CPUs to better reflect a typical customer configuration. This change in CPU represents a small portion of the changes to performance targets in this version of {% data variables.product.prodname_ghe_server %}. -{% endnote %} +> [!NOTE] +> * Beginning with {% data variables.product.prodname_ghe_server %} 3.6, {% data variables.product.company_short %} documents connected runners as opposed to concurrent jobs. Connected runners represents the most runners you can connect and expect to utilize. It should also be noted that connecting more runners than you can expect to utilize can negatively impact performance. +> * Beginning with {% data variables.product.prodname_ghe_server %} 3.5, {% data variables.product.company_short %}'s internal testing uses 3rd generation CPUs to better reflect a typical customer configuration. This change in CPU represents a small portion of the changes to performance targets in this version of {% data variables.product.prodname_ghe_server %}. If you plan to enable {% data variables.product.prodname_actions %} for the users of an existing instance, review the levels of activity for users and automations on the instance and ensure that you have provisioned adequate CPU and memory for your users. For more information about monitoring the capacity and performance of {% data variables.product.prodname_ghe_server %}, see "[AUTOTITLE](/admin/enterprise-management/monitoring-your-appliance)." @@ -89,13 +84,10 @@ All other {% data variables.product.prodname_actions %} data, such as the workfl {%- endif %} * S3-compatible MinIO cluster -{% note %} - -**Note:** These are the only storage providers that {% data variables.product.company_short %} supports and can provide assistance with. - -{% data reusables.actions.enterprise-s3-tech-partners %} - -{% endnote %} +> [!NOTE] +> These are the only storage providers that {% data variables.product.company_short %} supports and can provide assistance with. +> +> {% data reusables.actions.enterprise-s3-tech-partners %} ## Networking considerations diff --git a/content/admin/managing-github-actions-for-your-enterprise/getting-started-with-github-actions-for-your-enterprise/getting-started-with-self-hosted-runners-for-your-enterprise.md b/content/admin/managing-github-actions-for-your-enterprise/getting-started-with-github-actions-for-your-enterprise/getting-started-with-self-hosted-runners-for-your-enterprise.md index db66c5ba17c2..579a100bb523 100644 --- a/content/admin/managing-github-actions-for-your-enterprise/getting-started-with-github-actions-for-your-enterprise/getting-started-with-self-hosted-runners-for-your-enterprise.md +++ b/content/admin/managing-github-actions-for-your-enterprise/getting-started-with-github-actions-for-your-enterprise/getting-started-with-self-hosted-runners-for-your-enterprise.md @@ -81,15 +81,11 @@ You can create a runner group to manage access to the runner that you added to y {%- ifversion ghec or ghes %} 1. Optionally, to allow public repositories in the selected organizations to use runners in the group, select **Allow public repositories**. - {% warning %} + > [!WARNING] + > {% data reusables.actions.self-hosted-runner-security %} + > + > For more information, see "[AUTOTITLE](/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#self-hosted-runner-security-with-public-repositories)." - **Warning**: - - {% data reusables.actions.self-hosted-runner-security %} - - For more information, see "[AUTOTITLE](/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#self-hosted-runner-security-with-public-repositories)." - - {% endwarning %} {%- endif %} {% data reusables.actions.create-runner-group %} 1. Click the "Runners" tab. diff --git a/content/admin/managing-github-actions-for-your-enterprise/managing-access-to-actions-from-githubcom/about-using-actions-in-your-enterprise.md b/content/admin/managing-github-actions-for-your-enterprise/managing-access-to-actions-from-githubcom/about-using-actions-in-your-enterprise.md index 02abec8f664c..ac454a5e114b 100644 --- a/content/admin/managing-github-actions-for-your-enterprise/managing-access-to-actions-from-githubcom/about-using-actions-in-your-enterprise.md +++ b/content/admin/managing-github-actions-for-your-enterprise/managing-access-to-actions-from-githubcom/about-using-actions-in-your-enterprise.md @@ -43,13 +43,9 @@ There is no connection required between {% data variables.location.product_locat Each action is a repository in the `actions` organization, and each action repository includes the necessary tags, branches, and commit SHAs that your workflows can use to reference the action. For information on how to update the bundled official actions, see "[AUTOTITLE](/admin/github-actions/managing-access-to-actions-from-githubcom/using-the-latest-version-of-the-official-bundled-actions)." -{% note %} - -**Notes:** -* When using setup actions (such as `actions/setup-LANGUAGE`) on {% data variables.product.product_name %} with self-hosted runners, you might need to set up the tools cache on runners that do not have internet access. For more information, see "[AUTOTITLE](/admin/github-actions/managing-access-to-actions-from-githubcom/setting-up-the-tool-cache-on-self-hosted-runners-without-internet-access)." -* When {% data variables.product.product_name %} is updated, bundled actions are automatically replaced with default versions in the upgrade package. - -{% endnote %} +> [!NOTE] +> * When using setup actions (such as `actions/setup-LANGUAGE`) on {% data variables.product.product_name %} with self-hosted runners, you might need to set up the tools cache on runners that do not have internet access. For more information, see "[AUTOTITLE](/admin/github-actions/managing-access-to-actions-from-githubcom/setting-up-the-tool-cache-on-self-hosted-runners-without-internet-access)." +> * When {% data variables.product.product_name %} is updated, bundled actions are automatically replaced with default versions in the upgrade package. ## Configuring access to actions on {% data variables.product.prodname_dotcom_the_website %} @@ -58,11 +54,10 @@ Each action is a repository in the `actions` organization, and each action repos The recommended approach is to enable automatic access to all actions from {% data variables.product.prodname_dotcom_the_website %}. You can do this by using {% data variables.product.prodname_github_connect %} to integrate {% data variables.product.product_name %} with {% data variables.product.prodname_ghe_cloud %}. For more information, see "[AUTOTITLE](/admin/github-actions/managing-access-to-actions-from-githubcom/enabling-automatic-access-to-githubcom-actions-using-github-connect)". {% ifversion ghes %} -{% note %} -**Note:** Before you can configure access to actions on {% data variables.product.prodname_dotcom_the_website %}, you must configure {% data variables.location.product_location %} to use {% data variables.product.prodname_actions %}. For more information, see "[AUTOTITLE](/admin/github-actions/getting-started-with-github-actions-for-your-enterprise/getting-started-with-github-actions-for-github-enterprise-server)." +> [!NOTE] +> Before you can configure access to actions on {% data variables.product.prodname_dotcom_the_website %}, you must configure {% data variables.location.product_location %} to use {% data variables.product.prodname_actions %}. For more information, see "[AUTOTITLE](/admin/github-actions/getting-started-with-github-actions-for-your-enterprise/getting-started-with-github-actions-for-github-enterprise-server)." -{% endnote %} {% endif %} {% data reusables.actions.self-hosted-runner-networking-to-dotcom %} diff --git a/content/admin/managing-github-actions-for-your-enterprise/managing-access-to-actions-from-githubcom/enabling-automatic-access-to-githubcom-actions-using-github-connect.md b/content/admin/managing-github-actions-for-your-enterprise/managing-access-to-actions-from-githubcom/enabling-automatic-access-to-githubcom-actions-using-github-connect.md index f50b98a5d3d8..c4cd9c90feab 100644 --- a/content/admin/managing-github-actions-for-your-enterprise/managing-access-to-actions-from-githubcom/enabling-automatic-access-to-githubcom-actions-using-github-connect.md +++ b/content/admin/managing-github-actions-for-your-enterprise/managing-access-to-actions-from-githubcom/enabling-automatic-access-to-githubcom-actions-using-github-connect.md @@ -56,8 +56,5 @@ After using an action from {% data variables.product.prodname_dotcom_the_website 1. To the right of the namespace that you want use in {% data variables.location.product_location %}, click **Unretire**. 1. Go to the relevant organization and create a new repository. - {% tip %} - - **Tip:** When you unretire a namespace, always create the new repository with that name as soon as possible. If a workflow calls the associated action on {% data variables.product.prodname_dotcom_the_website %} before you create the local repository, the namespace will be retired again. For actions used in workflows that run frequently, you may find that a namespace is retired again before you have time to create the local repository. In this case, you can temporarily disable the relevant workflows until you have created the new repository. - - {% endtip %} + > [!TIP] + > When you unretire a namespace, always create the new repository with that name as soon as possible. If a workflow calls the associated action on {% data variables.product.prodname_dotcom_the_website %} before you create the local repository, the namespace will be retired again. For actions used in workflows that run frequently, you may find that a namespace is retired again before you have time to create the local repository. In this case, you can temporarily disable the relevant workflows until you have created the new repository. diff --git a/content/admin/managing-github-actions-for-your-enterprise/managing-access-to-actions-from-githubcom/manually-syncing-actions-from-githubcom.md b/content/admin/managing-github-actions-for-your-enterprise/managing-access-to-actions-from-githubcom/manually-syncing-actions-from-githubcom.md index 33ce08ba65e2..e59f9505e922 100644 --- a/content/admin/managing-github-actions-for-your-enterprise/managing-access-to-actions-from-githubcom/manually-syncing-actions-from-githubcom.md +++ b/content/admin/managing-github-actions-for-your-enterprise/managing-access-to-actions-from-githubcom/manually-syncing-actions-from-githubcom.md @@ -34,11 +34,8 @@ If your machine has access to both systems at the same time, you can do the sync The `actions-sync` tool can only download actions from {% data variables.product.prodname_dotcom_the_website %} that are stored in public repositories. -{% note %} - -**Note:** The `actions-sync` tool is intended for use in systems where {% data variables.product.prodname_github_connect %} is not enabled. If you run the tool on a system with {% data variables.product.prodname_github_connect %} enabled, you may see the error `The repository has been retired and cannot be reused`. This indicates that a workflow has used that action directly on {% data variables.product.prodname_dotcom_the_website %} and the namespace is retired on {% data variables.location.product_location %}. See "[AUTOTITLE](/admin/github-actions/managing-access-to-actions-from-githubcom/enabling-automatic-access-to-githubcom-actions-using-github-connect#automatic-retirement-of-namespaces-for-actions-accessed-on-githubcom)." - -{% endnote %} +> [!NOTE] +> The `actions-sync` tool is intended for use in systems where {% data variables.product.prodname_github_connect %} is not enabled. If you run the tool on a system with {% data variables.product.prodname_github_connect %} enabled, you may see the error `The repository has been retired and cannot be reused`. This indicates that a workflow has used that action directly on {% data variables.product.prodname_dotcom_the_website %} and the namespace is retired on {% data variables.location.product_location %}. See "[AUTOTITLE](/admin/github-actions/managing-access-to-actions-from-githubcom/enabling-automatic-access-to-githubcom-actions-using-github-connect#automatic-retirement-of-namespaces-for-actions-accessed-on-githubcom)." ## Prerequisites @@ -46,11 +43,8 @@ The `actions-sync` tool can only download actions from {% data variables.product * You must create a {% data variables.product.pat_generic %} on your enterprise that can create and write to repositories in the destination organizations. See "[AUTOTITLE](/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token)."{% ifversion ghes %} * If you want to sync the bundled actions in the `actions` organization on {% data variables.location.product_location %}, you must be an owner of the `actions` organization. - {% note %} - - **Note:** By default, even site administrators are not owners of the bundled `actions` organization. - - {% endnote %} + > [!NOTE] + > By default, even site administrators are not owners of the bundled `actions` organization. Site administrators can use the `ghe-org-admin-promote` command in the administrative shell to promote a user to be an owner of the bundled `actions` organization. See "[AUTOTITLE](/admin/configuration/configuring-your-enterprise/accessing-the-administrative-shell-ssh)" and "[AUTOTITLE](/admin/configuration/configuring-your-enterprise/command-line-utilities#ghe-org-admin-promote)." @@ -62,11 +56,8 @@ The `actions-sync` tool can only download actions from {% data variables.product This example demonstrates using the `actions-sync` tool to sync an individual action from {% data variables.product.prodname_dotcom_the_website %} to an enterprise instance. -{% note %} - -**Note:** This example uses the `actions-sync sync` command, which requires concurrent access to both the {% data variables.product.prodname_dotcom_the_website %} API and your enterprise instance's API from your machine. If you can only access one system at a time, you can use the `actions-sync pull` and `push` commands. See the [`actions-sync` README](https://github.com/actions/actions-sync#not-connected-instances). - -{% endnote %} +> [!NOTE] +> This example uses the `actions-sync sync` command, which requires concurrent access to both the {% data variables.product.prodname_dotcom_the_website %} API and your enterprise instance's API from your machine. If you can only access one system at a time, you can use the `actions-sync pull` and `push` commands. See the [`actions-sync` README](https://github.com/actions/actions-sync#not-connected-instances). 1. Download and extract the latest [`actions-sync` release](https://github.com/actions/actions-sync/releases) for your machine's operating system. 1. Create a directory to store cache files for the tool. diff --git a/content/admin/managing-github-actions-for-your-enterprise/managing-access-to-actions-from-githubcom/setting-up-the-tool-cache-on-self-hosted-runners-without-internet-access.md b/content/admin/managing-github-actions-for-your-enterprise/managing-access-to-actions-from-githubcom/setting-up-the-tool-cache-on-self-hosted-runners-without-internet-access.md index 9561e3019f18..5be74cbef0be 100644 --- a/content/admin/managing-github-actions-for-your-enterprise/managing-access-to-actions-from-githubcom/setting-up-the-tool-cache-on-self-hosted-runners-without-internet-access.md +++ b/content/admin/managing-github-actions-for-your-enterprise/managing-access-to-actions-from-githubcom/setting-up-the-tool-cache-on-self-hosted-runners-without-internet-access.md @@ -27,11 +27,8 @@ The `actions/setup-LANGUAGE` actions normally need internet access to download t You can populate the runner tool cache by running a {% data variables.product.prodname_actions %} workflow on {% data variables.product.prodname_dotcom_the_website %} that uploads a {% data variables.product.prodname_dotcom %}-hosted runner's tool cache as an artifact, which you can then transfer and extract on your internet-disconnected self-hosted runner. -{% note %} - -**Note:** You can only use a {% data variables.product.prodname_dotcom %}-hosted runner's tool cache for a self-hosted runner that has an identical operating system and architecture. For example, if you are using a `ubuntu-22.04` {% data variables.product.prodname_dotcom %}-hosted runner to generate a tool cache, your self-hosted runner must be a 64-bit Ubuntu 22.04 machine. For more information on {% data variables.product.prodname_dotcom %}-hosted runners, see "[AUTOTITLE](/free-pro-team@latest/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources)." - -{% endnote %} +> [!NOTE] +> You can only use a {% data variables.product.prodname_dotcom %}-hosted runner's tool cache for a self-hosted runner that has an identical operating system and architecture. For example, if you are using a `ubuntu-22.04` {% data variables.product.prodname_dotcom %}-hosted runner to generate a tool cache, your self-hosted runner must be a 64-bit Ubuntu 22.04 machine. For more information on {% data variables.product.prodname_dotcom %}-hosted runners, see "[AUTOTITLE](/free-pro-team@latest/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources)." ## Prerequisites diff --git a/content/admin/managing-github-actions-for-your-enterprise/managing-access-to-actions-from-githubcom/using-the-latest-version-of-the-official-bundled-actions.md b/content/admin/managing-github-actions-for-your-enterprise/managing-access-to-actions-from-githubcom/using-the-latest-version-of-the-official-bundled-actions.md index 7076fbab666f..9240db4d5cb8 100644 --- a/content/admin/managing-github-actions-for-your-enterprise/managing-access-to-actions-from-githubcom/using-the-latest-version-of-the-official-bundled-actions.md +++ b/content/admin/managing-github-actions-for-your-enterprise/managing-access-to-actions-from-githubcom/using-the-latest-version-of-the-official-bundled-actions.md @@ -46,8 +46,5 @@ Once {% data variables.product.prodname_github_connect %} is configured, you can 1. Configure your workflow's YAML to use `{% data reusables.actions.action-checkout %}`. 1. Each time your workflow runs, the runner will use the specified version of `actions/checkout` from {% data variables.product.prodname_dotcom_the_website %}. - {% note %} - - **Note:** The first time the `checkout` action is used from {% data variables.product.prodname_dotcom_the_website %}, the `actions/checkout` namespace is automatically retired on {% data variables.location.product_location %}. If you ever want to revert to using a local copy of the action, you first need to remove the namespace from retirement. For more information, see "[AUTOTITLE](/admin/github-actions/managing-access-to-actions-from-githubcom/enabling-automatic-access-to-githubcom-actions-using-github-connect#automatic-retirement-of-namespaces-for-actions-accessed-on-githubcom)." - - {% endnote %} + > [!NOTE] + > The first time the `checkout` action is used from {% data variables.product.prodname_dotcom_the_website %}, the `actions/checkout` namespace is automatically retired on {% data variables.location.product_location %}. If you ever want to revert to using a local copy of the action, you first need to remove the namespace from retirement. For more information, see "[AUTOTITLE](/admin/github-actions/managing-access-to-actions-from-githubcom/enabling-automatic-access-to-githubcom-actions-using-github-connect#automatic-retirement-of-namespaces-for-actions-accessed-on-githubcom)." diff --git a/content/admin/managing-iam/configuring-authentication-for-enterprise-managed-users/configuring-saml-single-sign-on-for-enterprise-managed-users.md b/content/admin/managing-iam/configuring-authentication-for-enterprise-managed-users/configuring-saml-single-sign-on-for-enterprise-managed-users.md index af1bf222866a..79a08bc9a3aa 100644 --- a/content/admin/managing-iam/configuring-authentication-for-enterprise-managed-users/configuring-saml-single-sign-on-for-enterprise-managed-users.md +++ b/content/admin/managing-iam/configuring-authentication-for-enterprise-managed-users/configuring-saml-single-sign-on-for-enterprise-managed-users.md @@ -100,11 +100,9 @@ After the initial configuration of SAML SSO, the only setting you can update on {% data reusables.emus.sign-in-as-setup-user %} - {% note %} - - **Note**: {% data reusables.enterprise-accounts.emu-password-reset-session %} + > [!NOTE] + > {% data reusables.enterprise-accounts.emu-password-reset-session %} - {% endnote %} {% data reusables.enterprise-accounts.access-enterprise-emu %} {% data reusables.enterprise-accounts.settings-tab %} {% data reusables.enterprise-accounts.security-tab %} @@ -117,11 +115,8 @@ After the initial configuration of SAML SSO, the only setting you can update on 1. Before enabling SAML SSO for your enterprise, to ensure that the information you've entered is correct, click **Test SAML configuration**. {% data reusables.saml.test-must-succeed %} 1. Click **Save**. - {% note %} - - **Note:** After you require SAML SSO for your enterprise and save SAML settings, the setup user will continue to have access to the enterprise and will remain signed in to GitHub along with the {% data variables.enterprise.prodname_managed_users %} provisioned by your IdP who will also have access to the enterprise. - - {% endnote %} + > [!NOTE] + > After you require SAML SSO for your enterprise and save SAML settings, the setup user will continue to have access to the enterprise and will remain signed in to GitHub along with the {% data variables.enterprise.prodname_managed_users %} provisioned by your IdP who will also have access to the enterprise. {% data reusables.enterprise-accounts.download-recovery-codes %} diff --git a/content/admin/managing-iam/configuring-authentication-for-enterprise-managed-users/disabling-authentication-and-provisioning-for-enterprise-managed-users.md b/content/admin/managing-iam/configuring-authentication-for-enterprise-managed-users/disabling-authentication-and-provisioning-for-enterprise-managed-users.md index f7c530a8f891..3a15ba026c5f 100644 --- a/content/admin/managing-iam/configuring-authentication-for-enterprise-managed-users/disabling-authentication-and-provisioning-for-enterprise-managed-users.md +++ b/content/admin/managing-iam/configuring-authentication-for-enterprise-managed-users/disabling-authentication-and-provisioning-for-enterprise-managed-users.md @@ -28,21 +28,15 @@ After you disable SAML or OIDC SSO for your enterprise, the following effects ap If you later reconfigure authentication for the enterprise, external groups must be reprovisioned via SCIM, and {% data variables.enterprise.prodname_managed_users %} must be reprovisioned before users can sign in. -{% note %} - -**Note:** When a {% data variables.enterprise.prodname_managed_user %} is suspended, the user's avatar is permanently deleted. If you reprovision the user, the user will need to reupload their avatar. - -{% endnote %} +> [!NOTE] +> When a {% data variables.enterprise.prodname_managed_user %} is suspended, the user's avatar is permanently deleted. If you reprovision the user, the user will need to reupload their avatar. If you want to migrate to a new identity provider (IdP) or tenant rather than disabling authentication entirely, see "[AUTOTITLE](/admin/identity-and-access-management/using-enterprise-managed-users-for-iam/migrating-your-enterprise-to-a-new-identity-provider-or-tenant)." ## Disabling authentication -{% warning %} - -**Warning**: Disabling authentication and provisioning will prevent your enterprise's {% data variables.enterprise.prodname_managed_users %} from signing in to access your enterprise on {% data variables.product.product_name %}. - -{% endwarning %} +> [!WARNING] +> Disabling authentication and provisioning will prevent your enterprise's {% data variables.enterprise.prodname_managed_users %} from signing in to access your enterprise on {% data variables.product.product_name %}. {% data reusables.emus.sign-in-as-setup-user %} 1. Attempt to access your enterprise account, and use a recovery code to bypass SAML SSO or OIDC. For more information, see "[AUTOTITLE](/admin/identity-and-access-management/managing-recovery-codes-for-your-enterprise/accessing-your-enterprise-account-if-your-identity-provider-is-unavailable)." diff --git a/content/admin/managing-iam/iam-configuration-reference/username-considerations-for-external-authentication.md b/content/admin/managing-iam/iam-configuration-reference/username-considerations-for-external-authentication.md index b4d2d76db57e..1fa8a0bb690f 100644 --- a/content/admin/managing-iam/iam-configuration-reference/username-considerations-for-external-authentication.md +++ b/content/admin/managing-iam/iam-configuration-reference/username-considerations-for-external-authentication.md @@ -19,11 +19,10 @@ redirect_from: --- {% ifversion ghec %} -{% note %} -**Note:** This article only applies to {% data variables.product.prodname_emus %}. If you use {% data variables.product.prodname_ghe_cloud %} without {% data variables.product.prodname_emus %}, usernames are created by users, not {% data variables.product.prodname_dotcom %}. +> [!NOTE] +> This article only applies to {% data variables.product.prodname_emus %}. If you use {% data variables.product.prodname_ghe_cloud %} without {% data variables.product.prodname_emus %}, usernames are created by users, not {% data variables.product.prodname_dotcom %}. -{% endnote %} {% endif %} ## About usernames with external authentication @@ -142,11 +141,10 @@ When you configure CAS, LDAP, or SAML authentication, {% data variables.product. {% data variables.product.product_name %} creates a mapping between the `NameID` from the IdP and the username on {% data variables.location.product_location %}, so the `NameID` should be persistent, unique, and not subject to change for the lifecycle of the user. {% ifversion ghes %} -{% note %} -**Note**: If the `NameID` for a user does change on the IdP, the person will see an error message when signing in to {% data variables.location.product_location %}. To restore the person's access, you'll need to update the user account's `NameID` mapping. For more information, see "[AUTOTITLE](/admin/identity-and-access-management/using-saml-for-enterprise-iam/updating-a-users-saml-nameid)." +> [!NOTE] +> If the `NameID` for a user does change on the IdP, the person will see an error message when signing in to {% data variables.location.product_location %}. To restore the person's access, you'll need to update the user account's `NameID` mapping. For more information, see "[AUTOTITLE](/admin/identity-and-access-management/using-saml-for-enterprise-iam/updating-a-users-saml-nameid)." -{% endnote %} {% endif %} {% endif %} @@ -164,11 +162,8 @@ To resolve this problem, you must make one of the following changes in your IdP When you change the attribute mapping, usernames of existing {% data variables.enterprise.prodname_managed_users %} will be updated, but nothing else about the accounts will change, including activity history. -{% note %} - -**Note:** {% data variables.contact.github_support %} cannot provide assistance with customizing attribute mappings or configuring custom expressions. You can contact your IdP with any questions. - -{% endnote %} +> [!NOTE] +> {% data variables.contact.github_support %} cannot provide assistance with customizing attribute mappings or configuring custom expressions. You can contact your IdP with any questions. ### Resolving username problems with Entra ID diff --git a/content/admin/managing-iam/managing-recovery-codes-for-your-enterprise/accessing-your-enterprise-account-if-your-identity-provider-is-unavailable.md b/content/admin/managing-iam/managing-recovery-codes-for-your-enterprise/accessing-your-enterprise-account-if-your-identity-provider-is-unavailable.md index 70f9e08db210..0ae98d67294e 100644 --- a/content/admin/managing-iam/managing-recovery-codes-for-your-enterprise/accessing-your-enterprise-account-if-your-identity-provider-is-unavailable.md +++ b/content/admin/managing-iam/managing-recovery-codes-for-your-enterprise/accessing-your-enterprise-account-if-your-identity-provider-is-unavailable.md @@ -25,11 +25,8 @@ In order to access your enterprise account this way, you must have previously do ## Using a recovery code -{% note %} - -**Note:** If your enterprises uses {% data variables.product.prodname_emus %}, you must sign in as the setup user to use a recovery code. - -{% endnote %} +> [!NOTE] +> If your enterprises uses {% data variables.product.prodname_emus %}, you must sign in as the setup user to use a recovery code. 1. Attempt to access the enterprise account. {% data reusables.saml.recovery-code-access %} diff --git a/content/admin/managing-iam/managing-recovery-codes-for-your-enterprise/downloading-your-enterprise-accounts-single-sign-on-recovery-codes.md b/content/admin/managing-iam/managing-recovery-codes-for-your-enterprise/downloading-your-enterprise-accounts-single-sign-on-recovery-codes.md index e68cd34da09f..6e9013a313f2 100644 --- a/content/admin/managing-iam/managing-recovery-codes-for-your-enterprise/downloading-your-enterprise-accounts-single-sign-on-recovery-codes.md +++ b/content/admin/managing-iam/managing-recovery-codes-for-your-enterprise/downloading-your-enterprise-accounts-single-sign-on-recovery-codes.md @@ -25,11 +25,11 @@ If you did not save your recovery codes when you configured SSO, you can still a {% data reusables.enterprise-accounts.security-tab %} 1. Under{% ifversion oidc-for-emu %} either{% endif %} "Require SAML authentication"{% ifversion oidc-for-emu %} or "Require OIDC authentication"{% endif %}, click **Save your recovery codes**.{% ifversion oidc-for-emu %} - {% note %} - **Note:** OIDC SSO is only available for {% data variables.product.prodname_emus %}. For more information, see "[AUTOTITLE](/admin/identity-and-access-management/using-enterprise-managed-users-for-iam/about-enterprise-managed-users)." + > [!NOTE] + > OIDC SSO is only available for {% data variables.product.prodname_emus %}. For more information, see "[AUTOTITLE](/admin/identity-and-access-management/using-enterprise-managed-users-for-iam/about-enterprise-managed-users)." - {% endnote %}{% endif %} + {% endif %} ![Screenshot of the "Authentication security" screen. The "Save your recovery codes" hyperlink is highlighted with an orange outline.](/assets/images/help/enterprises/saml-recovery-codes-link.png) 1. To save your recovery codes, click **Download**, **Print**, or **Copy**. diff --git a/content/admin/managing-iam/provisioning-user-accounts-with-scim/configuring-scim-provisioning-for-users.md b/content/admin/managing-iam/provisioning-user-accounts-with-scim/configuring-scim-provisioning-for-users.md index 6d13799eca08..f05dc7f0b328 100644 --- a/content/admin/managing-iam/provisioning-user-accounts-with-scim/configuring-scim-provisioning-for-users.md +++ b/content/admin/managing-iam/provisioning-user-accounts-with-scim/configuring-scim-provisioning-for-users.md @@ -177,11 +177,9 @@ If you don't use a partner IdP, or if you only use a partner IdP for authenticat {% data reusables.emus.sign-in-as-setup-user %} - {% note %} + > [!NOTE] + > {% data reusables.enterprise-accounts.emu-password-reset-session %} - **Note**: {% data reusables.enterprise-accounts.emu-password-reset-session %} - - {% endnote %} {% data reusables.enterprise-accounts.access-enterprise-emu %} {% data reusables.enterprise-accounts.settings-tab %} {% data reusables.enterprise-accounts.security-tab %} diff --git a/content/admin/managing-iam/provisioning-user-accounts-with-scim/configuring-scim-provisioning-with-okta.md b/content/admin/managing-iam/provisioning-user-accounts-with-scim/configuring-scim-provisioning-with-okta.md index a7f91ad37db3..b19409414190 100644 --- a/content/admin/managing-iam/provisioning-user-accounts-with-scim/configuring-scim-provisioning-with-okta.md +++ b/content/admin/managing-iam/provisioning-user-accounts-with-scim/configuring-scim-provisioning-with-okta.md @@ -46,11 +46,10 @@ Alternatively, if you only intend to use Okta for SAML authentication and you wa | Reactivate Users | Users in Okta whose Okta accounts are reactivated and who are assigned back to {% ifversion ghec %}the {% data variables.product.prodname_emu_idp_application %}{% else %}{% data variables.product.company_short %}'s{% endif %} application on Okta will be enabled. | {% ifversion ghec %} -{% note %} -**Note:** {% data variables.product.prodname_emus %} does not support modifications to usernames. +> [!NOTE] +> {% data variables.product.prodname_emus %} does not support modifications to usernames. -{% endnote %} {% endif %} ## Prerequisites @@ -148,11 +147,8 @@ You can also automatically manage organization membership by adding groups to th {% data reusables.enterprise-managed.assigning-roles %} -{% note %} - -**Note:** You can only set the "Roles" attribute for an individual user, not a group. If you want to set roles for everyone in a group that is assigned to the application in Okta, you must use the "Roles" attribute for each group member, individually. - -{% endnote %} +> [!NOTE] +> You can only set the "Roles" attribute for an individual user, not a group. If you want to set roles for everyone in a group that is assigned to the application in Okta, you must use the "Roles" attribute for each group member, individually. ## How do I deprovision users and groups? diff --git a/content/admin/managing-iam/provisioning-user-accounts-with-scim/managing-team-memberships-with-identity-provider-groups.md b/content/admin/managing-iam/provisioning-user-accounts-with-scim/managing-team-memberships-with-identity-provider-groups.md index d929d4f7f7ab..8c9e1b3c1f32 100644 --- a/content/admin/managing-iam/provisioning-user-accounts-with-scim/managing-team-memberships-with-identity-provider-groups.md +++ b/content/admin/managing-iam/provisioning-user-accounts-with-scim/managing-team-memberships-with-identity-provider-groups.md @@ -30,11 +30,8 @@ topics: When a change to an IdP group or a new team connection results in a user joining a team in an organization they were not already a member of, the user will automatically be added to the organization. When you disconnect a group from a team, users who became members of the organization via team membership are removed from the organization if they are not assigned membership in the organization by any other means. -{% note %} - -**Note:** Organization owners can also add users to organizations manually, as long as the accounts have already been provisioned via SCIM. - -{% endnote %} +> [!NOTE] +> Organization owners can also add users to organizations manually, as long as the accounts have already been provisioned via SCIM. When group membership changes on your IdP, your IdP sends a SCIM request with the changes to {% data variables.product.prodname_dotcom %} according to the schedule determined by your IdP, so change may not be immediate. Any requests that change team or organization membership will register in the audit log as changes made by the account used to configure user provisioning. @@ -71,11 +68,8 @@ Any member of an organization can create a new team and connect the team to an I Organization owners {% ifversion ghes %}and team maintainers {% endif %}can manage the existing connection between an IdP group and a team.{% ifversion ghec %} If your enterprise does not use {% data variables.enterprise.prodname_managed_users %}, team maintainers can also manage the connection.{% endif %} -{% note %} - -**Note**: Before you connect an existing team on {% data variables.product.prodname_dotcom %} to an IdP group for the first time, all members of the team on {% data variables.product.prodname_dotcom %} must first be removed. For more information, see "[AUTOTITLE](/organizations/organizing-members-into-teams/removing-organization-members-from-a-team)." - -{% endnote %} +> [!NOTE] +> Before you connect an existing team on {% data variables.product.prodname_dotcom %} to an IdP group for the first time, all members of the team on {% data variables.product.prodname_dotcom %} must first be removed. For more information, see "[AUTOTITLE](/organizations/organizing-members-into-teams/removing-organization-members-from-a-team)." {% data reusables.profile.access_profile %} diff --git a/content/admin/managing-iam/provisioning-user-accounts-with-scim/provisioning-users-and-groups-with-scim-using-the-rest-api.md b/content/admin/managing-iam/provisioning-user-accounts-with-scim/provisioning-users-and-groups-with-scim-using-the-rest-api.md index da0291ad9f0e..56ee38861e31 100644 --- a/content/admin/managing-iam/provisioning-user-accounts-with-scim/provisioning-users-and-groups-with-scim-using-the-rest-api.md +++ b/content/admin/managing-iam/provisioning-user-accounts-with-scim/provisioning-users-and-groups-with-scim-using-the-rest-api.md @@ -105,11 +105,8 @@ To ensure that your environment has a single source of truth, you should only pr However, you can safely retrieve information from {% data variables.product.company_short %}'s APIs with `GET` requests in scripts or ad hoc requests by an enterprise owner. -{% warning %} - -**Warning:** If you use a partner IdP for SCIM provisioning, the application on the IdP must be the only system that makes write requests to the API. If you make ad hoc requests using the `POST`, `PUT`, `PATCH`, or `DELETE` methods, subsequent synchronization attempts will fail, and provisioning won't function properly for your enterprise. - -{% endwarning %} +> [!WARNING] +> If you use a partner IdP for SCIM provisioning, the application on the IdP must be the only system that makes write requests to the API. If you make ad hoc requests using the `POST`, `PUT`, `PATCH`, or `DELETE` methods, subsequent synchronization attempts will fail, and provisioning won't function properly for your enterprise. ### Send valid requests to REST API endpoints diff --git a/content/admin/managing-iam/provisioning-user-accounts-with-scim/user-provisioning-with-scim-on-ghes.md b/content/admin/managing-iam/provisioning-user-accounts-with-scim/user-provisioning-with-scim-on-ghes.md index 607656e02089..e104a2421052 100644 --- a/content/admin/managing-iam/provisioning-user-accounts-with-scim/user-provisioning-with-scim-on-ghes.md +++ b/content/admin/managing-iam/provisioning-user-accounts-with-scim/user-provisioning-with-scim-on-ghes.md @@ -139,16 +139,12 @@ After you enable SCIM on a {% data variables.product.product_name %} instance, a 1. Sign into your instance as the new enterprise owner. 1. Create a {% data variables.product.pat_v1 %} with **admin:enterprise** scope. Do not specify an expiration date for the {% data variables.product.pat_v1 %}. For more information, see "[AUTOTITLE](/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token)." - {% warning %} + > [!WARNING] + > Ensure that you don't specify an expiration date for the {% data variables.product.pat_v1 %}. If you specify an expiration date, SCIM will no longer function after the expiration date passes. - **Warning**: Ensure that you don't specify an expiration date for the {% data variables.product.pat_v1 %}. If you specify an expiration date, SCIM will no longer function after the expiration date passes. + > [!NOTE] + > You'll need this {% data variables.product.pat_generic %} to test the SCIM configuration, and to configure the application for SCIM on your IdP. Store the token securely in a password manager until you need the token again later in these instructions. - {% endwarning %} - {% note %} - - **Note**: You'll need this {% data variables.product.pat_generic %} to test the SCIM configuration, and to configure the application for SCIM on your IdP. Store the token securely in a password manager until you need the token again later in these instructions. - - {% endnote %} {% data reusables.enterprise_installation.ssh-into-instance %} 1. To enable SCIM, run the commands provided to you by your account manager on {% data variables.contact.contact_enterprise_sales %}. {% data reusables.enterprise_site_admin_settings.wait-for-configuration-run %} diff --git a/content/admin/managing-iam/reconfiguring-iam-for-enterprise-managed-users/migrating-from-oidc-to-saml.md b/content/admin/managing-iam/reconfiguring-iam-for-enterprise-managed-users/migrating-from-oidc-to-saml.md index 33fb699a65b1..729b18136ee1 100644 --- a/content/admin/managing-iam/reconfiguring-iam-for-enterprise-managed-users/migrating-from-oidc-to-saml.md +++ b/content/admin/managing-iam/reconfiguring-iam-for-enterprise-managed-users/migrating-from-oidc-to-saml.md @@ -25,11 +25,8 @@ Then, you will configure SAML and SCIM. At this time, users, groups, and identit If you're new to {% data variables.product.prodname_emus %} and haven't yet configured authentication for your enterprise, you do not need to migrate and can set up SAML single sign-on (SSO) immediately. For more information, see "[AUTOTITLE](/admin/identity-and-access-management/using-enterprise-managed-users-for-iam/configuring-saml-single-sign-on-for-enterprise-managed-users)." -{% warning %} - -**Warning:** {% data reusables.enterprise_user_management.migration-teams-warning %} - -{% endwarning %} +> [!WARNING] +> {% data reusables.enterprise_user_management.migration-teams-warning %} ## Prerequisites diff --git a/content/admin/managing-iam/reconfiguring-iam-for-enterprise-managed-users/migrating-from-saml-to-oidc.md b/content/admin/managing-iam/reconfiguring-iam-for-enterprise-managed-users/migrating-from-saml-to-oidc.md index 3774c7fa28a8..dff53f06fe60 100644 --- a/content/admin/managing-iam/reconfiguring-iam-for-enterprise-managed-users/migrating-from-saml-to-oidc.md +++ b/content/admin/managing-iam/reconfiguring-iam-for-enterprise-managed-users/migrating-from-saml-to-oidc.md @@ -27,11 +27,8 @@ When you migrate from SAML to OIDC, {% data variables.enterprise.prodname_manage If you're new to {% data variables.product.prodname_emus %} and haven't yet configured authentication for your enterprise, you do not need to migrate and can set up OIDC single sign-on immediately. For more information, see "[AUTOTITLE](/admin/identity-and-access-management/using-enterprise-managed-users-for-iam/configuring-oidc-for-enterprise-managed-users)." -{% warning %} - -**Warning:** {% data reusables.enterprise_user_management.migration-teams-warning %} - -{% endwarning %} +> [!WARNING] +> {% data reusables.enterprise_user_management.migration-teams-warning %} ## Prerequisites @@ -43,11 +40,8 @@ If you're new to {% data variables.product.prodname_emus %} and haven't yet conf To migrate your enterprise from SAML to OIDC, you will disable your existing {% data variables.product.prodname_emu_idp_application %} application on Entra ID, prepare and begin the migration as the setup user for your enterprise on {% data variables.product.github %}, then install and configure the new application for OIDC on Entra ID. After the migration is complete and Entra ID provisions your users, the users can authenticate to access your enterprise's resources on {% data variables.product.github %} using OIDC. -{% warning %} - -**Warning**: Migration of your enterprise from SAML to OIDC can take up to an hour. During the migration, users cannot access your enterprise on {% data variables.product.github %}. - -{% endwarning %} +> [!WARNING] +> Migration of your enterprise from SAML to OIDC can take up to an hour. During the migration, users cannot access your enterprise on {% data variables.product.github %}. 1. Before you begin the migration, sign in to Azure and disable provisioning in the existing {% data variables.product.prodname_emu_idp_application %} application. 1. If you use [Conditional Access (CA) network location policies](https://docs.microsoft.com/en-us/azure/active-directory/conditional-access/location-condition) in Entra ID, and you're currently using an IP allow list with your enterprise account or any of the organizations owned by the enterprise account, disable the IP allow lists. For more information, see "[AUTOTITLE](/admin/policies/enforcing-policies-for-your-enterprise/enforcing-policies-for-security-settings-in-your-enterprise#managing-allowed-ip-addresses-for-organizations-in-your-enterprise)" and "[AUTOTITLE](/organizations/keeping-your-organization-secure/managing-security-settings-for-your-organization/managing-allowed-ip-addresses-for-your-organization)." @@ -62,11 +56,9 @@ To migrate your enterprise from SAML to OIDC, you will disable your existing {% 1. After you grant consent, a new browser window will open to {% data variables.product.github %} and display a new set of recovery codes for your {% data variables.enterprise.prodname_emu_enterprise %}. Download the codes, then click **Enable OIDC authentication**. 1. Wait for the migration to complete, which can take up to an hour. To check the status of the migration, navigate to your enterprise's authentication security settings page. If "Require SAML authentication" is selected, the migration is still in progress. - {% warning %} - - **Warning:** Do not provision new users from the application on Entra ID during the migration. + > [!WARNING] + > Do not provision new users from the application on Entra ID during the migration. - {% endwarning %} 1. In a new tab or window, while signed in as the setup user, create a {% data variables.product.pat_v1 %} with the **scim:enterprise** scope and **no expiration** and copy it to your clipboard. For more information about creating a new token, see "[AUTOTITLE](/admin/identity-and-access-management/using-enterprise-managed-users-for-iam/configuring-scim-provisioning-for-enterprise-managed-users#creating-a-personal-access-token)." 1. In the provisioning settings for the {% data variables.product.prodname_emu_idp_oidc_application %} application in the Microsoft Entra admin center, under "Tenant URL", type the tenant URL for your enterprise: diff --git a/content/admin/managing-iam/understanding-iam-for-enterprises/allowing-built-in-authentication-for-users-outside-your-provider.md b/content/admin/managing-iam/understanding-iam-for-enterprises/allowing-built-in-authentication-for-users-outside-your-provider.md index ecbe9a9e227d..d7a98ee7668c 100644 --- a/content/admin/managing-iam/understanding-iam-for-enterprises/allowing-built-in-authentication-for-users-outside-your-provider.md +++ b/content/admin/managing-iam/understanding-iam-for-enterprises/allowing-built-in-authentication-for-users-outside-your-provider.md @@ -28,11 +28,8 @@ If you're unable to add specific accounts to your external authentication provid If you configure built-in authentication and a person successfully authenticates with SAML or CAS, the person will no longer have the option to authenticate with a username and password. If a user successfully authenticates with LDAP, the credentials are no longer considered internal. -{% warning %} - -**Warning:** If you disable built-in authentication, you must individually suspend any users that should no longer have access to the instance. For more information, see "[AUTOTITLE](/admin/user-management/managing-users-in-your-enterprise/suspending-and-unsuspending-users)." - -{% endwarning %} +> [!WARNING] +> If you disable built-in authentication, you must individually suspend any users that should no longer have access to the instance. For more information, see "[AUTOTITLE](/admin/user-management/managing-users-in-your-enterprise/suspending-and-unsuspending-users)." ## Configuring built-in authentication for users outside your provider diff --git a/content/admin/managing-iam/using-ldap-for-enterprise-iam/using-ldap.md b/content/admin/managing-iam/using-ldap-for-enterprise-iam/using-ldap.md index 682db400deb0..27b1238e34dc 100644 --- a/content/admin/managing-iam/using-ldap-for-enterprise-iam/using-ldap.md +++ b/content/admin/managing-iam/using-ldap-for-enterprise-iam/using-ldap.md @@ -54,11 +54,8 @@ After you configure LDAP, users will be able to sign into your instance with the When you configure LDAP access for users via the {% data variables.enterprise.management_console %}, your user licenses aren't used until the first time a user signs in to your instance. However, if you create an account manually using site admin settings, the user license is immediately accounted for. -{% warning %} - -**Warning:** Before configuring LDAP on {% data variables.location.product_location %}, make sure that your LDAP service supports paged results. - -{% endwarning %} +> [!WARNING] +> Before configuring LDAP on {% data variables.location.product_location %}, make sure that your LDAP service supports paged results. {% data reusables.enterprise_site_admin_settings.access-settings %} {% data reusables.enterprise_site_admin_settings.management-console %} @@ -117,15 +114,12 @@ You can establish role-based access control for users from your LDAP server by s LDAP sync does not create user accounts on {% data variables.location.product_location %}. For more information, see "[Viewing and creating LDAP users](#viewing-and-creating-ldap-users)." -{% note %} - -**Note:** Using LDAP Synchronization with groups that exceed 1499 members may lead to team membership synchronization failures. - -If you use Active Directory specifically, user lookups and team synchronization may fail when the LDAP groups configured for teams or in the {% data variables.enterprise.management_console %} exceed 1500 members, due to the `MaxValRange` limit in Active Directory. As a workaround, you can use Active Directory groups that contain less than 1500 members, or you can work with your Active Directory administrator to increase the `MaxValRange` value for your domain controllers. For more information, see [View and set LDAP policy in Active Directory by using Ntdsutil.exe](https://learn.microsoft.com/en-US/troubleshoot/windows-server/identity/view-set-ldap-policy-using-ntdsutil) in Microsoft Learn. - -If you need help determining if modifying the `MaxValRange` is the right approach for your Active Directory environment, contact Microsoft Support. - -{% endnote %} +> [!NOTE] +> Using LDAP Synchronization with groups that exceed 1499 members may lead to team membership synchronization failures. +> +> If you use Active Directory specifically, user lookups and team synchronization may fail when the LDAP groups configured for teams or in the {% data variables.enterprise.management_console %} exceed 1500 members, due to the `MaxValRange` limit in Active Directory. As a workaround, you can use Active Directory groups that contain less than 1500 members, or you can work with your Active Directory administrator to increase the `MaxValRange` value for your domain controllers. For more information, see [View and set LDAP policy in Active Directory by using Ntdsutil.exe](https://learn.microsoft.com/en-US/troubleshoot/windows-server/identity/view-set-ldap-policy-using-ntdsutil) in Microsoft Learn. +> +> If you need help determining if modifying the `MaxValRange` is the right approach for your Active Directory environment, contact Microsoft Support. To enable LDAP Sync, in your LDAP settings, select **Synchronization**. @@ -149,22 +143,17 @@ After you enable LDAP sync, a synchronization job will run at the specified time * If an LDAP User field is defined for SSH public keys, synchronize the user's public SSH keys with the LDAP entry. * If an LDAP User field is defined for GPG keys, synchronize the user's GPG keys with the LDAP entry. -{% note %} - -**Note**: LDAP entries can only be marked as disabled if you use Active Directory and the `userAccountControl` attribute is present and flagged with `ACCOUNTDISABLE`. Some variations of Active Directory, such as AD LDS and ADAM, don't support the `userAccountControl` attribute. - -{% endnote %} +> [!NOTE] +> LDAP entries can only be marked as disabled if you use Active Directory and the `userAccountControl` attribute is present and flagged with `ACCOUNTDISABLE`. Some variations of Active Directory, such as AD LDS and ADAM, don't support the `userAccountControl` attribute. A synchronization job will also run at the specified time interval to perform the following operations on each team that has been mapped to an LDAP group: * If a team's corresponding LDAP group has been removed, remove all members from the team. * If LDAP member entries have been removed from the LDAP group, remove the corresponding users from the team. If the user is no longer a member of any team in the organization and is not an owner of the organization, remove the user from the organization. If the user loses access to any repositories as a result, delete any private forks the user has of those repositories. - {% note %} - - **Note:** LDAP Sync will not remove a user from an organization if the user is an owner of that organization. Another organization owner will need to manually remove the user instead. + > [!NOTE] + > LDAP Sync will not remove a user from an organization if the user is an owner of that organization. Another organization owner will need to manually remove the user instead. - {% endnote %} * If LDAP member entries have been added to the LDAP group, add the corresponding users to the team. If the user regains access to any repositories as a result, restore any private forks of the repositories that were deleted because the user lost access in the past 90 days. {% data reusables.enterprise_user_management.ldap-sync-nested-teams %} diff --git a/content/admin/managing-iam/using-saml-for-enterprise-iam/configuring-saml-single-sign-on-for-your-enterprise.md b/content/admin/managing-iam/using-saml-for-enterprise-iam/configuring-saml-single-sign-on-for-your-enterprise.md index d16acfa7072f..91eafe780cc9 100644 --- a/content/admin/managing-iam/using-saml-for-enterprise-iam/configuring-saml-single-sign-on-for-your-enterprise.md +++ b/content/admin/managing-iam/using-saml-for-enterprise-iam/configuring-saml-single-sign-on-for-your-enterprise.md @@ -111,11 +111,8 @@ For more detailed information about how to enable SAML using Okta, see "[AUTOTIT You can enable or disable SAML authentication for {% data variables.location.product_location %}, or you can edit an existing configuration. You can view and edit authentication settings for {% data variables.product.product_name %} in the {% data variables.enterprise.management_console %}. For more information, see "[AUTOTITLE](/admin/configuration/administering-your-instance-from-the-management-console)." -{% note %} - -**Note**: {% data reusables.enterprise.test-in-staging %} - -{% endnote %} +> [!NOTE] +> {% data reusables.enterprise.test-in-staging %} {% data reusables.enterprise_site_admin_settings.access-settings %} {% data reusables.enterprise_site_admin_settings.management-console %} @@ -124,11 +121,8 @@ You can enable or disable SAML authentication for {% data variables.location.pro 1. {% data reusables.enterprise_user_management.built-in-authentication-option %} 1. Optionally, to enable unsolicited response SSO, select **IdP initiated SSO**. By default, {% data variables.product.prodname_ghe_server %} will reply to an unsolicited Identity Provider (IdP) initiated request with an `AuthnRequest` back to the IdP. - {% tip %} - - **Note**: We recommend keeping this value **unselected**. You should enable this feature **only** in the rare instance that your SAML implementation does not support service provider initiated SSO, and when advised by {% data variables.contact.enterprise_support %}. - - {% endtip %} + > [!TIP] + > We recommend keeping this value **unselected**. You should enable this feature **only** in the rare instance that your SAML implementation does not support service provider initiated SSO, and when advised by {% data variables.contact.enterprise_support %}. 1. Optionally, if you do not want your SAML provider to determine administrator rights for users on {% data variables.location.product_location %}, select **Disable administrator demotion/promotion** {%- ifversion ghes %} diff --git a/content/admin/managing-iam/using-saml-for-enterprise-iam/disabling-saml-single-sign-on-for-your-enterprise.md b/content/admin/managing-iam/using-saml-for-enterprise-iam/disabling-saml-single-sign-on-for-your-enterprise.md index aa4287543f5a..896b7ee89047 100644 --- a/content/admin/managing-iam/using-saml-for-enterprise-iam/disabling-saml-single-sign-on-for-your-enterprise.md +++ b/content/admin/managing-iam/using-saml-for-enterprise-iam/disabling-saml-single-sign-on-for-your-enterprise.md @@ -23,11 +23,8 @@ After you disable SAML SSO for your enterprise, the following effects apply: {% data reusables.enterprise-accounts.access-enterprise %} - {% note %} - - **Note:** If you're unable to access the enterprise because your IdP is unavailable, you can use a recovery code to bypass SSO. For more information, see "[AUTOTITLE](/admin/identity-and-access-management/managing-recovery-codes-for-your-enterprise/accessing-your-enterprise-account-if-your-identity-provider-is-unavailable)." - - {% endnote %} + > [!NOTE] + > If you're unable to access the enterprise because your IdP is unavailable, you can use a recovery code to bypass SSO. For more information, see "[AUTOTITLE](/admin/identity-and-access-management/managing-recovery-codes-for-your-enterprise/accessing-your-enterprise-account-if-your-identity-provider-is-unavailable)." {% data reusables.enterprise-accounts.settings-tab %} {% data reusables.enterprise-accounts.security-tab %} diff --git a/content/admin/managing-iam/using-saml-for-enterprise-iam/enabling-encrypted-assertions.md b/content/admin/managing-iam/using-saml-for-enterprise-iam/enabling-encrypted-assertions.md index abddbb4fe25c..6c70faf5f692 100644 --- a/content/admin/managing-iam/using-saml-for-enterprise-iam/enabling-encrypted-assertions.md +++ b/content/admin/managing-iam/using-saml-for-enterprise-iam/enabling-encrypted-assertions.md @@ -29,11 +29,8 @@ To enable encrypted assertions for authentication to {% data variables.product.p To enable encrypted assertions, you must provide {% data variables.location.product_location %}'s public certificate to your IdP, and configure encryption settings that match your IdP. -{% note %} - -**Note**: {% data reusables.enterprise.test-in-staging %} - -{% endnote %} +> [!NOTE] +> {% data reusables.enterprise.test-in-staging %} 1. Optionally, enable SAML debugging. SAML debugging records verbose entries in {% data variables.product.product_name %}'s authentication log, and may help you troubleshoot failed authentication attempts. For more information, see "[AUTOTITLE](/admin/identity-and-access-management/using-saml-for-enterprise-iam/troubleshooting-saml-authentication#configuring-saml-debugging)." {% data reusables.enterprise_site_admin_settings.access-settings %} diff --git a/content/admin/managing-iam/using-saml-for-enterprise-iam/troubleshooting-saml-authentication.md b/content/admin/managing-iam/using-saml-for-enterprise-iam/troubleshooting-saml-authentication.md index d4b39fb35442..744d83d1a918 100644 --- a/content/admin/managing-iam/using-saml-for-enterprise-iam/troubleshooting-saml-authentication.md +++ b/content/admin/managing-iam/using-saml-for-enterprise-iam/troubleshooting-saml-authentication.md @@ -30,14 +30,9 @@ For more information about SAML response requirements, see "[AUTOTITLE](/admin/i You can configure {% data variables.product.product_name %} to write verbose debug logs for every SAML authentication attempt. You may be able to troubleshoot failed authentication attempts with this extra output. -{% warning %} - -**Warnings**: - -* Only enable SAML debugging temporarily, and disable debugging immediately after you finish troubleshooting. If you leave debugging enabled, the size of the {% ifversion opentelemetry-and-otel-log-migration-phase-1 %}logs{% endif %} increases much faster than usual, which can negatively impact the performance of {% data variables.product.product_name %}. -* Test new authentication settings for {% data variables.location.product_location %} in a staging environment before you apply the settings in your production environment. For more information, see "[AUTOTITLE](/admin/installation/setting-up-a-github-enterprise-server-instance/setting-up-a-staging-instance)." - -{% endwarning %} +> [!WARNING] +> * Only enable SAML debugging temporarily, and disable debugging immediately after you finish troubleshooting. If you leave debugging enabled, the size of the {% ifversion opentelemetry-and-otel-log-migration-phase-1 %}logs{% endif %} increases much faster than usual, which can negatively impact the performance of {% data variables.product.product_name %}. +> * Test new authentication settings for {% data variables.location.product_location %} in a staging environment before you apply the settings in your production environment. For more information, see "[AUTOTITLE](/admin/installation/setting-up-a-github-enterprise-server-instance/setting-up-a-staging-instance)." {% data reusables.enterprise-accounts.access-enterprise %} {% data reusables.enterprise-accounts.policies-tab %} diff --git a/content/admin/managing-your-enterprise-account/changing-the-url-for-your-enterprise.md b/content/admin/managing-your-enterprise-account/changing-the-url-for-your-enterprise.md index 1ca94de3f765..a704f81a7e4c 100644 --- a/content/admin/managing-your-enterprise-account/changing-the-url-for-your-enterprise.md +++ b/content/admin/managing-your-enterprise-account/changing-the-url-for-your-enterprise.md @@ -17,11 +17,8 @@ When you create an enterprise, you choose a "slug" for the enterprise, which is If your company pays for {% data variables.product.prodname_ghe_cloud %} by credit card or PayPal, you can change the slug in the settings for your enterprise. When you change the slug, {% data variables.product.company_short %} does not set up any redirects from the old URL. Your old enterprise slug will immediately become available for another customer to use. -{% note %} - -**Note:** If you pay for {% data variables.product.prodname_ghe_cloud %} via invoice, or if your enterprise uses {% data variables.product.prodname_emus %}, you must contact {% data variables.contact.contact_enterprise_sales %} to change your enterprise slug. - -{% endnote %} +> [!NOTE] +> If you pay for {% data variables.product.prodname_ghe_cloud %} via invoice, or if your enterprise uses {% data variables.product.prodname_emus %}, you must contact {% data variables.contact.contact_enterprise_sales %} to change your enterprise slug. ## Considerations when changing your enterprise slug @@ -49,11 +46,8 @@ If your enterprise is linked to one or more {% data variables.product.prodname_g ## Changing the enterprise slug -{% note %} - -**Note:** Before changing the slug for an enterprise, make sure you have understood the potential consequences. For more information, see "[Considerations when changing your enterprise slug](#considerations-when-changing-your-enterprise-slug)." - -{% endnote %} +> [!NOTE] +> Before changing the slug for an enterprise, make sure you have understood the potential consequences. For more information, see "[Considerations when changing your enterprise slug](#considerations-when-changing-your-enterprise-slug)." {% data reusables.enterprise-accounts.access-enterprise %} {% data reusables.enterprise-accounts.settings-tab %} diff --git a/content/admin/monitoring-activity-in-your-enterprise/exploring-user-activity-in-your-enterprise/accessing-reports-for-your-instance.md b/content/admin/monitoring-activity-in-your-enterprise/exploring-user-activity-in-your-enterprise/accessing-reports-for-your-instance.md index d0fcc3a80de6..e3fc9165dfae 100644 --- a/content/admin/monitoring-activity-in-your-enterprise/exploring-user-activity-in-your-enterprise/accessing-reports-for-your-instance.md +++ b/content/admin/monitoring-activity-in-your-enterprise/exploring-user-activity-in-your-enterprise/accessing-reports-for-your-instance.md @@ -45,11 +45,8 @@ curl --remote-name \ To access the other reports programmatically, replace `all_users` with `active_users`, `dormant_users`, `suspended_users`, `all_organizations`, or `all_repositories`. -{% note %} - -**Note:** The initial curl request will return an HTTP `202` response if there are no cached reports available. Your instance will generate a report in the background. You can send a second request to download the report. You can use a password or an OAuth token with the `site_admin` scope in place of a password. - -{% endnote %} +> [!NOTE] +> The initial curl request will return an HTTP `202` response if there are no cached reports available. Your instance will generate a report in the background. You can send a second request to download the report. You can use a password or an OAuth token with the `site_admin` scope in place of a password. ## User reports diff --git a/content/admin/monitoring-activity-in-your-enterprise/reviewing-audit-logs-for-your-enterprise/about-the-audit-log-for-your-enterprise.md b/content/admin/monitoring-activity-in-your-enterprise/reviewing-audit-logs-for-your-enterprise/about-the-audit-log-for-your-enterprise.md index 0fdba7b642fd..d34fc1e21c75 100644 --- a/content/admin/monitoring-activity-in-your-enterprise/reviewing-audit-logs-for-your-enterprise/about-the-audit-log-for-your-enterprise.md +++ b/content/admin/monitoring-activity-in-your-enterprise/reviewing-audit-logs-for-your-enterprise/about-the-audit-log-for-your-enterprise.md @@ -24,11 +24,8 @@ topics: ## About audit logs -{% note %} - -**Note:** {% data reusables.webhooks.webhooks-as-audit-log-alternative %} - -{% endnote %} +> [!NOTE] +> {% data reusables.webhooks.webhooks-as-audit-log-alternative %} {% data reusables.audit_log.retention-periods %} diff --git a/content/admin/monitoring-activity-in-your-enterprise/reviewing-audit-logs-for-your-enterprise/configuring-the-audit-log-for-your-enterprise.md b/content/admin/monitoring-activity-in-your-enterprise/reviewing-audit-logs-for-your-enterprise/configuring-the-audit-log-for-your-enterprise.md index 6728c7038214..7b27b34729ab 100644 --- a/content/admin/monitoring-activity-in-your-enterprise/reviewing-audit-logs-for-your-enterprise/configuring-the-audit-log-for-your-enterprise.md +++ b/content/admin/monitoring-activity-in-your-enterprise/reviewing-audit-logs-for-your-enterprise/configuring-the-audit-log-for-your-enterprise.md @@ -51,11 +51,8 @@ Before you can enable Git events in the audit log, you must configure a retentio {% data reusables.audit_log.audit-data-retention-tab %} 1. Under "Git event opt-in", select or deselect **Enable git events in the audit-log**. - {% note %} - - **Note:** The retention policy must be set to something other than infinite for this option to display. - - {% endnote %} + > [!NOTE] + > The retention policy must be set to something other than infinite for this option to display. ![Screenshot of the audit log. The checkbox to enable Git events in the audit log is highlighted with an orange outline.](/assets/images/help/enterprises/enable-git-events-checkbox.png) 1. Click **Save**. diff --git a/content/admin/monitoring-activity-in-your-enterprise/reviewing-audit-logs-for-your-enterprise/searching-the-audit-log-for-your-enterprise.md b/content/admin/monitoring-activity-in-your-enterprise/reviewing-audit-logs-for-your-enterprise/searching-the-audit-log-for-your-enterprise.md index 81f1b9075991..b80e2dfa4147 100644 --- a/content/admin/monitoring-activity-in-your-enterprise/reviewing-audit-logs-for-your-enterprise/searching-the-audit-log-for-your-enterprise.md +++ b/content/admin/monitoring-activity-in-your-enterprise/reviewing-audit-logs-for-your-enterprise/searching-the-audit-log-for-your-enterprise.md @@ -31,11 +31,8 @@ You can also use the API to retrieve audit log events. For more information, see You cannot search for entries using text. You can, however, construct search queries using a variety of filters. Many operators used when querying the log, such as `-`, `>`, or `<`, match the same format as searching across {% data variables.product.product_name %}. For more information, see "[AUTOTITLE](/search-github/getting-started-with-searching-on-github/about-searching-on-github)." -{% note %} - -**Note**: {% data reusables.audit_log.retention-periods %} - -{% endnote %} +> [!NOTE] +> {% data reusables.audit_log.retention-periods %} ## Search query filters diff --git a/content/admin/monitoring-and-managing-your-instance/caching-repositories/configuring-a-repository-cache.md b/content/admin/monitoring-and-managing-your-instance/caching-repositories/configuring-a-repository-cache.md index 156cb7a92c7c..785c220277be 100644 --- a/content/admin/monitoring-and-managing-your-instance/caching-repositories/configuring-a-repository-cache.md +++ b/content/admin/monitoring-and-managing-your-instance/caching-repositories/configuring-a-repository-cache.md @@ -82,11 +82,8 @@ You can control data locality by configuring data location policies for your rep Data location policies affect only Git content. Content in the database, such as issues and pull request comments, will be replicated to all nodes regardless of policy. -{% note %} - -**Note:** Data location policies are not the same as access control. You must use repository roles to control which users may access a repository. For more information about repository roles, see "[AUTOTITLE](/organizations/managing-user-access-to-your-organizations-repositories/managing-repository-roles/repository-roles-for-an-organization)." - -{% endnote %} +> [!NOTE] +> Data location policies are not the same as access control. You must use repository roles to control which users may access a repository. For more information about repository roles, see "[AUTOTITLE](/organizations/managing-user-access-to-your-organizations-repositories/managing-repository-roles/repository-roles-for-an-organization)." You can configure a policy to replicate all networks with the `--default` flag. For example, this command will create a policy to replicate a single copy of every repository network to the set of repository caches whose `cache_location` is "kansas". diff --git a/content/admin/monitoring-and-managing-your-instance/configuring-clustering/about-cluster-nodes.md b/content/admin/monitoring-and-managing-your-instance/configuring-clustering/about-cluster-nodes.md index 778587c400d9..f5b022842c9c 100644 --- a/content/admin/monitoring-and-managing-your-instance/configuring-clustering/about-cluster-nodes.md +++ b/content/admin/monitoring-and-managing-your-instance/configuring-clustering/about-cluster-nodes.md @@ -39,11 +39,8 @@ Each node must have a root volume, as well as a separate data volume. These are For adequate redundancy, use these minimum nodes operating each service. -{% tip %} - -**Note:** Your environment's scaling requirements depend on many factors, including the size and number of repositories, number of users, and overall utilization. - -{% endtip %} +> [!NOTE] +> Your environment's scaling requirements depend on many factors, including the size and number of repositories, number of users, and overall utilization. ## Example cluster configuration diff --git a/content/admin/monitoring-and-managing-your-instance/configuring-clustering/configuring-high-availability-replication-for-a-cluster.md b/content/admin/monitoring-and-managing-your-instance/configuring-clustering/configuring-high-availability-replication-for-a-cluster.md index 8a2e5d0b766b..9bd2a60328a1 100644 --- a/content/admin/monitoring-and-managing-your-instance/configuring-clustering/configuring-high-availability-replication-for-a-cluster.md +++ b/content/admin/monitoring-and-managing-your-instance/configuring-clustering/configuring-high-availability-replication-for-a-cluster.md @@ -32,11 +32,8 @@ For each existing node in your active cluster, you'll need to provision a second On each new virtual machine, install the same version of {% data variables.product.prodname_ghe_server %} that runs on the nodes in your active cluster. You don't need to upload a license or perform any additional configuration. For more information, see "[AUTOTITLE](/enterprise/admin/installation/setting-up-a-github-enterprise-server-instance)." -{% note %} - -**Note**: The nodes that you intend to use for high availability replication should be standalone {% data variables.product.prodname_ghe_server %} instances. Don't initialize the replica nodes as a second cluster. - -{% endnote %} +> [!NOTE] +> The nodes that you intend to use for high availability replication should be standalone {% data variables.product.prodname_ghe_server %} instances. Don't initialize the replica nodes as a second cluster. ### Network @@ -53,15 +50,10 @@ To create a high availability replica for your cluster, use the `ghe-cluster-rep {% data reusables.enterprise_clustering.ssh-to-a-node %} 1. To begin configuration of high availability, run the following command. The `-p` and `-s` flags are optional. If you're using the flags, replace PRIMARY-DATACENTER and SECONDARY-DATACENTER with the names of your primary and secondary datacenters. - {% note %} - - **Notes:** - - * By default, the utility will use the name of the primary datacenter in `cluster.conf`. - * If no name for the primary datacenter is defined, the utility will use `mona`. - * If no name for the secondary datacenter is defined, the utility will use `hubot`. - - {% endnote %} + > [!NOTE] + > * By default, the utility will use the name of the primary datacenter in `cluster.conf`. + > * If no name for the primary datacenter is defined, the utility will use `mona`. + > * If no name for the secondary datacenter is defined, the utility will use `hubot`. ```shell copy ghe-cluster-repl-bootstrap -p PRIMARY-DATACENTER -s SECONDARY-DATACENTER @@ -111,15 +103,12 @@ Before you define a secondary datacenter for your replica nodes, ensure that you ... ``` - {% note %} - - **Note**: If you changed the name of the primary datacenter in step 3, find the `consul-datacenter` key-value pair in the section for each node and change the value to the renamed primary datacenter. For example, if you named the primary datacenter `primary`, use the following key-value pair for each node. - - ```text - consul-datacenter = primary - ``` - - {% endnote %} + > [!NOTE] + > If you changed the name of the primary datacenter in step 3, find the `consul-datacenter` key-value pair in the section for each node and change the value to the renamed primary datacenter. For example, if you named the primary datacenter `primary`, use the following key-value pair for each node. + > + > ```text + > consul-datacenter = primary + > ``` {% data reusables.enterprise_clustering.apply-configuration %} {% data reusables.enterprise_clustering.configuration-finished %} @@ -139,11 +128,8 @@ For an example configuration, see "[Review an example configuration](#3-review-a 1. For each node in your cluster, provision a matching virtual machine with identical specifications, running the same version of {% data variables.product.prodname_ghe_server %}. Note the IPv4 address and hostname for each new cluster node. For more information, see "[Prerequisites](#prerequisites)." - {% note %} - - **Note**: If you're reconfiguring high availability after a failover, you can use the old nodes from the primary datacenter instead. - - {% endnote %} + > [!NOTE] + > If you're reconfiguring high availability after a failover, you can use the old nodes from the primary datacenter instead. {% data reusables.enterprise_clustering.ssh-to-a-node %} 1. Back up your existing cluster configuration. @@ -172,11 +158,8 @@ For an example configuration, see "[Review an example configuration](#3-review-a 1. Decide on a pattern for the replica nodes' hostnames. - {% warning %} - - **Warning**: Hostnames for replica nodes must be unique and differ from the hostname for the corresponding active node. - - {% endwarning %} + > [!WARNING] + > Hostnames for replica nodes must be unique and differ from the hostname for the corresponding active node. 1. Open the temporary cluster configuration file from step 3 in a text editor. For example, you can use Vim. @@ -213,25 +196,22 @@ For an example configuration, see "[Review an example configuration](#3-review-a git config -f /data/user/common/cluster.conf cluster.redis-master-replica REPLICA-REDIS-PRIMARY-HOSTNAME ``` - {% warning %} - - **Warning**: Review your cluster configuration file before proceeding. - - * In the top-level `[cluster]` section, ensure that the values for `mysql-master-replica` and `redis-master-replica` are the correct hostnames for the replica nodes in the secondary datacenter that will serve as the MySQL and Redis primaries after a failover. - * In each section for an active node named [cluster "ACTIVE NODE HOSTNAME"], double-check the following key-value pairs. - * `datacenter` should match the value of `primary-datacenter` in the top-level `[cluster]` section. - * `consul-datacenter` should match the value of `datacenter`, which should be the same as the value for `primary-datacenter` in the top-level `[cluster]` section. - * Ensure that for each active node, the configuration has **one** corresponding section for **one** replica node with the same roles. In each section for a replica node, double-check each key-value pair. - * `datacenter` should match all other replica nodes. - * `consul-datacenter` should match all other replica nodes. - * `hostname` should match the hostname in the section heading. - * `ipv4` should match the node's unique, static IPv4 address. - * `replica` should be configured as `enabled`. - * Take the opportunity to remove sections for offline nodes that are no longer in use. - - To review an example configuration, see "[Review an example configuration](#3-review-an-example-configuration)." - - {% endwarning %} + > [!WARNING] + > Review your cluster configuration file before proceeding. + > + > * In the top-level `[cluster]` section, ensure that the values for `mysql-master-replica` and `redis-master-replica` are the correct hostnames for the replica nodes in the secondary datacenter that will serve as the MySQL and Redis primaries after a failover. + > * In each section for an active node named [cluster "ACTIVE NODE HOSTNAME"], double-check the following key-value pairs. + > * `datacenter` should match the value of `primary-datacenter` in the top-level `[cluster]` section. + > * `consul-datacenter` should match the value of `datacenter`, which should be the same as the value for `primary-datacenter` in the top-level `[cluster]` section. + > * Ensure that for each active node, the configuration has **one** corresponding section for **one** replica node with the same roles. In each section for a replica node, double-check each key-value pair. + > * `datacenter` should match all other replica nodes. + > * `consul-datacenter` should match all other replica nodes. + > * `hostname` should match the hostname in the section heading. + > * `ipv4` should match the node's unique, static IPv4 address. + > * `replica` should be configured as `enabled`. + > * Take the opportunity to remove sections for offline nodes that are no longer in use. + > + > To review an example configuration, see "[Review an example configuration](#3-review-an-example-configuration)." 1. Initialize the new cluster configuration. {% data reusables.enterprise.use-a-multiplexer %} diff --git a/content/admin/monitoring-and-managing-your-instance/configuring-clustering/evacuating-a-cluster-node-running-data-services.md b/content/admin/monitoring-and-managing-your-instance/configuring-clustering/evacuating-a-cluster-node-running-data-services.md index b164054bf3da..92e4e55c94ec 100644 --- a/content/admin/monitoring-and-managing-your-instance/configuring-clustering/evacuating-a-cluster-node-running-data-services.md +++ b/content/admin/monitoring-and-managing-your-instance/configuring-clustering/evacuating-a-cluster-node-running-data-services.md @@ -24,15 +24,9 @@ In a cluster configuration for {% data variables.product.product_name %}, you ma For more information about nodes and service tiers for {% data variables.product.prodname_ghe_server %}, see "[AUTOTITLE](/admin/enterprise-management/configuring-clustering/about-cluster-nodes)." -{% warning %} - -**Warnings**: - -* To avoid data loss during replacement of a node, {% data variables.product.company_short %} strongly recommends evacuation of the applicable data services on the node before you take the node offline. - -* To ensure redundancy for any data service on your cluster, copies of data should exist on at least three nodes. For example, when four or more nodes store Git data, during evacuation, evacuated repository data will move from the node you're evacuating to the other three nodes. If you only have three nodes that store data for a service, evacuation of one node could fail and result in under-replicated data. - -{% endwarning %} +> [!WARNING] +> * To avoid data loss during replacement of a node, {% data variables.product.company_short %} strongly recommends evacuation of the applicable data services on the node before you take the node offline. +> * To ensure redundancy for any data service on your cluster, copies of data should exist on at least three nodes. For example, when four or more nodes store Git data, during evacuation, evacuated repository data will move from the node you're evacuating to the other three nodes. If you only have three nodes that store data for a service, evacuation of one node could fail and result in under-replicated data. ## Evacuating a cluster node running data services @@ -134,11 +128,8 @@ If you plan to take a node offline and the node runs any of the following roles, 1. To monitor evacuation of a service while {% data variables.product.product_name %} copies the data, run the following commands. For each command, replace UUID with the UUID from the earlier step. - {% warning %} - - **Warning**: Do not shut down the node until evacuation is complete. Evacuation is complete when the data counts reach zero, which means that all data is safely stored on other nodes. - - {% endwarning %} + > [!WARNING] + > Do not shut down the node until evacuation is complete. Evacuation is complete when the data counts reach zero, which means that all data is safely stored on other nodes. * `git-server`: diff --git a/content/admin/monitoring-and-managing-your-instance/configuring-clustering/monitoring-the-health-of-your-cluster.md b/content/admin/monitoring-and-managing-your-instance/configuring-clustering/monitoring-the-health-of-your-cluster.md index 7cca2dc02956..72e629d24785 100644 --- a/content/admin/monitoring-and-managing-your-instance/configuring-clustering/monitoring-the-health-of-your-cluster.md +++ b/content/admin/monitoring-and-managing-your-instance/configuring-clustering/monitoring-the-health-of-your-cluster.md @@ -42,11 +42,8 @@ admin@ghe-data-node-0:~$ ghe-cluster-status | grep error > mysql cluster: error ``` -{% note %} - -**Note:** If there are no failing tests, this command produces no output. This indicates the cluster is healthy. - -{% endnote %} +> [!NOTE] +> If there are no failing tests, this command produces no output. This indicates the cluster is healthy. {% ifversion ghes-manage-api-cli-extension %} @@ -84,15 +81,14 @@ You can configure [Nagios](https://www.nagios.org/) to monitor {% data variables **Security Warning:** An SSH key without a passphrase can pose a security risk if authorized for full access to a host. Limit this key's authorization to a single read-only command. {% enddanger %} - {% note %} - - **Note:** If you're using a distribution of Linux that doesn't support the Ed25519 algorithm, use the command: - ```shell - nagiosuser@nagios:~$ ssh-keygen -t rsa -b 4096 - ``` + > [!NOTE] + > If you're using a distribution of Linux that doesn't support the Ed25519 algorithm, use the command: + > + > ```shell + > nagiosuser@nagios:~$ ssh-keygen -t rsa -b 4096 + > ``` - {% endnote %} 1. Copy the private key (`id_ed25519`) to the `nagios` home folder and set the appropriate ownership. ```shell diff --git a/content/admin/monitoring-and-managing-your-instance/configuring-clustering/rebalancing-cluster-workloads.md b/content/admin/monitoring-and-managing-your-instance/configuring-clustering/rebalancing-cluster-workloads.md index 890dbe0dd1ac..f3ec38874342 100644 --- a/content/admin/monitoring-and-managing-your-instance/configuring-clustering/rebalancing-cluster-workloads.md +++ b/content/admin/monitoring-and-managing-your-instance/configuring-clustering/rebalancing-cluster-workloads.md @@ -64,11 +64,8 @@ After you determine which jobs are unbalanced across your cluster's nodes, you c You can schedule rebalancing of jobs on your cluster by setting and applying configuration values for {% data variables.location.product_location %}. -{% note %} - -**Note:** Currently, you can only schedule reallocation of jobs for the HTTP server, `github-unicorn`. - -{% endnote %} +> [!NOTE] +> Currently, you can only schedule reallocation of jobs for the HTTP server, `github-unicorn`. 1. To configure automatic, hourly balancing of jobs, run the following command. diff --git a/content/admin/monitoring-and-managing-your-instance/configuring-clustering/replacing-a-cluster-node.md b/content/admin/monitoring-and-managing-your-instance/configuring-clustering/replacing-a-cluster-node.md index 8d2629274b12..af3019ccc21b 100644 --- a/content/admin/monitoring-and-managing-your-instance/configuring-clustering/replacing-a-cluster-node.md +++ b/content/admin/monitoring-and-managing-your-instance/configuring-clustering/replacing-a-cluster-node.md @@ -25,11 +25,8 @@ You can replace a functional node in a {% data variables.product.product_name %} After you replace a node, {% data variables.location.product_location %} does not automatically distribute jobs to the new node. You can force your instance to balance jobs across nodes. For more information, see "[AUTOTITLE](/admin/enterprise-management/configuring-clustering/rebalancing-cluster-workloads)." {% endif %} -{% warning %} - -**Warning:** To avoid conflicts, do not reuse a hostname that was previously assigned to a node in the cluster. - -{% endwarning %} +> [!WARNING] +> To avoid conflicts, do not reuse a hostname that was previously assigned to a node in the cluster. ## Replacing a functional node @@ -37,11 +34,8 @@ You can replace an existing, functional node in your cluster. For example, you m To replace a functional node, install the {% data variables.product.product_name %} appliance on a new VM, configure an IP address, add the new node to the cluster configuration file, initialize the cluster and apply the configuration, then take the node you replaced offline. -{% note %} - -**Note:** If you're replacing the primary MySQL node, see "[Replacing the primary MySQL node](#replacing-the-primary-mysql-node)." - -{% endnote %} +> [!NOTE] +> If you're replacing the primary MySQL node, see "[Replacing the primary MySQL node](#replacing-the-primary-mysql-node)." {% data reusables.enterprise_clustering.replacing-a-cluster-node-provision %} {% data reusables.enterprise_clustering.replacing-a-cluster-node-admin-configure-ip %} @@ -66,11 +60,8 @@ To replace a functional node, install the {% data variables.product.product_name You can replace a failed node in your cluster. For example, a software or hardware issue may affect a node's availability. -{% note %} - -**Note:** If you're replacing the primary MySQL node, see "[Replacing the primary MySQL node](#replacing-the-primary-mysql-node)." - -{% endnote %} +> [!NOTE] +> If you're replacing the primary MySQL node, see "[Replacing the primary MySQL node](#replacing-the-primary-mysql-node)." {% ifversion cluster-node-removal %} @@ -116,11 +107,8 @@ To replace a node in an emergency, you'll take the failed node offline, add your These commands indicate to each service that the node is permanently removed. The services will recreate any replicas contained within the node on the available nodes within the cluster. - {% note %} - - **Note:** These commands may cause increased load on the server while data is rebalanced across replicas. - - {% endnote %} + > [!NOTE] + > These commands may cause increased load on the server while data is rebalanced across replicas. For the `git-server` service (used for repository data): @@ -202,11 +190,9 @@ If you want to provide the VM for your primary MySQL node with more resources, o 1. During your scheduled maintenance window, enable maintenance mode. For more information, see "[AUTOTITLE](/admin/administering-your-instance/configuring-maintenance-mode/enabling-and-scheduling-maintenance-mode#enabling-or-disabling-maintenance-mode-for-all-nodes-in-a-cluster-via-the-cli)." 1. Ensure that MySQL replication is finished from any node in the cluster by running `ghe-cluster-status -v`. - {% warning %} - - **Warning**: If you do not wait for MySQL replication to finish, you risk data loss on your instance. + > [!WARNING] + > If you do not wait for MySQL replication to finish, you risk data loss on your instance. - {% endwarning %} 1. To set the current MySQL primary node to read-only mode, run the following command from of the instance's nodes. ```shell copy diff --git a/content/admin/monitoring-and-managing-your-instance/configuring-high-availability/creating-a-high-availability-replica.md b/content/admin/monitoring-and-managing-your-instance/configuring-high-availability/creating-a-high-availability-replica.md index 8ff5839c1581..ab6c35f56def 100644 --- a/content/admin/monitoring-and-managing-your-instance/configuring-high-availability/creating-a-high-availability-replica.md +++ b/content/admin/monitoring-and-managing-your-instance/configuring-high-availability/creating-a-high-availability-replica.md @@ -81,11 +81,9 @@ This example configuration uses a primary and two replicas, which are located in (replica2)$ ghe-repl-node --datacenter [SECOND REPLICA DC NAME] ``` - {% tip %} + > [!TIP] + > You can set the `--datacenter` and `--active` options at the same time. - **Tip:** You can set the `--datacenter` and `--active` options at the same time. - - {% endtip %} 1. An active replica node will store copies of the appliance data and service end user requests. An inactive node will store copies of the appliance data but will be unable to service end user requests. Enable active mode using the `--active` flag or inactive mode using the `--inactive` flag. On the first replica: diff --git a/content/admin/monitoring-and-managing-your-instance/configuring-high-availability/initiating-a-failover-to-your-replica-appliance.md b/content/admin/monitoring-and-managing-your-instance/configuring-high-availability/initiating-a-failover-to-your-replica-appliance.md index fba58ea767df..982d75f0f689 100644 --- a/content/admin/monitoring-and-managing-your-instance/configuring-high-availability/initiating-a-failover-to-your-replica-appliance.md +++ b/content/admin/monitoring-and-managing-your-instance/configuring-high-availability/initiating-a-failover-to-your-replica-appliance.md @@ -34,11 +34,8 @@ The time required to failover depends on how long it takes to manually promote t * When the number of active Git operations, MySQL queries, and Resque jobs reaches zero, wait 30 seconds. - {% note %} - - **Note:** Nomad will always have jobs running, even in maintenance mode, so you can safely ignore these jobs. - - {% endnote %} + > [!NOTE] + > Nomad will always have jobs running, even in maintenance mode, so you can safely ignore these jobs. * To verify all replication channels report `OK`, use the `ghe-repl-status -vv` command. @@ -54,11 +51,8 @@ The time required to failover depends on how long it takes to manually promote t ghe-repl-promote ``` - {% note %} - - **Note:** If the primary node is unavailable, warnings and timeouts may occur but can be ignored. - - {% endnote %} + > [!NOTE] + > If the primary node is unavailable, warnings and timeouts may occur but can be ignored. 1. Update the DNS record to point to the IP address of the replica. Traffic is directed to the replica after the TTL period elapses. If you are using a load balancer, ensure it is configured to send traffic to the replica. 1. Notify users that they can resume normal operations. diff --git a/content/admin/monitoring-and-managing-your-instance/configuring-high-availability/recovering-a-high-availability-configuration.md b/content/admin/monitoring-and-managing-your-instance/configuring-high-availability/recovering-a-high-availability-configuration.md index 9b75ac3fde00..a4a447922253 100644 --- a/content/admin/monitoring-and-managing-your-instance/configuring-high-availability/recovering-a-high-availability-configuration.md +++ b/content/admin/monitoring-and-managing-your-instance/configuring-high-availability/recovering-a-high-availability-configuration.md @@ -21,11 +21,8 @@ shortTitle: Recover a HA configuration You can use the former primary appliance as the new replica appliance if the failover was planned or was not related to the health of the appliance. If the failover was related to an issue with the primary appliance, you may prefer to create a new replica appliance. For more information, see "[AUTOTITLE](/admin/enterprise-management/configuring-high-availability/creating-a-high-availability-replica)." -{% warning %} - -**Warning:** You must enable maintenance mode before configuring a former primary appliance as a new replica. If you do not enable maintenance mode, you will cause a production outage. - -{% endwarning %} +> [!WARNING] +> You must enable maintenance mode before configuring a former primary appliance as a new replica. If you do not enable maintenance mode, you will cause a production outage. ## Configuring a former primary appliance as a new replica diff --git a/content/admin/monitoring-and-managing-your-instance/configuring-high-availability/removing-a-high-availability-replica.md b/content/admin/monitoring-and-managing-your-instance/configuring-high-availability/removing-a-high-availability-replica.md index ebf8865d267a..8a0bb14c6a57 100644 --- a/content/admin/monitoring-and-managing-your-instance/configuring-high-availability/removing-a-high-availability-replica.md +++ b/content/admin/monitoring-and-managing-your-instance/configuring-high-availability/removing-a-high-availability-replica.md @@ -48,9 +48,8 @@ shortTitle: Remove a HA replica ``` {% ifversion ghes %} - {% note %} - **Note:** If you have {% data variables.product.prodname_actions %} enabled, you should decommission the former replica server or update its {% data variables.product.prodname_actions %} configuration to use different external storage. For more information, see "[AUTOTITLE](/admin/github-actions/advanced-configuration-and-troubleshooting/high-availability-for-github-actions#high-availability-replicas)." + > [!NOTE] + > If you have {% data variables.product.prodname_actions %} enabled, you should decommission the former replica server or update its {% data variables.product.prodname_actions %} configuration to use different external storage. For more information, see "[AUTOTITLE](/admin/github-actions/advanced-configuration-and-troubleshooting/high-availability-for-github-actions#high-availability-replicas)." - {% endnote %} {% endif %} diff --git a/content/admin/monitoring-and-managing-your-instance/monitoring-your-instance/about-system-logs.md b/content/admin/monitoring-and-managing-your-instance/monitoring-your-instance/about-system-logs.md index 577298599ebc..ac843bb1987f 100644 --- a/content/admin/monitoring-and-managing-your-instance/monitoring-your-instance/about-system-logs.md +++ b/content/admin/monitoring-and-managing-your-instance/monitoring-your-instance/about-system-logs.md @@ -30,11 +30,8 @@ You can forward system logs and audit logs to an external system for analysis or In addition to reviewing your system logs, you can monitor activity on your instance in other ways. For example, you can review audit logs and push logs, or configure global webhooks. For more information, see "[AUTOTITLE](/admin/monitoring-activity-in-your-enterprise)." -{% note %} - -**Note**: The following lists of logs are not intended to be comprehensive. - -{% endnote %} +> [!NOTE] +> The following lists of logs are not intended to be comprehensive. ## System log files diff --git a/content/admin/monitoring-and-managing-your-instance/monitoring-your-instance/generating-a-health-check-for-your-enterprise.md b/content/admin/monitoring-and-managing-your-instance/monitoring-your-instance/generating-a-health-check-for-your-enterprise.md index e70fb7841602..c86924ded6b0 100644 --- a/content/admin/monitoring-and-managing-your-instance/monitoring-your-instance/generating-a-health-check-for-your-enterprise.md +++ b/content/admin/monitoring-and-managing-your-instance/monitoring-your-instance/generating-a-health-check-for-your-enterprise.md @@ -17,11 +17,8 @@ redirect_from: - /admin/monitoring-managing-and-updating-your-instance/monitoring-your-instance/generating-a-health-check-for-your-enterprise --- -{% note %} - -**Note:** Generating a Health Check is currently in {% data variables.release-phases.public_preview %} for {% data variables.product.prodname_ghe_server %} and subject to change. - -{% endnote %} +> [!NOTE] +> Generating a Health Check is currently in {% data variables.release-phases.public_preview %} for {% data variables.product.prodname_ghe_server %} and subject to change. ## About generated Health Checks diff --git a/content/admin/monitoring-and-managing-your-instance/monitoring-your-instance/monitoring-using-snmp.md b/content/admin/monitoring-and-managing-your-instance/monitoring-your-instance/monitoring-using-snmp.md index 5545195c792a..d4f550bd3ae0 100644 --- a/content/admin/monitoring-and-managing-your-instance/monitoring-your-instance/monitoring-using-snmp.md +++ b/content/admin/monitoring-and-managing-your-instance/monitoring-your-instance/monitoring-using-snmp.md @@ -113,10 +113,7 @@ With SNMP v2c, to query for `hrMemorySize`, run the following command on a separ snmpget -v 2c -c COMMUNITY-STRING HOSTNAME HOST-RESOURCES-MIB::hrMemorySize.0 ``` -{% tip %} - -**Note:** To prevent leaking information about services running on your appliance, we exclude the `hrSWRun` table (1.3.6.1.2.1.25.4) from the resulting SNMP reports unless you're using the `authPriv` security level with SNMP v3. If you're using the `authPriv` security level, we include the `hrSWRun` table. - -{% endtip %} +> [!NOTE] +> To prevent leaking information about services running on your appliance, we exclude the `hrSWRun` table (1.3.6.1.2.1.25.4) from the resulting SNMP reports unless you're using the `authPriv` security level with SNMP v3. If you're using the `authPriv` security level, we include the `hrSWRun` table. For more information on OID mappings for common system attributes in SNMP, see "[Linux SNMP OID’s for CPU, Memory and Disk Statistics](http://www.linux-admins.net/2012/02/linux-snmp-oids-for-cpumemory-and-disk.html)". diff --git a/content/admin/monitoring-and-managing-your-instance/monitoring-your-instance/troubleshooting-resource-allocation-problems.md b/content/admin/monitoring-and-managing-your-instance/monitoring-your-instance/troubleshooting-resource-allocation-problems.md index a1dd3611a2e5..d6a5a48e8fe5 100644 --- a/content/admin/monitoring-and-managing-your-instance/monitoring-your-instance/troubleshooting-resource-allocation-problems.md +++ b/content/admin/monitoring-and-managing-your-instance/monitoring-your-instance/troubleshooting-resource-allocation-problems.md @@ -18,15 +18,12 @@ shortTitle: Troubleshooting resource allocation problems ## Troubleshooting common resource allocation problems on your appliance -{% note %} - -**Note**: Regularly making repeated requests (polling) to {% data variables.location.product_location %} from continuous integration (CI) systems, build servers, or any other clients (such as Git or API clients) can overwhelm the system. This can lead to a denial of service (DoS) attack, causing significant performance issues and resource saturation. - -To avoid these problems, we strongly recommend using webhooks to receive updates. Webhooks allow the system to push updates to you automatically, eliminating the need for constant polling. Additionally, consider using conditional requests and caching strategies to minimize unnecessary requests. Avoid running jobs in large, simultaneous batches (thundering herds) and instead wait for webhook events to trigger actions. - -For more information, see "[AUTOTITLE](/get-started/exploring-integrations/about-webhooks)." - -{% endnote %} +> [!NOTE] +> Regularly making repeated requests (polling) to {% data variables.location.product_location %} from continuous integration (CI) systems, build servers, or any other clients (such as Git or API clients) can overwhelm the system. This can lead to a denial of service (DoS) attack, causing significant performance issues and resource saturation. +> +> To avoid these problems, we strongly recommend using webhooks to receive updates. Webhooks allow the system to push updates to you automatically, eliminating the need for constant polling. Additionally, consider using conditional requests and caching strategies to minimize unnecessary requests. Avoid running jobs in large, simultaneous batches (thundering herds) and instead wait for webhook events to trigger actions. +> +> For more information, see "[AUTOTITLE](/get-started/exploring-integrations/about-webhooks)." We recommend using the monitor dashboard to stay informed on your appliance's resource health and make decisions on how to fix high usage issues, such as the ones outlined on this page. diff --git a/content/admin/monitoring-and-managing-your-instance/updating-the-virtual-machine-and-physical-resources/increasing-storage-capacity.md b/content/admin/monitoring-and-managing-your-instance/updating-the-virtual-machine-and-physical-resources/increasing-storage-capacity.md index 593f665dd1f0..887911d05730 100644 --- a/content/admin/monitoring-and-managing-your-instance/updating-the-virtual-machine-and-physical-resources/increasing-storage-capacity.md +++ b/content/admin/monitoring-and-managing-your-instance/updating-the-virtual-machine-and-physical-resources/increasing-storage-capacity.md @@ -23,11 +23,8 @@ As more users join {% data variables.location.product_location %}, you may need ## Requirements and recommendations -{% note %} - -**Note:** Before resizing any storage volume, put your instance in maintenance mode.{% ifversion ip-exception-list %} You can validate changes by configuring an IP exception list to allow access from specified IP addresses. {% endif %} For more information, see "[AUTOTITLE](/admin/configuration/configuring-your-enterprise/enabling-and-scheduling-maintenance-mode)." - -{% endnote %} +> [!NOTE] +> Before resizing any storage volume, put your instance in maintenance mode.{% ifversion ip-exception-list %} You can validate changes by configuring an IP exception list to allow access from specified IP addresses. {% endif %} For more information, see "[AUTOTITLE](/admin/configuration/configuring-your-enterprise/enabling-and-scheduling-maintenance-mode)." ### Minimum recommended requirements @@ -68,11 +65,8 @@ Root storage refers to the total size of your instance's root disk. The availabl ## Increasing the root partition size using an existing appliance -{% warning %} - -**Warning:** Before increasing the root partition size, you must put your instance in maintenance mode. For more information, see "[AUTOTITLE](/admin/configuration/configuring-your-enterprise/enabling-and-scheduling-maintenance-mode)." - -{% endwarning %} +> [!WARNING] +> Before increasing the root partition size, you must put your instance in maintenance mode. For more information, see "[AUTOTITLE](/admin/configuration/configuring-your-enterprise/enabling-and-scheduling-maintenance-mode)." 1. Attach a new disk to your {% data variables.product.prodname_ghe_server %} appliance. 1. Run the `lsblk` command to identify the new disk's device name. diff --git a/content/admin/overview/setting-up-a-trial-of-github-enterprise-server.md b/content/admin/overview/setting-up-a-trial-of-github-enterprise-server.md index 829100343466..4da5f204d758 100644 --- a/content/admin/overview/setting-up-a-trial-of-github-enterprise-server.md +++ b/content/admin/overview/setting-up-a-trial-of-github-enterprise-server.md @@ -49,11 +49,8 @@ To get the most out of your trial, follow these steps: * Add users to your {% data variables.product.prodname_ghe_server %} instance using built-in authentication or your configured identity provider. For more information, see "[AUTOTITLE](/enterprise-server@latest/admin/identity-and-access-management/using-built-in-authentication/configuring-built-in-authentication)." * To invite people to become account administrators, visit the [{% data variables.product.prodname_enterprise %} Web portal](https://enterprise.github.com/login). - {% note %} - - **Note:** People you invite to become account administrators will receive an email with a link to accept your invitation. - - {% endnote %} + > [!NOTE] + > People you invite to become account administrators will receive an email with a link to accept your invitation. {% data reusables.enterprise.best-practices %} diff --git a/content/admin/overview/system-overview.md b/content/admin/overview/system-overview.md index b22bc21ff407..2b210afb4435 100644 --- a/content/admin/overview/system-overview.md +++ b/content/admin/overview/system-overview.md @@ -55,11 +55,8 @@ By default, {% data variables.product.product_name %} runs as a standalone insta ## Data retention and datacenter redundancy -{% warning %} - -**Warning**: Before using {% data variables.product.product_name %} in a production environment, we strongly recommend you set up backups and a disaster recovery plan. - -{% endwarning %} +> [!WARNING] +> Before using {% data variables.product.product_name %} in a production environment, we strongly recommend you set up backups and a disaster recovery plan. {% data variables.product.product_name %} includes support for online and incremental backups with {% data variables.product.prodname_enterprise_backup_utilities %}. You can take incremental snapshots over a secure network link (the SSH administrative port) over long distances for off-site or geographically dispersed storage. You can restore snapshots over the network into a newly provisioned instance at time of recovery in case of disaster at the primary datacenter. diff --git a/content/admin/upgrading-your-instance/performing-an-upgrade/migrating-from-github-enterprise-1110x-to-2123.md b/content/admin/upgrading-your-instance/performing-an-upgrade/migrating-from-github-enterprise-1110x-to-2123.md index 0c91198fe3b5..f5b9b0dd7c1c 100644 --- a/content/admin/upgrading-your-instance/performing-an-upgrade/migrating-from-github-enterprise-1110x-to-2123.md +++ b/content/admin/upgrading-your-instance/performing-an-upgrade/migrating-from-github-enterprise-1110x-to-2123.md @@ -23,11 +23,9 @@ topics: - Upgrades shortTitle: Migrate from 11.10.x to 2.1.23 --- -{% note %} -**Note**: {% data variables.product.prodname_ghe_server %} 11.10 is an unsupported release from 2014. For a list of supported releases, see "[AUTOTITLE](/admin/all-releases)." - -{% endnote %} +> [!NOTE] +> {% data variables.product.prodname_ghe_server %} 11.10 is an unsupported release from 2014. For a list of supported releases, see "[AUTOTITLE](/admin/all-releases)." Migrations from {% data variables.product.prodname_enterprise %} 11.10.348 and later are supported. Migrating from {% data variables.product.prodname_enterprise %} 11.10.348 and earlier is not supported. You must first upgrade to 11.10.348 in several upgrades. For more information, see the 11.10.348 upgrading procedure, "[Upgrading to the latest release](/enterprise/11.10.340/admin/articles/upgrading-to-the-latest-release/)." @@ -42,11 +40,8 @@ To upgrade to the latest version of {% data variables.product.prodname_enterpris * If you are not currently running scheduled backups, set up {% data variables.product.prodname_enterprise_backup_utilities %}. 1. Take an initial full backup snapshot of the current instance using the `ghe-backup` command. If you have already configured scheduled backups for your current instance, you don't need to take a snapshot of your instance. - {% tip %} - - **Tip:** You can leave the instance online and in active use during the snapshot. You'll take another snapshot during the maintenance portion of the migration. Since backups are incremental, this initial snapshot reduces the amount of data transferred in the final snapshot, which may shorten the maintenance window. - - {% endtip %} + > [!TIP] + > You can leave the instance online and in active use during the snapshot. You'll take another snapshot during the maintenance portion of the migration. Since backups are incremental, this initial snapshot reduces the amount of data transferred in the final snapshot, which may shorten the maintenance window. 1. Determine the method for switching user network traffic to the new instance. After you've migrated, all HTTP and Git network traffic directs to the new instance. * **DNS** - We recommend this method for all environments, as it's simple and works well even when migrating from one datacenter to another. Before starting migration, reduce the existing DNS record's TTL to five minutes or less and allow the change to propagate. Once the migration is complete, update the DNS record(s) to point to the IP address of the new instance. @@ -64,11 +59,8 @@ To upgrade to the latest version of {% data variables.product.prodname_enterpris 1. Copy the `ghe-restore` command that you'll run on the backup host to migrate data to the new instance. 1. Enable maintenance mode on the old instance and wait for all active processes to complete. For more information, see "[AUTOTITLE](/admin/configuration/configuring-your-enterprise/enabling-and-scheduling-maintenance-mode)." - {% note %} - - **Note:** The instance will be unavailable for normal use from this point forward. - - {% endnote %} + > [!NOTE] + > The instance will be unavailable for normal use from this point forward. 1. On the backup host, run the `ghe-backup` command to take a final backup snapshot. This ensures that all data from the old instance is captured. 1. On the backup host, run the `ghe-restore` command you copied on the new instance's restore status screen to restore the latest snapshot. @@ -97,11 +89,8 @@ To upgrade to the latest version of {% data variables.product.prodname_enterpris 1. Click **Continue to settings** to review and adjust the configuration information and settings that were imported from the previous instance. 1. Click **Save settings**. - {% note %} - - **Note:** You can use the new instance after you've applied configuration settings and restarted the server. - - {% endnote %} + > [!NOTE] + > You can use the new instance after you've applied configuration settings and restarted the server. 1. Switch user network traffic from the old instance to the new instance using either DNS or IP address assignment. 1. Upgrade to the latest patch release of {% data variables.product.prodname_ghe_server %}. For more information, see "[AUTOTITLE](/admin/upgrading-your-instance/preparing-to-upgrade/overview-of-the-upgrade-process)." diff --git a/content/admin/upgrading-your-instance/preparing-to-upgrade/enabling-automatic-update-checks.md b/content/admin/upgrading-your-instance/preparing-to-upgrade/enabling-automatic-update-checks.md index 6ae4bf91d3b3..9e8cc9d3bbc4 100644 --- a/content/admin/upgrading-your-instance/preparing-to-upgrade/enabling-automatic-update-checks.md +++ b/content/admin/upgrading-your-instance/preparing-to-upgrade/enabling-automatic-update-checks.md @@ -25,11 +25,8 @@ If a hotpatch is available for an upgrade, the `.hpkg` will download automatical ## Enabling automatic update checks -{% tip %} - -**Tip:** To enable automatic update checks, {% data variables.location.product_location %} must be able to connect to `https://github-enterprise.s3.amazonaws.com`. - -{% endtip %} +> [!TIP] +> To enable automatic update checks, {% data variables.location.product_location %} must be able to connect to `https://github-enterprise.s3.amazonaws.com`. {% data reusables.enterprise_site_admin_settings.access-settings %} {% data reusables.enterprise_site_admin_settings.management-console %} diff --git a/content/admin/upgrading-your-instance/preparing-to-upgrade/upgrade-requirements.md b/content/admin/upgrading-your-instance/preparing-to-upgrade/upgrade-requirements.md index 52125afeddff..82d6864c244e 100644 --- a/content/admin/upgrading-your-instance/preparing-to-upgrade/upgrade-requirements.md +++ b/content/admin/upgrading-your-instance/preparing-to-upgrade/upgrade-requirements.md @@ -17,14 +17,11 @@ topics: - Enterprise - Upgrades --- -{% note %} -**Notes:** -* Upgrade packages are available at [enterprise.github.com](https://enterprise.github.com/releases) for supported versions. Verify the availability of the upgrade packages you will need to complete the upgrade. If a package is not available, visit {% data variables.contact.contact_ent_support %} and contact us for assistance. -* If you're using {% data variables.product.prodname_ghe_server %} Clustering, see "[AUTOTITLE](/admin/enterprise-management/configuring-clustering/upgrading-a-cluster)" in the {% data variables.product.prodname_ghe_server %} Clustering Guide for specific instructions unique to clustering. -* The release notes for {% data variables.product.prodname_ghe_server %} provide a comprehensive list of new features for every version of {% data variables.product.prodname_ghe_server %}. For more information, see the [releases page](https://enterprise.github.com/releases). - -{% endnote %} +> [!NOTE] +> * Upgrade packages are available at [enterprise.github.com](https://enterprise.github.com/releases) for supported versions. Verify the availability of the upgrade packages you will need to complete the upgrade. If a package is not available, visit {% data variables.contact.contact_ent_support %} and contact us for assistance. +> * If you're using {% data variables.product.prodname_ghe_server %} Clustering, see "[AUTOTITLE](/admin/enterprise-management/configuring-clustering/upgrading-a-cluster)" in the {% data variables.product.prodname_ghe_server %} Clustering Guide for specific instructions unique to clustering. +> * The release notes for {% data variables.product.prodname_ghe_server %} provide a comprehensive list of new features for every version of {% data variables.product.prodname_ghe_server %}. For more information, see the [releases page](https://enterprise.github.com/releases). ## Recommendations diff --git a/content/admin/upgrading-your-instance/troubleshooting-upgrades/known-issues-with-upgrades-to-your-instance.md b/content/admin/upgrading-your-instance/troubleshooting-upgrades/known-issues-with-upgrades-to-your-instance.md index 8cfe99cbbbb9..9da154474ed8 100644 --- a/content/admin/upgrading-your-instance/troubleshooting-upgrades/known-issues-with-upgrades-to-your-instance.md +++ b/content/admin/upgrading-your-instance/troubleshooting-upgrades/known-issues-with-upgrades-to-your-instance.md @@ -33,11 +33,8 @@ To future-proof {% data variables.product.prodname_ghe_server %} and provide the After the upgrade to {% data variables.product.prodname_ghe_server %} 3.9, if you experience unacceptable degradation in the performance of your instance, you can collect data from your instance's monitor dashboard to confirm the impact. You can attempt to mitigate the issue, and you can provide the data to {% data variables.contact.github_support %} to help profile and communicate the real-world impact of this change. -{% warning %} - -**Warning**: Due to the nature of this upgrade, back up your instance's configuration and data before proceeding. Validate the backup in a staging environment. For more information, see "[AUTOTITLE](/admin/configuration/configuring-your-enterprise/configuring-backups-on-your-appliance)" and "[AUTOTITLE](/admin/installation/setting-up-a-github-enterprise-server-instance/setting-up-a-staging-instance)." - -{% endwarning %} +> [!WARNING] +> Due to the nature of this upgrade, back up your instance's configuration and data before proceeding. Validate the backup in a staging environment. For more information, see "[AUTOTITLE](/admin/configuration/configuring-your-enterprise/configuring-backups-on-your-appliance)" and "[AUTOTITLE](/admin/installation/setting-up-a-github-enterprise-server-instance/setting-up-a-staging-instance)." ### Collecting baseline I/O utilization data before the MySQL upgrade @@ -77,14 +74,11 @@ To attempt to mitigate the performance impact, you can adjust InnoDB's flushing The following instructions are only intended for {% data variables.product.product_name %} 3.9 and later. -{% warning %} - -**Warning**: Adjustment of the flushing method requires that your instance's storage device has a battery-backed cache. If the device's cache is not battery-backed, you risk data loss. - -* If you host your instance using a virtualization hypervisor within an on-premises datacenter, review your storage specifications to confirm. -* If you host your instance in a public cloud service, consult your provider's documentation or support team to confirm. - -{% endwarning %} +> [!WARNING] +> Adjustment of the flushing method requires that your instance's storage device has a battery-backed cache. If the device's cache is not battery-backed, you risk data loss. +> +> * If you host your instance using a virtualization hypervisor within an on-premises datacenter, review your storage specifications to confirm. +> * If you host your instance in a public cloud service, consult your provider's documentation or support team to confirm. {% data reusables.enterprise_installation.ssh-into-instance %} 1. To validate the current flushing method for InnoDB, run the following command. diff --git a/content/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-as-a-github-app.md b/content/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-as-a-github-app.md index c690b83dc197..164ee9c83686 100644 --- a/content/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-as-a-github-app.md +++ b/content/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-as-a-github-app.md @@ -33,11 +33,8 @@ If a REST API endpoint requires you to authenticate as an app, the documentation You can use {% data variables.product.company_short %}'s Octokit.js SDK to authenticate as a {% data variables.product.prodname_github_app %}. One advantage of using the SDK to authenticate is that you do not need to generate a JSON web token (JWT) yourself. Additionally, the SDK will take care of regenerating the JWT when it expires. -{% note %} - -**Note**: You must install and import `octokit` in order to use the Octokit.js library. The following example uses import statements in accordance with ES6. For more information about different installation and import methods, see [Usage](https://github.com/octokit/octokit.js/#usage) in the octokit/octokit repository. - -{% endnote %} +> [!NOTE] +> You must install and import `octokit` in order to use the Octokit.js library. The following example uses import statements in accordance with ES6. For more information about different installation and import methods, see [Usage](https://github.com/octokit/octokit.js/#usage) in the octokit/octokit repository. 1. Get the ID of your app. You can find your app's ID on the settings page for your {% data variables.product.prodname_github_app %}. For more information about navigating to the settings page for your {% data variables.product.prodname_github_app %}, see "[AUTOTITLE](/apps/maintaining-github-apps/modifying-a-github-app-registration#navigating-to-your-github-app-settings)." 1. Generate a private key. For more information, see "[AUTOTITLE](/apps/creating-github-apps/authenticating-with-a-github-app/managing-private-keys-for-github-apps)." diff --git a/content/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-with-a-github-app-on-behalf-of-a-user.md b/content/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-with-a-github-app-on-behalf-of-a-user.md index 3b1f6859dff2..a9585fbd3733 100644 --- a/content/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-with-a-github-app-on-behalf-of-a-user.md +++ b/content/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-with-a-github-app-on-behalf-of-a-user.md @@ -35,8 +35,5 @@ Requests made with a user access token are sometimes called "user-to-server" req If you want to attribute app activity to the app instead of to a user, you should authenticate as an app installation instead. For more information, see "[AUTOTITLE](/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-as-a-github-app-installation)." -{% note %} - -**Note**: {% data reusables.apps.github_app_auth_saml %} - -{% endnote %} +> [!NOTE] +> {% data reusables.apps.github_app_auth_saml %} diff --git a/content/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-json-web-token-jwt-for-a-github-app.md b/content/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-json-web-token-jwt-for-a-github-app.md index 09318dcb36fd..2d9a1888bceb 100644 --- a/content/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-json-web-token-jwt-for-a-github-app.md +++ b/content/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-json-web-token-jwt-for-a-github-app.md @@ -39,19 +39,13 @@ curl --request GET \ Most programming languages have a package that can generate a JWT. In all cases, you must have a private key and the ID of your {% data variables.product.prodname_github_app %}. For more information about generating a private key, see "[AUTOTITLE](/apps/creating-github-apps/authenticating-with-a-github-app/managing-private-keys-for-github-apps)". You can find your app's ID with the `GET /app` REST API endpoint. For more information, see "[Apps](/rest/apps/apps)" in the REST API documentation. -{% note %} - -Note: Instead of creating a JWT, you can use {% data variables.product.company_short %}'s Octokit SDKs to authenticate as an app. The SDK will take care of generating a JWT for you and will regenerate the JWT once the token expires. For more information, see "[Scripting with the REST API and JavaScript](/rest/guides/scripting-with-the-rest-api-and-javascript#authenticating-with-a-github-app)." - -{% endnote %} +> [!NOTE] +> Instead of creating a JWT, you can use {% data variables.product.company_short %}'s Octokit SDKs to authenticate as an app. The SDK will take care of generating a JWT for you and will regenerate the JWT once the token expires. For more information, see "[Scripting with the REST API and JavaScript](/rest/guides/scripting-with-the-rest-api-and-javascript#authenticating-with-a-github-app)." ### Example: Using Ruby to generate a JWT -{% note %} - -**Note:** You must run `gem install jwt` to install the `jwt` package in order to use this script. - -{% endnote %} +> [!NOTE] +> You must run `gem install jwt` to install the `jwt` package in order to use this script. In the following example, replace `YOUR_PATH_TO_PEM` with the file path where your private key is stored. Replace `YOUR_APP_ID` with the ID of your app. Make sure to enclose the values for `YOUR_PATH_TO_PEM` and `YOUR_APP_ID` in double quotes. @@ -82,11 +76,8 @@ puts jwt ### Example: Using Python to generate a JWT -{% note %} - -**Note:** You must run `pip install PyJWT` to install the `PyJWT` package in order to use this script. - -{% endnote %} +> [!NOTE] +> You must run `pip install PyJWT` to install the `PyJWT` package in order to use this script. ```python copy #!/usr/bin/env python3 @@ -142,11 +133,8 @@ This script will prompt you for the file path where your private key is stored a ### Example: Using Bash to generate a JWT -{% note %} - -**Note:** You must pass your {% ifversion client-id-for-app %}Client ID{% else %}App ID{% endif %} and the file path where your private key is stored as arguments when running this script. - -{% endnote %} +> [!NOTE] +> You must pass your {% ifversion client-id-for-app %}Client ID{% else %}App ID{% endif %} and the file path where your private key is stored as arguments when running this script. ```bash copy #!/usr/bin/env bash diff --git a/content/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app.md b/content/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app.md index df2800978425..7002d6ae6683 100644 --- a/content/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app.md +++ b/content/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app.md @@ -58,11 +58,8 @@ If your app runs in the browser, you should use the web application flow to gene ## Using the device flow to generate a user access token -{% note %} - -**Note:** The device flow is in {% data variables.release-phases.public_preview %} and subject to change. - -{% endnote %} +> [!NOTE] +> The device flow is in {% data variables.release-phases.public_preview %} and subject to change. If your app is headless or does not have access to a browser, you should use the device flow to generate a user access token. For example, CLI tools, simple Raspberry Pis, and desktop applications should use the device flow. For a tutorial that uses device flow, see "[AUTOTITLE](/apps/creating-github-apps/guides/building-a-cli-with-a-github-app)." diff --git a/content/apps/creating-github-apps/authenticating-with-a-github-app/generating-an-installation-access-token-for-a-github-app.md b/content/apps/creating-github-apps/authenticating-with-a-github-app/generating-an-installation-access-token-for-a-github-app.md index bf1e8df51d21..2339fab3086b 100644 --- a/content/apps/creating-github-apps/authenticating-with-a-github-app/generating-an-installation-access-token-for-a-github-app.md +++ b/content/apps/creating-github-apps/authenticating-with-a-github-app/generating-an-installation-access-token-for-a-github-app.md @@ -14,11 +14,8 @@ topics: In order to authenticate as an app installation, you must generate an installation access token. For more information about authenticating as an app installation, see "[Authenticating as a GitHub App installation](/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-as-a-github-app-installation)." -{% note %} - -**Note**: Instead of generating an installation access token, you can use {% data variables.product.company_short %}'s Octokit SDKs to authenticate as an app. The SDK will take care of generating an installation access token for you and will regenerate the token once it expires. For more information about authenticating as an app installation, see "[Authenticating as a GitHub App installation](/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-as-a-github-app-installation)." - -{% endnote %} +> [!NOTE] +> Instead of generating an installation access token, you can use {% data variables.product.company_short %}'s Octokit SDKs to authenticate as an app. The SDK will take care of generating an installation access token for you and will regenerate the token once it expires. For more information about authenticating as an app installation, see "[Authenticating as a GitHub App installation](/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-as-a-github-app-installation)." You should keep your installation access token secure. For more information, see "[AUTOTITLE](/apps/creating-github-apps/setting-up-a-github-app/best-practices-for-creating-a-github-app)." diff --git a/content/apps/creating-github-apps/authenticating-with-a-github-app/managing-private-keys-for-github-apps.md b/content/apps/creating-github-apps/authenticating-with-a-github-app/managing-private-keys-for-github-apps.md index b9f3eb9999e7..648d16528b34 100644 --- a/content/apps/creating-github-apps/authenticating-with-a-github-app/managing-private-keys-for-github-apps.md +++ b/content/apps/creating-github-apps/authenticating-with-a-github-app/managing-private-keys-for-github-apps.md @@ -32,11 +32,8 @@ To generate a private key: 1. Under "Private keys", click **Generate a private key**. 1. You will see a private key in PEM format downloaded to your computer. Make sure to store this file because GitHub only stores the public portion of the key. For more information about securely storing your key, see "[Storing private keys](#storing-private-keys)." -{% note %} - -**Note:** If you're using a library that requires a specific file format, the PEM file you download will be in `PKCS#1 RSAPrivateKey` format. - -{% endnote %} +> [!NOTE] +> If you're using a library that requires a specific file format, the PEM file you download will be in `PKCS#1 RSAPrivateKey` format. ## Verifying private keys diff --git a/content/apps/creating-github-apps/authenticating-with-a-github-app/refreshing-user-access-tokens.md b/content/apps/creating-github-apps/authenticating-with-a-github-app/refreshing-user-access-tokens.md index f03912991260..73f78828b283 100644 --- a/content/apps/creating-github-apps/authenticating-with-a-github-app/refreshing-user-access-tokens.md +++ b/content/apps/creating-github-apps/authenticating-with-a-github-app/refreshing-user-access-tokens.md @@ -16,11 +16,8 @@ shortTitle: Refresh user access tokens --- ## About user access tokens that expire -{% note %} - -**Note:** User access tokens that expire are currently an optional feature and are subject to change. For more information, see "[Expiring user-to-server access tokens for GitHub Apps](https://developer.github.com/changes/2020-04-30-expiring-user-to-server-access-tokens-for-github-apps)." - -{% endnote %} +> [!NOTE] +> User access tokens that expire are currently an optional feature and are subject to change. For more information, see "[Expiring user-to-server access tokens for GitHub Apps](https://developer.github.com/changes/2020-04-30-expiring-user-to-server-access-tokens-for-github-apps)." To enforce regular token rotation and reduce the impact of a compromised token, you can configure your {% data variables.product.prodname_github_app %} to use user access tokens that expire. If your app uses user access tokens that expire, then you will receive a refresh token when you generate a user access token. The user access token expires after eight hours, and the refresh token expires after six months. For more information, see "[AUTOTITLE](/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app)." diff --git a/content/apps/creating-github-apps/registering-a-github-app/about-the-setup-url.md b/content/apps/creating-github-apps/registering-a-github-app/about-the-setup-url.md index 9b4805f27f78..d2c6c2f27fe0 100644 --- a/content/apps/creating-github-apps/registering-a-github-app/about-the-setup-url.md +++ b/content/apps/creating-github-apps/registering-a-github-app/about-the-setup-url.md @@ -16,11 +16,8 @@ When you register a {% data variables.product.prodname_github_app %}, you can sp If you specify a setup URL, you can also select **Redirect on update** to specify that users should be redirected to the setup URL after they update an installation. An update includes adding or removing access to a repository for an installation. -{% warning %} - -**Warning**: When {% data variables.product.company_short %} redirects users to the setup URL, it includes an `installation_id` query parameter. Bad actors can hit this URL with a spoofed `installation_id`. Therefore, you should not rely on the validity of the `installation_id` parameter. Instead, you should generate a user access token for the user who installed the {% data variables.product.prodname_github_app %} and then check that the installation is associated with that user. For more information, see "[AUTOTITLE](/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app)." - -{% endwarning %} +> [!WARNING] +> When {% data variables.product.company_short %} redirects users to the setup URL, it includes an `installation_id` query parameter. Bad actors can hit this URL with a spoofed `installation_id`. Therefore, you should not rely on the validity of the `installation_id` parameter. Instead, you should generate a user access token for the user who installed the {% data variables.product.prodname_github_app %} and then check that the installation is associated with that user. For more information, see "[AUTOTITLE](/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app)." {% ifversion fpt or ghec %} Although the setup URL is optional during {% data variables.product.prodname_github_app %} registration, it is required if you want to allow users to purchase your app in {% data variables.product.prodname_marketplace %}. For more information, see "[AUTOTITLE](/apps/publishing-apps-to-github-marketplace/using-the-github-marketplace-api-in-your-app/handling-new-purchases-and-free-trials)." diff --git a/content/apps/creating-github-apps/registering-a-github-app/creating-a-custom-badge-for-your-github-app.md b/content/apps/creating-github-apps/registering-a-github-app/creating-a-custom-badge-for-your-github-app.md index 341b1dd1fb41..8821e3661bd3 100644 --- a/content/apps/creating-github-apps/registering-a-github-app/creating-a-custom-badge-for-your-github-app.md +++ b/content/apps/creating-github-apps/registering-a-github-app/creating-a-custom-badge-for-your-github-app.md @@ -43,11 +43,10 @@ For more information about badges for {% data variables.product.prodname_github_ 1. Under "Badge background color", type the hexadecimal color code of the background color for your badge. {% ifversion fpt or ghec %} - {% note %} - **Note:** The "Badge background color" input field will only appear after you upload a logo. + > [!NOTE] + > The "Badge background color" input field will only appear after you upload a logo. - {% endnote %} {% endif %} {% ifversion fpt or ghec %} diff --git a/content/apps/creating-github-apps/registering-a-github-app/using-webhooks-with-github-apps.md b/content/apps/creating-github-apps/registering-a-github-app/using-webhooks-with-github-apps.md index a19cab8a04ab..46c4a1a6d181 100644 --- a/content/apps/creating-github-apps/registering-a-github-app/using-webhooks-with-github-apps.md +++ b/content/apps/creating-github-apps/registering-a-github-app/using-webhooks-with-github-apps.md @@ -31,7 +31,7 @@ When you activate webhooks for your {% data variables.product.prodname_github_ap ### Choosing a webhook URL for development and testing -While you develop and test your app, you can use a webhook payload delivery service like [Smee](https://smee.io/) to capture and forward webhook payloads to your local development environment. Never use Smee for an application in production, because Smee channels are not authenticated or secure. Alternatively, you can use a tool like [ngrok](https://ngrok.com/docs/guides/developer-preview/getting-started/), [localtunnel](https://localtunnel.github.io/www/), or the [Hookdeck Console](https://console.hookdeck.com?provider=github) that exposes your local machine to the internet to receive the payloads. +While you develop and test your app, you can use a webhook payload delivery service like [Smee](https://smee.io/) to capture and forward webhook payloads to your local development environment. Never use Smee for an application in production, because Smee channels are not authenticated or secure. Alternatively, you can use a tool like [ngrok](https://ngrok.com/docs/guides/getting-started/), [localtunnel](https://localtunnel.github.io/www/), or the [Hookdeck Console](https://console.hookdeck.com?provider=github) that exposes your local machine to the internet to receive the payloads. #### Creating a webhook URL with Smee diff --git a/content/apps/github-marketplace/creating-apps-for-github-marketplace/requirements-for-listing-an-app.md b/content/apps/github-marketplace/creating-apps-for-github-marketplace/requirements-for-listing-an-app.md index 7bd218855676..9c427f5d8807 100644 --- a/content/apps/github-marketplace/creating-apps-for-github-marketplace/requirements-for-listing-an-app.md +++ b/content/apps/github-marketplace/creating-apps-for-github-marketplace/requirements-for-listing-an-app.md @@ -86,11 +86,8 @@ To publish a paid app (or an app that offers a paid plan), you must also meet th When you are ready to publish the app on {% data variables.product.prodname_marketplace %} you must request verification for the app listing. -{% note %} - -**Note:** {% data reusables.marketplace.app-transfer-to-org-for-verification %} For information on how to transfer an app to an organization, see: "[AUTOTITLE](/apps/github-marketplace/listing-an-app-on-github-marketplace/submitting-your-listing-for-publication#transferring-an-app-to-an-organization-before-you-submit)." - -{% endnote %} +> [!NOTE] +> {% data reusables.marketplace.app-transfer-to-org-for-verification %} For information on how to transfer an app to an organization, see: "[AUTOTITLE](/apps/github-marketplace/listing-an-app-on-github-marketplace/submitting-your-listing-for-publication#transferring-an-app-to-an-organization-before-you-submit)." ## Billing requirements for paid apps diff --git a/content/apps/github-marketplace/creating-apps-for-github-marketplace/viewing-metrics-for-your-listing.md b/content/apps/github-marketplace/creating-apps-for-github-marketplace/viewing-metrics-for-your-listing.md index 12bcc7ccdc60..0b6286019275 100644 --- a/content/apps/github-marketplace/creating-apps-for-github-marketplace/viewing-metrics-for-your-listing.md +++ b/content/apps/github-marketplace/creating-apps-for-github-marketplace/viewing-metrics-for-your-listing.md @@ -21,11 +21,8 @@ shortTitle: View listing metrics You can view metrics for the past day (24 hours), week, month, or for the entire duration of time that your {% data variables.product.prodname_github_app %} has been listed. -{% note %} - -**Note:** Because it takes time to aggregate data, you'll notice a slight delay in the dates shown. When you select a time period, you can see exact dates for the metrics at the top of the page. - -{% endnote %} +> [!NOTE] +> Because it takes time to aggregate data, you'll notice a slight delay in the dates shown. When you select a time period, you can see exact dates for the metrics at the top of the page. ## Performance metrics @@ -35,11 +32,8 @@ The Insights page displays these performance metrics, for the selected time peri * **Visitors:** Number of people that have viewed a page in your GitHub Apps listing. This number includes both logged in and logged out visitors. * **Pageviews:** Number of views the pages in your GitHub App's listing received. A single visitor can generate more than one page view. -{% note %} - -**Note:** Your estimated subscription value could be much higher than the transactions processed for this period. - -{% endnote %} +> [!NOTE] +> Your estimated subscription value could be much higher than the transactions processed for this period. ### Conversion performance diff --git a/content/apps/github-marketplace/creating-apps-for-github-marketplace/viewing-transactions-for-your-listing.md b/content/apps/github-marketplace/creating-apps-for-github-marketplace/viewing-transactions-for-your-listing.md index 9277c100d2b7..6fac54b4d15b 100644 --- a/content/apps/github-marketplace/creating-apps-for-github-marketplace/viewing-transactions-for-your-listing.md +++ b/content/apps/github-marketplace/creating-apps-for-github-marketplace/viewing-transactions-for-your-listing.md @@ -16,11 +16,8 @@ shortTitle: View listing transactions {% data reusables.marketplace.marketplace-apps-not-actions %} -{% note %} - -**Note:** Because it takes time to aggregate data, you'll notice a slight delay in the dates shown. When you select a time period, you can see exact dates for the metrics at the top of the page. - -{% endnote %} +> [!NOTE] +> Because it takes time to aggregate data, you'll notice a slight delay in the dates shown. When you select a time period, you can see exact dates for the metrics at the top of the page. You can view or download the transaction data to keep track of your subscription activity. Click the **Export CSV** button to download a `.csv` file. You can also select a period of time to view and search within the transaction page. diff --git a/content/apps/github-marketplace/listing-an-app-on-github-marketplace/drafting-a-listing-for-your-app.md b/content/apps/github-marketplace/listing-an-app-on-github-marketplace/drafting-a-listing-for-your-app.md index 820b98cc03fa..f09bc4504389 100644 --- a/content/apps/github-marketplace/listing-an-app-on-github-marketplace/drafting-a-listing-for-your-app.md +++ b/content/apps/github-marketplace/listing-an-app-on-github-marketplace/drafting-a-listing-for-your-app.md @@ -39,11 +39,8 @@ To create a {% data variables.product.prodname_marketplace %} listing: {% data reusables.user-settings.developer_settings %} 1. In the left sidebar, click either **OAuth Apps** or **GitHub Apps** depending on the app you're adding to {% data variables.product.prodname_marketplace %}. - {% note %} - - **Note**: You can also add a listing by navigating to https://github.com/marketplace/new, viewing your available apps, and clicking **Create draft listing**. - - {% endnote %} + > [!NOTE] + > You can also add a listing by navigating to https://github.com/marketplace/new, viewing your available apps, and clicking **Create draft listing**. ![Screenshot of the sidebar on the "Developer Settings" page of {% data variables.product.prodname_dotcom %}. Options labeled "{% data variables.product.prodname_github_apps %}" and "{% data variables.product.prodname_oauth_apps %}" are outlined in dark orange.](/assets/images/settings/apps-choose-app.png) @@ -53,11 +50,8 @@ To create a {% data variables.product.prodname_marketplace %} listing: ![Screenshot of a draft {% data variables.product.prodname_marketplace %} listing. In a section labeled "Publish your app to Marketplace," unfinished action items such as "Add your contact info" are marked with orange circles.](/assets/images/marketplace/marketplace-listing-overview.png) -{% note %} - -**Note:** In the "Contact info" section of your listing, we recommend using individual email addresses, rather than group emails addresses like support@domain.com. GitHub will use these email addresses to contact you about updates to {% data variables.product.prodname_marketplace %} that might affect your listing, new feature releases, marketing opportunities, payouts, and information on conferences and sponsorships. - -{% endnote %} +> [!NOTE] +> In the "Contact info" section of your listing, we recommend using individual email addresses, rather than group emails addresses like support@domain.com. GitHub will use these email addresses to contact you about updates to {% data variables.product.prodname_marketplace %} that might affect your listing, new feature releases, marketing opportunities, payouts, and information on conferences and sponsorships. ## Editing your listing diff --git a/content/apps/github-marketplace/listing-an-app-on-github-marketplace/setting-pricing-plans-for-your-listing.md b/content/apps/github-marketplace/listing-an-app-on-github-marketplace/setting-pricing-plans-for-your-listing.md index 61b042d149c6..882a2c9ef1c7 100644 --- a/content/apps/github-marketplace/listing-an-app-on-github-marketplace/setting-pricing-plans-for-your-listing.md +++ b/content/apps/github-marketplace/listing-an-app-on-github-marketplace/setting-pricing-plans-for-your-listing.md @@ -75,11 +75,8 @@ Once you publish a pricing plan for an app that is already listed in {% data var Once you remove a pricing plan, users won't be able to purchase your app using that plan. Existing users on the removed pricing plan will continue to stay on the plan until they cancel their plan subscription. -{% note %} - -**Note:** {% data variables.product.product_name %} can't remove users from a removed pricing plan. You can run a campaign to encourage users to upgrade or downgrade from the removed pricing plan onto a new pricing plan. - -{% endnote %} +> [!NOTE] +> {% data variables.product.product_name %} can't remove users from a removed pricing plan. You can run a campaign to encourage users to upgrade or downgrade from the removed pricing plan onto a new pricing plan. You can disable GitHub Marketplace free trials without retiring the pricing plan, but this prevents you from initiating future free trials for that plan. If you choose to disable free trials for a pricing plan, users already signed up can complete their free trial. diff --git a/content/apps/github-marketplace/selling-your-app-on-github-marketplace/pricing-plans-for-github-marketplace-apps.md b/content/apps/github-marketplace/selling-your-app-on-github-marketplace/pricing-plans-for-github-marketplace-apps.md index 3708fa823534..c17b52b0d0bd 100644 --- a/content/apps/github-marketplace/selling-your-app-on-github-marketplace/pricing-plans-for-github-marketplace-apps.md +++ b/content/apps/github-marketplace/selling-your-app-on-github-marketplace/pricing-plans-for-github-marketplace-apps.md @@ -57,8 +57,5 @@ Free trials have a fixed length of 14 days. Customers are notified 4 days before For more information, see: "[AUTOTITLE](/apps/github-marketplace/using-the-github-marketplace-api-in-your-app/handling-new-purchases-and-free-trials)." -{% note %} - -**Note:** GitHub expects you to delete any private customer data within 30 days of a canceled trial, beginning at the receipt of the cancellation event. - -{% endnote %} +> [!NOTE] +> GitHub expects you to delete any private customer data within 30 days of a canceled trial, beginning at the receipt of the cancellation event. diff --git a/content/apps/github-marketplace/selling-your-app-on-github-marketplace/receiving-payment-for-app-purchases.md b/content/apps/github-marketplace/selling-your-app-on-github-marketplace/receiving-payment-for-app-purchases.md index 96c03547fab1..a3a3fa54ffbb 100644 --- a/content/apps/github-marketplace/selling-your-app-on-github-marketplace/receiving-payment-for-app-purchases.md +++ b/content/apps/github-marketplace/selling-your-app-on-github-marketplace/receiving-payment-for-app-purchases.md @@ -26,8 +26,5 @@ Once your revenue reaches a minimum of 500 US dollars for the month, you'll rece For transactions made before January 1, 2021, {% data variables.product.company_short %} retains 25% of transaction income. For transactions made after that date, only 5% is retained by {% data variables.product.company_short %}. This change will be reflected in payments received from the end of January 2021 onward. -{% note %} - -**Note:** For details of the current pricing and payment terms, see "[AUTOTITLE](/free-pro-team@latest/site-policy/github-terms/github-marketplace-developer-agreement)." - -{% endnote %} +> [!NOTE] +> For details of the current pricing and payment terms, see "[AUTOTITLE](/free-pro-team@latest/site-policy/github-terms/github-marketplace-developer-agreement)." diff --git a/content/apps/github-marketplace/using-the-github-marketplace-api-in-your-app/handling-new-purchases-and-free-trials.md b/content/apps/github-marketplace/using-the-github-marketplace-api-in-your-app/handling-new-purchases-and-free-trials.md index 49bc2e183cd6..7f774e33bc2c 100644 --- a/content/apps/github-marketplace/using-the-github-marketplace-api-in-your-app/handling-new-purchases-and-free-trials.md +++ b/content/apps/github-marketplace/using-the-github-marketplace-api-in-your-app/handling-new-purchases-and-free-trials.md @@ -45,11 +45,8 @@ If your app is a {% data variables.product.prodname_github_app %}, {% data varia At this point, if you specified a **Setup URL** in your {% data variables.product.prodname_github_app %} settings, {% data variables.product.product_name %} will redirect the customer to that URL. If you do not specify a setup URL, you will not be able to handle purchases of your {% data variables.product.prodname_github_app %}. -{% note %} - -**Note:** The **Setup URL** is described as optional in {% data variables.product.prodname_github_app %} settings, but it is a required field if you want to offer your app in {% data variables.product.prodname_marketplace %}. For more information, see "[AUTOTITLE](/apps/creating-github-apps/registering-a-github-app/about-the-setup-url)." - -{% endnote %} +> [!NOTE] +> The **Setup URL** is described as optional in {% data variables.product.prodname_github_app %} settings, but it is a required field if you want to offer your app in {% data variables.product.prodname_marketplace %}. For more information, see "[AUTOTITLE](/apps/creating-github-apps/registering-a-github-app/about-the-setup-url)." If your app is an {% data variables.product.prodname_oauth_app %}, {% data variables.product.product_name %} does not install it anywhere. Instead, {% data variables.product.product_name %} redirects the customer to the **Installation URL** you specified in your [{% data variables.product.prodname_marketplace %} listing](/apps/github-marketplace/listing-an-app-on-github-marketplace/writing-a-listing-description-for-your-app#listing-urls). @@ -67,11 +64,8 @@ For either type of app, the first step is to redirect the customer to [https://g After the customer completes the authorization, your app receives an OAuth access token for the customer. You'll need this token for the next step. -{% note %} - -**Note:** When authorizing a customer on a free trial, grant them the same access they would have on the paid plan. You'll move them to the paid plan after the trial period ends. - -{% endnote %} +> [!NOTE] +> When authorizing a customer on a free trial, grant them the same access they would have on the paid plan. You'll move them to the paid plan after the trial period ends. ## Step 4. Provisioning customer accounts diff --git a/content/apps/github-marketplace/using-the-github-marketplace-api-in-your-app/handling-plan-cancellations.md b/content/apps/github-marketplace/using-the-github-marketplace-api-in-your-app/handling-plan-cancellations.md index 5d6f2921c64d..ca4356152ff1 100644 --- a/content/apps/github-marketplace/using-the-github-marketplace-api-in-your-app/handling-plan-cancellations.md +++ b/content/apps/github-marketplace/using-the-github-marketplace-api-in-your-app/handling-plan-cancellations.md @@ -33,8 +33,5 @@ When a customer cancels a free or paid plan, your app must perform these steps t 1. If your app is an {% data variables.product.prodname_oauth_app %}, remove all webhooks your app created for repositories. 1. Remove all customer data within 30 days of receiving the `cancelled` event. -{% note %} - -**Note:** We recommend using the [`marketplace_purchase`](/apps/github-marketplace/using-the-github-marketplace-api-in-your-app/webhook-events-for-the-github-marketplace-api) webhook's `effective_date` to determine when a plan change will occur and periodically synchronizing the [List accounts for a plan](/rest/apps/marketplace#list-accounts-for-a-plan). For more information on webhooks, see "[AUTOTITLE](/apps/github-marketplace/using-the-github-marketplace-api-in-your-app/webhook-events-for-the-github-marketplace-api)." - -{% endnote %} +> [!NOTE] +> We recommend using the [`marketplace_purchase`](/apps/github-marketplace/using-the-github-marketplace-api-in-your-app/webhook-events-for-the-github-marketplace-api) webhook's `effective_date` to determine when a plan change will occur and periodically synchronizing the [List accounts for a plan](/rest/apps/marketplace#list-accounts-for-a-plan). For more information on webhooks, see "[AUTOTITLE](/apps/github-marketplace/using-the-github-marketplace-api-in-your-app/webhook-events-for-the-github-marketplace-api)." diff --git a/content/apps/github-marketplace/using-the-github-marketplace-api-in-your-app/handling-plan-changes.md b/content/apps/github-marketplace/using-the-github-marketplace-api-in-your-app/handling-plan-changes.md index fe4755bb0a27..ba48be038923 100644 --- a/content/apps/github-marketplace/using-the-github-marketplace-api-in-your-app/handling-plan-changes.md +++ b/content/apps/github-marketplace/using-the-github-marketplace-api-in-your-app/handling-plan-changes.md @@ -40,11 +40,8 @@ When a customer downgrades a plan, it's recommended to review whether a customer To encourage people to upgrade you can display an upgrade URL in your app's UI. See "[About upgrade URLs](#about-upgrade-urls)" for more details. -{% note %} - -**Note:** We recommend performing a periodic synchronization using `GET /marketplace_listing/plans/:id/accounts` to ensure your app has the correct plan, billing cycle information, and unit count (for per-unit pricing) for each account. - -{% endnote %} +> [!NOTE] +> We recommend performing a periodic synchronization using `GET /marketplace_listing/plans/:id/accounts` to ensure your app has the correct plan, billing cycle information, and unit count (for per-unit pricing) for each account. ## Failed upgrade payments @@ -62,8 +59,5 @@ For example, if you notice that a customer is on a 5 person plan and needs to mo Use the `LISTING_PLAN_NUMBER` for the plan the customer would like to purchase. When you create new pricing plans they receive a `LISTING_PLAN_NUMBER`, which is unique to each plan across your listing, and a `LISTING_PLAN_ID`, which is unique to each plan in the {% data variables.product.prodname_marketplace %}. You can find these numbers when you [List plans](/rest/apps#list-plans), which identifies your listing's pricing plans. Use the `LISTING_PLAN_ID` and the "[`GET /marketplace_listing/plans/{plan_id}/accounts`](/rest/apps/marketplace#list-accounts-for-a-plan)" endpoint to get the `CUSTOMER_ACCOUNT_ID`. -{% note %} - -**Note:** If your customer upgrades to additional units (such as seats), you can still send them to the appropriate plan for their purchase, but we are unable to support `unit_count` parameters at this time. - -{% endnote %} +> [!NOTE] +> If your customer upgrades to additional units (such as seats), you can still send them to the appropriate plan for their purchase, but we are unable to support `unit_count` parameters at this time. diff --git a/content/apps/github-marketplace/using-the-github-marketplace-api-in-your-app/rest-endpoints-for-the-github-marketplace-api.md b/content/apps/github-marketplace/using-the-github-marketplace-api-in-your-app/rest-endpoints-for-the-github-marketplace-api.md index cb4bca5adca9..41c94e246838 100644 --- a/content/apps/github-marketplace/using-the-github-marketplace-api-in-your-app/rest-endpoints-for-the-github-marketplace-api.md +++ b/content/apps/github-marketplace/using-the-github-marketplace-api-in-your-app/rest-endpoints-for-the-github-marketplace-api.md @@ -30,8 +30,5 @@ See these pages for details on how to authenticate when using the {% data variab * [Authorization options for {% data variables.product.prodname_oauth_apps %}](/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps) * [Authentication options for {% data variables.product.prodname_github_apps %}](/apps/creating-github-apps/authenticating-with-a-github-app/about-authentication-with-a-github-app) -{% note %} - -**Note:** [Rate limits for the REST API](/rest/using-the-rest-api/rate-limits-for-the-rest-api) apply to all {% data variables.product.prodname_marketplace %} API endpoints. - -{% endnote %} +> [!NOTE] +> [Rate limits for the REST API](/rest/using-the-rest-api/rate-limits-for-the-rest-api) apply to all {% data variables.product.prodname_marketplace %} API endpoints. diff --git a/content/apps/maintaining-github-apps/activating-optional-features-for-github-apps.md b/content/apps/maintaining-github-apps/activating-optional-features-for-github-apps.md index 9eab002e22f0..1b2208abf532 100644 --- a/content/apps/maintaining-github-apps/activating-optional-features-for-github-apps.md +++ b/content/apps/maintaining-github-apps/activating-optional-features-for-github-apps.md @@ -14,11 +14,9 @@ topics: - GitHub Apps shortTitle: Activate optional features --- -{% warning %} -**Warning:** Optional features are subject to change. - -{% endwarning %} +> [!WARNING] +> Optional features are subject to change. ## Activating optional features for {% data variables.product.prodname_github_apps %} diff --git a/content/apps/maintaining-github-apps/deleting-a-github-app.md b/content/apps/maintaining-github-apps/deleting-a-github-app.md index a0bcd0a92abd..1b50989a562c 100644 --- a/content/apps/maintaining-github-apps/deleting-a-github-app.md +++ b/content/apps/maintaining-github-apps/deleting-a-github-app.md @@ -15,11 +15,8 @@ topics: - GitHub Apps --- -{% note %} - -**Note**: If you want to remove a {% data variables.product.prodname_github_app %} that you use but do not own, see "[AUTOTITLE](/apps/using-github-apps/reviewing-and-modifying-installed-github-apps#blocking-access)" instead. - -{% endnote %} +> [!NOTE] +> If you want to remove a {% data variables.product.prodname_github_app %} that you use but do not own, see "[AUTOTITLE](/apps/using-github-apps/reviewing-and-modifying-installed-github-apps#blocking-access)" instead. ## About deleting {% data variables.product.prodname_github_apps %} @@ -29,11 +26,8 @@ When you delete a {% data variables.product.prodname_github_app %} registration, {% ifversion ghec or fpt %} -{% note %} - -**Note**: If your {% data variables.product.prodname_github_app %} is published on {% data variables.product.prodname_marketplace %}, you must remove your app from {% data variables.product.prodname_marketplace %} before you can delete your app. For more information, see "[AUTOTITLE](/apps/publishing-apps-to-github-marketplace/listing-an-app-on-github-marketplace/deleting-your-github-app-listing-from-github-marketplace)." - -{% endnote %} +> [!NOTE] +> If your {% data variables.product.prodname_github_app %} is published on {% data variables.product.prodname_marketplace %}, you must remove your app from {% data variables.product.prodname_marketplace %} before you can delete your app. For more information, see "[AUTOTITLE](/apps/publishing-apps-to-github-marketplace/listing-an-app-on-github-marketplace/deleting-your-github-app-listing-from-github-marketplace)." {% endif %} diff --git a/content/apps/maintaining-github-apps/suspending-a-github-app-installation.md b/content/apps/maintaining-github-apps/suspending-a-github-app-installation.md index dfcb00a9cc66..2714d61c719e 100644 --- a/content/apps/maintaining-github-apps/suspending-a-github-app-installation.md +++ b/content/apps/maintaining-github-apps/suspending-a-github-app-installation.md @@ -14,11 +14,8 @@ topics: shortTitle: Suspend an installation --- -{% note %} - -**Note**: If you want to suspend a {% data variables.product.prodname_github_app %} that you use but do not own, see "[AUTOTITLE](/apps/using-github-apps/reviewing-and-modifying-installed-github-apps#blocking-access)" instead. - -{% endnote %} +> [!NOTE] +> If you want to suspend a {% data variables.product.prodname_github_app %} that you use but do not own, see "[AUTOTITLE](/apps/using-github-apps/reviewing-and-modifying-installed-github-apps#blocking-access)" instead. When a {% data variables.product.prodname_github_app %} is suspended for an installation, the {% data variables.product.prodname_github_app %} cannot access resources owned by that installation account. For example, you might want to suspend your {% data variables.product.prodname_github_app %} if you are worried that your app's credentials were leaked. diff --git a/content/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps.md b/content/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps.md index 6b039372c749..c27305aa2a88 100644 --- a/content/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps.md +++ b/content/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps.md @@ -18,15 +18,12 @@ topics: - OAuth apps --- -{% note %} - -**Note**: Consider building a {% data variables.product.prodname_github_app %} instead of an {% data variables.product.prodname_oauth_app %}. - -Both {% data variables.product.prodname_oauth_apps %} and {% data variables.product.prodname_github_apps %} use OAuth 2.0. - -{% data variables.product.prodname_github_apps %} can act on behalf of a user, similar to an {% data variables.product.prodname_oauth_app %}, or as themselves, which is beneficial for automations that do not require user input. Additionally, {% data variables.product.prodname_github_apps %} use fine-grained permissions, give the user more control over which repositories the app can access, and use short-lived tokens. For more information, see "[AUTOTITLE](/apps/oauth-apps/building-oauth-apps/differences-between-github-apps-and-oauth-apps)" and "[AUTOTITLE](/apps/creating-github-apps/setting-up-a-github-app/about-creating-github-apps)." - -{% endnote %} +> [!NOTE] +> Consider building a {% data variables.product.prodname_github_app %} instead of an {% data variables.product.prodname_oauth_app %}. +> +> Both {% data variables.product.prodname_oauth_apps %} and {% data variables.product.prodname_github_apps %} use OAuth 2.0. +> +> {% data variables.product.prodname_github_apps %} can act on behalf of a user, similar to an {% data variables.product.prodname_oauth_app %}, or as themselves, which is beneficial for automations that do not require user input. Additionally, {% data variables.product.prodname_github_apps %} use fine-grained permissions, give the user more control over which repositories the app can access, and use short-lived tokens. For more information, see "[AUTOTITLE](/apps/oauth-apps/building-oauth-apps/differences-between-github-apps-and-oauth-apps)" and "[AUTOTITLE](/apps/creating-github-apps/setting-up-a-github-app/about-creating-github-apps)." {% data variables.product.product_name %}'s OAuth implementation supports the standard [authorization code grant type](https://tools.ietf.org/html/rfc6749#section-4.1) and the OAuth 2.0 [Device Authorization Grant](https://tools.ietf.org/html/rfc8628) for apps that don't have access to a web browser. @@ -45,11 +42,8 @@ To authorize your {% data variables.product.prodname_oauth_app %}, consider whic ## Web application flow -{% note %} - -**Note:** If you are building a GitHub App, you can still use the OAuth web application flow, but the setup has some important differences. See "[AUTOTITLE](/apps/creating-github-apps/authenticating-with-a-github-app/identifying-and-authorizing-users-for-github-apps)" for more information. - -{% endnote %} +> [!NOTE] +> If you are building a GitHub App, you can still use the OAuth web application flow, but the setup has some important differences. See "[AUTOTITLE](/apps/creating-github-apps/authenticating-with-a-github-app/identifying-and-authorizing-users-for-github-apps)" for more information. The web application flow to authorize users for your app is: @@ -323,11 +317,8 @@ To build this link, you'll need your {% data variables.product.prodname_oauth_ap {% data variables.product.oauth_host_code %}/settings/connections/applications/:client_id ``` -{% tip %} - -**Tip:** To learn more about the resources that your {% data variables.product.prodname_oauth_app %} can access for a user, see "[AUTOTITLE](/rest/guides/discovering-resources-for-a-user)." - -{% endtip %} +> [!TIP] +> To learn more about the resources that your {% data variables.product.prodname_oauth_app %} can access for a user, see "[AUTOTITLE](/rest/guides/discovering-resources-for-a-user)." ## Troubleshooting diff --git a/content/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app.md b/content/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app.md index 439ed9d5f083..2c29ffe5eb28 100644 --- a/content/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app.md +++ b/content/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app.md @@ -14,26 +14,22 @@ topics: - OAuth apps --- -{% note %} - -**Note**: Consider building a {% data variables.product.prodname_github_app %} instead of an {% data variables.product.prodname_oauth_app %}. - -Both {% data variables.product.prodname_oauth_apps %} and {% data variables.product.prodname_github_apps %} use OAuth 2.0. - -{% data variables.product.prodname_oauth_apps %} can only act on behalf of a user while {% data variables.product.prodname_github_apps %} can either act on behalf of a user or independently of a user. - -{% data variables.product.prodname_github_apps %} use fine-grained permissions, give the user more control over which repositories the app can access, and use short-lived tokens. - -For more information, see "[AUTOTITLE](/apps/oauth-apps/building-oauth-apps/differences-between-github-apps-and-oauth-apps)" and "[AUTOTITLE](/apps/creating-github-apps/setting-up-a-github-app/about-creating-github-apps)." - -{% endnote %} +> [!NOTE] +> Consider building a {% data variables.product.prodname_github_app %} instead of an {% data variables.product.prodname_oauth_app %}. +> +> Both {% data variables.product.prodname_oauth_apps %} and {% data variables.product.prodname_github_apps %} use OAuth 2.0. +> +> {% data variables.product.prodname_oauth_apps %} can only act on behalf of a user while {% data variables.product.prodname_github_apps %} can either act on behalf of a user or independently of a user. +> +> {% data variables.product.prodname_github_apps %} use fine-grained permissions, give the user more control over which repositories the app can access, and use short-lived tokens. +> +> For more information, see "[AUTOTITLE](/apps/oauth-apps/building-oauth-apps/differences-between-github-apps-and-oauth-apps)" and "[AUTOTITLE](/apps/creating-github-apps/setting-up-a-github-app/about-creating-github-apps)." {% ifversion fpt or ghec %} -{% note %} - **Note:** {% data reusables.apps.maximum-oauth-apps-allowed %} +> [!NOTE] +> {% data reusables.apps.maximum-oauth-apps-allowed %} -{% endnote %} {% endif %} {% data reusables.user-settings.access_settings %} @@ -41,27 +37,21 @@ For more information, see "[AUTOTITLE](/apps/oauth-apps/building-oauth-apps/diff {% data reusables.user-settings.oauth_apps %} 1. Click **New OAuth App**. - {% note %} - - **Note:** If you haven't created an app before, this button will say, **Register a new application**. + > [!NOTE] + > If you haven't created an app before, this button will say, **Register a new application**. - {% endnote %} 1. In "Application name", type the name of your app. - {% warning %} - - **Warning:** Only use information in your {% data variables.product.prodname_oauth_app %} that you consider public. Avoid using sensitive data, such as internal URLs, when creating an {% data variables.product.prodname_oauth_app %}. - - {% endwarning %} + > [!WARNING] + > Only use information in your {% data variables.product.prodname_oauth_app %} that you consider public. Avoid using sensitive data, such as internal URLs, when creating an {% data variables.product.prodname_oauth_app %}. 1. In "Homepage URL", type the full URL to your app's website. 1. Optionally, in "Application description", type a description of your app that users will see. 1. In "Authorization callback URL", type the callback URL of your app. - {% note %} - **Note:** {% data variables.product.prodname_oauth_apps %} cannot have multiple callback URLs, unlike {% data variables.product.prodname_github_apps %}. + > [!NOTE] + > {% data variables.product.prodname_oauth_apps %} cannot have multiple callback URLs, unlike {% data variables.product.prodname_github_apps %}. - {% endnote %} 1. If your {% data variables.product.prodname_oauth_app %} will use the device flow to identify and authorize users, click **Enable Device Flow**. For more information about the device flow, see "[AUTOTITLE](/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps#device-flow)." 1. Click **Register application**. diff --git a/content/apps/oauth-apps/building-oauth-apps/differences-between-github-apps-and-oauth-apps.md b/content/apps/oauth-apps/building-oauth-apps/differences-between-github-apps-and-oauth-apps.md index bc4bc9fb072a..d7d09058eac8 100644 --- a/content/apps/oauth-apps/building-oauth-apps/differences-between-github-apps-and-oauth-apps.md +++ b/content/apps/oauth-apps/building-oauth-apps/differences-between-github-apps-and-oauth-apps.md @@ -71,11 +71,8 @@ An _authorized_ {% data variables.product.prodname_oauth_app %} has access to al ## Token-based identification -{% note %} - -**Note:** GitHub Apps can also use a user-based token. For more information, see "[AUTOTITLE](/apps/creating-github-apps/authenticating-with-a-github-app/identifying-and-authorizing-users-for-github-apps)." - -{% endnote %} +> [!NOTE] +> GitHub Apps can also use a user-based token. For more information, see "[AUTOTITLE](/apps/creating-github-apps/authenticating-with-a-github-app/identifying-and-authorizing-users-for-github-apps)." | GitHub Apps | {% data variables.product.prodname_oauth_apps %} | | ----- | ----------- | diff --git a/content/apps/oauth-apps/building-oauth-apps/rate-limits-for-oauth-apps.md b/content/apps/oauth-apps/building-oauth-apps/rate-limits-for-oauth-apps.md index 08f95595c6b7..d417fd9f11e3 100644 --- a/content/apps/oauth-apps/building-oauth-apps/rate-limits-for-oauth-apps.md +++ b/content/apps/oauth-apps/building-oauth-apps/rate-limits-for-oauth-apps.md @@ -10,11 +10,8 @@ topics: shortTitle: Rate limits --- -{% note %} - -**Note**: Consider building a {% data variables.product.prodname_github_app %} instead of an {% data variables.product.prodname_oauth_app %}. The rate limit for {% data variables.product.prodname_github_apps %} using an installation access token scales with the number of repositories and number of organization users. Conversely, {% data variables.product.prodname_oauth_apps %} have lower rate limits and do not scale. For more information, see "[AUTOTITLE](/apps/oauth-apps/building-oauth-apps/differences-between-github-apps-and-oauth-apps)" and "[AUTOTITLE](/apps/creating-github-apps/setting-up-a-github-app/about-creating-github-apps)." - -{% endnote %} +> [!NOTE] +> Consider building a {% data variables.product.prodname_github_app %} instead of an {% data variables.product.prodname_oauth_app %}. The rate limit for {% data variables.product.prodname_github_apps %} using an installation access token scales with the number of repositories and number of organization users. Conversely, {% data variables.product.prodname_oauth_apps %} have lower rate limits and do not scale. For more information, see "[AUTOTITLE](/apps/oauth-apps/building-oauth-apps/differences-between-github-apps-and-oauth-apps)" and "[AUTOTITLE](/apps/creating-github-apps/setting-up-a-github-app/about-creating-github-apps)." ## About rate limits for {% data variables.product.prodname_oauth_apps %} diff --git a/content/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps.md b/content/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps.md index 343af94d9b93..f59c64709781 100644 --- a/content/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps.md +++ b/content/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps.md @@ -15,19 +15,13 @@ topics: - OAuth apps --- -{% note %} - -**Note**: Consider building a {% data variables.product.prodname_github_app %} instead of an {% data variables.product.prodname_oauth_app %}. {% data variables.product.prodname_github_apps %} use fine-grained permissions instead of scopes, which give you more control over what your app can do. For more information, see "[AUTOTITLE](/apps/oauth-apps/building-oauth-apps/differences-between-github-apps-and-oauth-apps)" and "[AUTOTITLE](/apps/creating-github-apps/setting-up-a-github-app/about-creating-github-apps)." - -{% endnote %} +> [!NOTE] +> Consider building a {% data variables.product.prodname_github_app %} instead of an {% data variables.product.prodname_oauth_app %}. {% data variables.product.prodname_github_apps %} use fine-grained permissions instead of scopes, which give you more control over what your app can do. For more information, see "[AUTOTITLE](/apps/oauth-apps/building-oauth-apps/differences-between-github-apps-and-oauth-apps)" and "[AUTOTITLE](/apps/creating-github-apps/setting-up-a-github-app/about-creating-github-apps)." When setting up an {% data variables.product.prodname_oauth_app %} on GitHub, requested scopes are displayed to the user on the authorization form. -{% note %} - -**Note:** If you're building a GitHub App, you don’t need to provide scopes in your authorization request. For more on this, see "[AUTOTITLE](/apps/creating-github-apps/authenticating-with-a-github-app/identifying-and-authorizing-users-for-github-apps)." - -{% endnote %} +> [!NOTE] +> If you're building a GitHub App, you don’t need to provide scopes in your authorization request. For more on this, see "[AUTOTITLE](/apps/creating-github-apps/authenticating-with-a-github-app/identifying-and-authorizing-users-for-github-apps)." If your {% data variables.product.prodname_oauth_app %} doesn't have access to a browser, such as a CLI tool, then you don't need to specify a scope for users to authenticate to your app. For more information, see "[AUTOTITLE](/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps#device-flow)." @@ -49,7 +43,7 @@ Name | Description -----|-----------| **`(no scope)`** | Grants read-only access to public information (including user profile info, repository info, and gists){% ifversion ghes %} **`site_admin`** | Grants site administrators access to [{% data variables.product.prodname_ghe_server %} Administration API endpoints](/rest/enterprise-admin).{% endif %} -**`repo`** | Grants full access to public{% ifversion ghec or ghes %}, internal,{% endif %} and private repositories including read and write access to code, commit statuses, repository invitations, collaborators, deployment statuses, and repository webhooks. **Note**: In addition to repository related resources, the `repo` scope also grants access to manage organization-owned resources including projects, invitations, team memberships and webhooks. This scope also grants the ability to manage projects owned by users. +**`repo`** | Grants full access to public{% ifversion ghec or ghes %}, internal,{% endif %} and private repositories including read and write access to code, commit statuses, repository invitations, collaborators, deployment statuses, and repository webhooks. **Note:** In addition to repository related resources, the `repo` scope also grants access to manage organization-owned resources including projects, invitations, team memberships and webhooks. This scope also grants the ability to manage projects owned by users.  `repo:status`| Grants read/write access to commit statuses in {% ifversion fpt %}public and private{% elsif ghec or ghes %}public, private, and internal{% endif %} repositories. This scope is only necessary to grant other users or services access to private repository commit statuses _without_ granting access to the code.  `repo_deployment`| Grants access to [deployment statuses](/rest/repos#deployments) for public and private repositories. This scope is only necessary to grant other users or services access to deployment statuses, _without_ granting access to the code.  `public_repo`| Limits access to public repositories. That includes read/write access to code, commit statuses, repository projects, collaborators, and deployment statuses for public repositories and organizations. Also required for starring public repositories. @@ -89,16 +83,15 @@ Name | Description  `manage_billing:enterprise` | Read and write enterprise billing data. For more information, see "[AUTOTITLE](/rest/billing)." {% endif %}  `read:enterprise` | Read all data on an enterprise profile. Does not include profile data of enterprise members or organizations.{% endif %}{% ifversion read-audit-scope %} **`read:audit_log`** | Read audit log data.{% endif %} -{% note %} - -**Note:** Your {% data variables.product.prodname_oauth_app %} can request the scopes in the initial redirection. You -can specify multiple scopes by separating them with a space using `%20`: - - https://github.com/login/oauth/authorize? - client_id=...& - scope=user%20repo_deployment -{% endnote %} +> [!NOTE] +> Your {% data variables.product.prodname_oauth_app %} can request the scopes in the initial redirection. You can specify multiple scopes by separating them with a space using `%20`: +> +> ```text +> https://github.com/login/oauth/authorize? +> client_id=...& +> scope=user%20repo_deployment +> ``` ## Requested scopes and granted scopes diff --git a/content/apps/oauth-apps/maintaining-oauth-apps/activating-optional-features-for-oauth-apps.md b/content/apps/oauth-apps/maintaining-oauth-apps/activating-optional-features-for-oauth-apps.md index 647fe4b09942..3b460ab856a0 100644 --- a/content/apps/oauth-apps/maintaining-oauth-apps/activating-optional-features-for-oauth-apps.md +++ b/content/apps/oauth-apps/maintaining-oauth-apps/activating-optional-features-for-oauth-apps.md @@ -9,11 +9,9 @@ topics: - OAuth apps shortTitle: Activate optional features --- -{% warning %} -**Warning:** Optional features are subject to change. - -{% endwarning %} +> [!WARNING] +> Optional features are subject to change. ## Activating optional features for {% data variables.product.prodname_oauth_apps %} diff --git a/content/apps/oauth-apps/maintaining-oauth-apps/troubleshooting-oauth-app-access-token-request-errors.md b/content/apps/oauth-apps/maintaining-oauth-apps/troubleshooting-oauth-app-access-token-request-errors.md index bdc109d8e27a..f085f84f1c26 100644 --- a/content/apps/oauth-apps/maintaining-oauth-apps/troubleshooting-oauth-app-access-token-request-errors.md +++ b/content/apps/oauth-apps/maintaining-oauth-apps/troubleshooting-oauth-app-access-token-request-errors.md @@ -14,11 +14,9 @@ topics: - OAuth apps shortTitle: Troubleshoot token request --- -{% note %} -**Note:** These examples only show JSON responses. - -{% endnote %} +> [!NOTE] +> These examples only show JSON responses. ## Incorrect client credentials diff --git a/content/apps/oauth-apps/using-oauth-apps/authorizing-oauth-apps.md b/content/apps/oauth-apps/using-oauth-apps/authorizing-oauth-apps.md index 8b68cf377eb6..e45e08ae7e52 100644 --- a/content/apps/oauth-apps/using-oauth-apps/authorizing-oauth-apps.md +++ b/content/apps/oauth-apps/using-oauth-apps/authorizing-oauth-apps.md @@ -18,11 +18,8 @@ When an {% data variables.product.prodname_oauth_app %} wants to identify you by {% ifversion fpt or ghec %} -{% tip %} - -**Tip:** You must [verify your email address](/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/verifying-your-email-address) before you can authorize an {% data variables.product.prodname_oauth_app %}. - -{% endtip %} +> [!TIP] +> You must [verify your email address](/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/verifying-your-email-address) before you can authorize an {% data variables.product.prodname_oauth_app %}. {% endif %} @@ -33,11 +30,8 @@ When an {% data variables.product.prodname_oauth_app %} wants to identify you by * **Read access** only allows an app to _look at_ your data. * **Write access** allows an app to _change_ your data. -{% tip %} - -**Tip:** {% data reusables.user-settings.review_oauth_tokens_tip %} - -{% endtip %} +> [!TIP] +> {% data reusables.user-settings.review_oauth_tokens_tip %} ### About OAuth scopes @@ -45,11 +39,8 @@ _Scopes_ are named groups of permissions that an {% data variables.product.prodn When you want to use an {% data variables.product.prodname_oauth_app %} that integrates with {% data variables.product.product_name %}, that app lets you know what type of access to your data will be required. If you grant access to the app, then the app will be able to perform actions on your behalf, such as reading or modifying data. For example, if you want to use an app that requests `user:email` scope, the app will have read-only access to your private email addresses. For more information, see "[AUTOTITLE](/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps)." -{% tip %} - -**Note:** Currently, you can't scope source code access to read-only. - -{% endtip %} +> [!NOTE] +> Currently, you can't scope source code access to read-only. {% data reusables.user-settings.token_access_capabilities %} For example, an application can create an access token that is configured with an `admin:org` scope, but if the user of the application is not an organization owner, the application will not be granted administrative access to the organization. @@ -90,11 +81,8 @@ When you authorize an {% data variables.product.prodname_oauth_app %} for your p If you belong to any organizations with SAML single sign-on (SSO) enabled, and you have created a linked identity for that organization by authenticating via SAML in the past, you must have an active SAML session for each organization each time you authorize an {% data variables.product.prodname_oauth_app %}. -{% note %} - -**Note:** If you're encountering issues with an authorized {% data variables.product.prodname_oauth_app %} or {% data variables.product.prodname_github_app %} accessing an organization that is protected by SAML, you may need to revoke the app from your [Authorized {% data variables.product.prodname_github_apps %}](https://github.com/settings/applications) or [Authorized {% data variables.product.prodname_oauth_apps %}](https://github.com/settings/apps/authorizations) page, visit the organization to authenticate and establish an active SAML session, and then attempt to reauthorize the app by accessing it. - -{% endnote %} +> [!NOTE] +> If you're encountering issues with an authorized {% data variables.product.prodname_oauth_app %} or {% data variables.product.prodname_github_app %} accessing an organization that is protected by SAML, you may need to revoke the app from your [Authorized {% data variables.product.prodname_github_apps %}](https://github.com/settings/applications) or [Authorized {% data variables.product.prodname_oauth_apps %}](https://github.com/settings/apps/authorizations) page, visit the organization to authenticate and establish an active SAML session, and then attempt to reauthorize the app by accessing it. ## Further reading diff --git a/content/apps/oauth-apps/using-oauth-apps/connecting-with-third-party-applications.md b/content/apps/oauth-apps/using-oauth-apps/connecting-with-third-party-applications.md index 51a8bf48e117..1c4be9d6bdcc 100644 --- a/content/apps/oauth-apps/using-oauth-apps/connecting-with-third-party-applications.md +++ b/content/apps/oauth-apps/using-oauth-apps/connecting-with-third-party-applications.md @@ -37,17 +37,11 @@ _Scopes_ are named groups of permissions that an application can request to acce When you want to use a third-party application that integrates with {% data variables.product.product_name %}, that application lets you know what type of access to your data will be required. If you grant access to the application, then the application will be able to perform actions on your behalf, such as reading or modifying data. For example, if you want to use an app that requests `user:email` scope, the app will have read-only access to your private email addresses. For more information, see "[AUTOTITLE](/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps)." -{% tip %} +> [!NOTE] +> Currently, you can't scope source code access to read-only. -**Note:** Currently, you can't scope source code access to read-only. - -{% endtip %} - -{% tip %} - -**Tip:** {% data reusables.user-settings.review_oauth_tokens_tip %} - -{% endtip %} +> [!TIP] +> {% data reusables.user-settings.review_oauth_tokens_tip %} ### Types of requested data diff --git a/content/apps/sharing-github-apps/registering-a-github-app-from-a-manifest.md b/content/apps/sharing-github-apps/registering-a-github-app-from-a-manifest.md index 9265fc8b395b..58c756198855 100644 --- a/content/apps/sharing-github-apps/registering-a-github-app-from-a-manifest.md +++ b/content/apps/sharing-github-apps/registering-a-github-app-from-a-manifest.md @@ -172,11 +172,8 @@ To complete the handshake, send the temporary `code` in a `POST` request to the You must complete this step of the {% data variables.product.prodname_github_app %} Manifest flow within one hour. -{% note %} - -**Note:** This endpoint is rate limited. See [Rate limits](/rest/rate-limit/rate-limit) to learn how to get your current rate limit status. - -{% endnote %} +> [!NOTE] +> This endpoint is rate limited. See [Rate limits](/rest/rate-limit/rate-limit) to learn how to get your current rate limit status. POST /app-manifests/{code}/conversions diff --git a/content/apps/using-github-apps/authorizing-github-apps.md b/content/apps/using-github-apps/authorizing-github-apps.md index 6e17b938d78a..ea3c7304724b 100644 --- a/content/apps/using-github-apps/authorizing-github-apps.md +++ b/content/apps/using-github-apps/authorizing-github-apps.md @@ -28,11 +28,10 @@ When authorizing the {% data variables.product.prodname_github_app %}, you shoul You can review and revoke your authorization at any time. For more information, see "[AUTOTITLE](/apps/using-github-apps/reviewing-your-authorized-integrations)." {% ifversion ghec %} -{% note %} -**Note**: If your organization uses SAML SSO and you cannot see your organization's resources after you authorize a {% data variables.product.prodname_github_app %}, you may need to reauthorize the app after starting an active SAML session for your organization. For more information, see "[AUTOTITLE](/apps/using-github-apps/saml-and-github-apps)." +> [!NOTE] +> If your organization uses SAML SSO and you cannot see your organization's resources after you authorize a {% data variables.product.prodname_github_app %}, you may need to reauthorize the app after starting an active SAML session for your organization. For more information, see "[AUTOTITLE](/apps/using-github-apps/saml-and-github-apps)." -{% endnote %} {% endif %} ## About {% data variables.product.prodname_github_apps %} acting on your behalf diff --git a/content/apps/using-github-apps/installing-a-github-app-from-a-third-party.md b/content/apps/using-github-apps/installing-a-github-app-from-a-third-party.md index 219f0660d954..8ed8085e2e16 100644 --- a/content/apps/using-github-apps/installing-a-github-app-from-a-third-party.md +++ b/content/apps/using-github-apps/installing-a-github-app-from-a-third-party.md @@ -55,11 +55,10 @@ During the installation process, the app owner will direct you to a {% data vari 1. Select the location where you want to install the {% data variables.product.prodname_github_app %}. You can select your personal account or an organization that you are a member of. {% ifversion ghec %} - {% note %} - **Note**: {% data reusables.apps.github_app_install_saml %} + > [!NOTE] + > {% data reusables.apps.github_app_install_saml %} - {% endnote %} {% endif %} 1. If the app requires repository permissions, select **All repositories** or **Only select repositories**. The app will always have at least read-only access to all public repositories on {% data variables.product.company_short %}. diff --git a/content/apps/using-github-apps/requesting-a-github-app-from-your-organization-owner.md b/content/apps/using-github-apps/requesting-a-github-app-from-your-organization-owner.md index b3130bab6c90..4f80dac477f3 100644 --- a/content/apps/using-github-apps/requesting-a-github-app-from-your-organization-owner.md +++ b/content/apps/using-github-apps/requesting-a-github-app-from-your-organization-owner.md @@ -7,12 +7,9 @@ versions: shortTitle: Request for org --- -{% note %} - -**Note**: Currently, you can only request a {% data variables.product.prodname_github_app %} from your organization owner when installing the {% data variables.product.prodname_github_app %} directly from the {% data variables.product.prodname_github_app %} owner, not when installing a {% data variables.product.prodname_github_app %} from {% data variables.product.prodname_marketplace %}. - -If you find a {% data variables.product.prodname_github_app %} on {% data variables.product.prodname_marketplace %} that you want your organization owner to install, you must make the request from the {% data variables.product.prodname_github_app %}'s public installation page. The URL for a {% data variables.product.prodname_github_app %} public installation page is `https://github.com/apps/APP-NAME/installations/new`, where `APP-NAME` is the name of the {% data variables.product.prodname_github_app %}. - -{% endnote %} +> [!NOTE] +> Currently, you can only request a {% data variables.product.prodname_github_app %} from your organization owner when installing the {% data variables.product.prodname_github_app %} directly from the {% data variables.product.prodname_github_app %} owner, not when installing a {% data variables.product.prodname_github_app %} from {% data variables.product.prodname_marketplace %}. +> +> If you find a {% data variables.product.prodname_github_app %} on {% data variables.product.prodname_marketplace %} that you want your organization owner to install, you must make the request from the {% data variables.product.prodname_github_app %}'s public installation page. The URL for a {% data variables.product.prodname_github_app %} public installation page is `https://github.com/apps/APP-NAME/installations/new`, where `APP-NAME` is the name of the {% data variables.product.prodname_github_app %}. Organization members can send a request for their organization owner to install a {% data variables.product.prodname_github_app %} on the organization. To do so, follow the steps outlined in "[AUTOTITLE](/apps/using-github-apps/installing-a-github-app-from-a-third-party#installing-a-github-app)." If you don't have permission to install the {% data variables.product.prodname_github_app %} on the organization, {% data variables.product.company_short %} will send an email to the organization owner to notify them of the request. The organization owner can modify the repositories that you selected and choose whether to install the {% data variables.product.prodname_github_app %}. diff --git a/content/apps/using-github-apps/reviewing-and-modifying-installed-github-apps.md b/content/apps/using-github-apps/reviewing-and-modifying-installed-github-apps.md index 152eba9b4ba0..b3707cfda5f9 100644 --- a/content/apps/using-github-apps/reviewing-and-modifying-installed-github-apps.md +++ b/content/apps/using-github-apps/reviewing-and-modifying-installed-github-apps.md @@ -34,11 +34,8 @@ In addition to reviewing {% data variables.product.prodname_github_apps %} that * For a repository where a {% data variables.product.prodname_github_app %} was granted access: - {% note %} - - **Note**: In the following steps, you will be taken to the account settings for the organization or personal account where the {% data variables.product.prodname_github_app %} is installed. The settings will affect all repositories where the app is installed under that account, not just the repository where you navigated from. - - {% endnote %} + > [!NOTE] + > In the following steps, you will be taken to the account settings for the organization or personal account where the {% data variables.product.prodname_github_app %} is installed. The settings will affect all repositories where the app is installed under that account, not just the repository where you navigated from. 1. Navigate to the main page of the organization or repository. 1. Click {% octicon "gear" aria-label="The Settings gear" %} **Settings**. diff --git a/content/authentication/authenticating-with-a-passkey/managing-your-passkeys.md b/content/authentication/authenticating-with-a-passkey/managing-your-passkeys.md index 39b597a6ed1d..fb28e0430f25 100644 --- a/content/authentication/authenticating-with-a-passkey/managing-your-passkeys.md +++ b/content/authentication/authenticating-with-a-passkey/managing-your-passkeys.md @@ -24,14 +24,9 @@ For information on how to remove a passkey from your account, see "[Removing a p ## Upgrading an existing security key to a passkey -{% note %} - -**Notes:** - -* Platform support for upgrading security keys is inconsistent, so if you're seeing failures from your operating system or browser when trying to register an existing credential, we suggest that you remove and re-register the security key. -* If you have used a security key recently and it's eligible for an upgrade, an upgrade button will be shown next to the security key in the settings menu. You can use the button to trigger the upgrade flow. You can also attempt to upgrade other keys by registering them as a passkey, even if the upgrade button isn't shown. - -{% endnote %} +> [!NOTE] +> * Platform support for upgrading security keys is inconsistent, so if you're seeing failures from your operating system or browser when trying to register an existing credential, we suggest that you remove and re-register the security key. +> * If you have used a security key recently and it's eligible for an upgrade, an upgrade button will be shown next to the security key in the settings menu. You can use the button to trigger the upgrade flow. You can also attempt to upgrade other keys by registering them as a passkey, even if the upgrade button isn't shown. Before starting the upgrade procedure, make sure that you are using the device that's linked to the existing security key. Then, when you click **Add a passkey** in your account settings, {% data variables.product.company_short %} will automatically bump you into the "Upgrade to a passkey" flow. diff --git a/content/authentication/authenticating-with-saml-single-sign-on/authorizing-an-ssh-key-for-use-with-saml-single-sign-on.md b/content/authentication/authenticating-with-saml-single-sign-on/authorizing-an-ssh-key-for-use-with-saml-single-sign-on.md index 962b32513efe..804d284ba126 100644 --- a/content/authentication/authenticating-with-saml-single-sign-on/authorizing-an-ssh-key-for-use-with-saml-single-sign-on.md +++ b/content/authentication/authenticating-with-saml-single-sign-on/authorizing-an-ssh-key-for-use-with-saml-single-sign-on.md @@ -21,11 +21,8 @@ You can authorize an existing SSH key, or create a new SSH key and then authoriz {% data reusables.saml.authorized-creds-info %} -{% note %} - -**Note:** If your SSH key authorization is revoked by an organization, you will not be able to reauthorize the same key. You will need to create a new SSH key and authorize it. For more information about creating a new SSH key, see "[AUTOTITLE](/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent)." - -{% endnote %} +> [!NOTE] +> If your SSH key authorization is revoked by an organization, you will not be able to reauthorize the same key. You will need to create a new SSH key and authorize it. For more information about creating a new SSH key, see "[AUTOTITLE](/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent)." You do not need to authorize SSH certificates signed by your organization's SSH certificate authority (CA). diff --git a/content/authentication/authenticating-with-saml-single-sign-on/viewing-and-managing-your-active-saml-sessions.md b/content/authentication/authenticating-with-saml-single-sign-on/viewing-and-managing-your-active-saml-sessions.md index 4f9d359ceaec..c7e4b31fafef 100644 --- a/content/authentication/authenticating-with-saml-single-sign-on/viewing-and-managing-your-active-saml-sessions.md +++ b/content/authentication/authenticating-with-saml-single-sign-on/viewing-and-managing-your-active-saml-sessions.md @@ -21,11 +21,8 @@ You can view a list of devices that have logged into your account, and revoke an 1. To see the session details, next to the session, click **See more**. 1. To revoke a session, in the session details, click **Revoke SAML**. - {% note %} - - **Note:** When you revoke a session, you remove your SAML authentication to that organization. To access the organization again, you will need to single sign-on through your identity provider. For more information, see "[AUTOTITLE](/authentication/authenticating-with-saml-single-sign-on/about-authentication-with-saml-single-sign-on)." - - {% endnote %} + > [!NOTE] + > When you revoke a session, you remove your SAML authentication to that organization. To access the organization again, you will need to single sign-on through your identity provider. For more information, see "[AUTOTITLE](/authentication/authenticating-with-saml-single-sign-on/about-authentication-with-saml-single-sign-on)." ## Further reading diff --git a/content/authentication/connecting-to-github-with-ssh/checking-for-existing-ssh-keys.md b/content/authentication/connecting-to-github-with-ssh/checking-for-existing-ssh-keys.md index 1dbd51dff41c..4e53697022e5 100644 --- a/content/authentication/connecting-to-github-with-ssh/checking-for-existing-ssh-keys.md +++ b/content/authentication/connecting-to-github-with-ssh/checking-for-existing-ssh-keys.md @@ -39,11 +39,8 @@ Before you generate a new SSH key, you should check your local machine for exist * _id_ecdsa.pub_ * _id_ed25519.pub_ - {% tip %} - - **Tip**: If you receive an error that _~/.ssh_ doesn't exist, you do not have an existing SSH key pair in the default location. You can create a new SSH key pair in the next step. - - {% endtip %} + > [!TIP] + > If you receive an error that _~/.ssh_ doesn't exist, you do not have an existing SSH key pair in the default location. You can create a new SSH key pair in the next step. 1. Either generate a new SSH key or upload an existing key. * If you don't have a supported public and private key pair, or don't wish to use any that are available, generate a new SSH key. diff --git a/content/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent.md b/content/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent.md index 4acead2314aa..f35c4710b060 100644 --- a/content/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent.md +++ b/content/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent.md @@ -45,15 +45,12 @@ If you are a site administrator for {% data variables.location.product_location_ ssh-keygen -t ed25519 -C "your_email@example.com" ``` - {% note %} - - **Note:** If you are using a legacy system that doesn't support the Ed25519 algorithm, use: - - ```shell - ssh-keygen -t rsa -b 4096 -C "your_email@example.com" - ``` - - {% endnote %} + > [!NOTE] + > If you are using a legacy system that doesn't support the Ed25519 algorithm, use: + > + > ```shell + > ssh-keygen -t rsa -b 4096 -C "your_email@example.com" + > ``` This creates a new SSH key, using the provided email as a label. @@ -126,20 +123,14 @@ Before adding a new SSH key to the ssh-agent to manage your keys, you should hav IdentityFile ~/.ssh/id_ed25519 ``` - {% note %} - - **Notes:** - - * If you chose not to add a passphrase to your key, you should omit the `UseKeychain` line. - - * If you see a `Bad configuration option: usekeychain` error, add an additional line to the configuration's' `Host *.{% ifversion ghes %}HOSTNAME{% else %}github.com{% endif %}` section. - - ```text copy - Host {% ifversion ghes %}HOSTNAME{% else %}github.com{% endif %} - IgnoreUnknown UseKeychain - ``` - - {% endnote %} + > [!NOTE] + > * If you chose not to add a passphrase to your key, you should omit the `UseKeychain` line. + > * If you see a `Bad configuration option: usekeychain` error, add an additional line to the configuration's' `Host *.{% ifversion ghes %}HOSTNAME{% else %}github.com{% endif %}` section. + > + > ```text copy + > Host {% ifversion ghes %}HOSTNAME{% else %}github.com{% endif %} + > IgnoreUnknown UseKeychain + > ``` 1. Add your SSH private key to the ssh-agent and store your passphrase in the keychain. {% data reusables.ssh.add-ssh-key-to-ssh-agent %} @@ -223,15 +214,12 @@ If you are using macOS or Linux, you may need to update your SSH client or insta {% endlinux %} - {% note %} - - **Note:** If the command fails and you receive the error `invalid format` or `feature not supported,` you may be using a hardware security key that does not support the Ed25519 algorithm. Enter the following command instead. - - ```shell - ssh-keygen -t ecdsa-sk -C "your_email@example.com" - ``` - - {% endnote %} + > [!NOTE] + > If the command fails and you receive the error `invalid format` or `feature not supported,` you may be using a hardware security key that does not support the Ed25519 algorithm. Enter the following command instead. + > + > ```shell + > ssh-keygen -t ecdsa-sk -C "your_email@example.com" + > ``` 1. When you are prompted, touch the button on your hardware security key. 1. When you are prompted to "Enter a file in which to save the key," press Enter to accept the default file location. diff --git a/content/authentication/connecting-to-github-with-ssh/managing-deploy-keys.md b/content/authentication/connecting-to-github-with-ssh/managing-deploy-keys.md index d1f0d655d40d..59e336391be4 100644 --- a/content/authentication/connecting-to-github-with-ssh/managing-deploy-keys.md +++ b/content/authentication/connecting-to-github-with-ssh/managing-deploy-keys.md @@ -167,15 +167,12 @@ If your server needs to access multiple repositories, you can create a new accou {% ifversion fpt or ghec %} -{% tip %} - -**Tip:** Our [terms of service][tos] state: - -> _Accounts registered by "bots" or other automated methods are not permitted._ - -This means that you cannot automate the creation of accounts. But if you want to create a single machine user for automating tasks such as deploy scripts in your project or organization, that is totally cool. - -{% endtip %} +> [!TIP] +> Our [terms of service][tos] state: +> +> > _Accounts registered by "bots" or other automated methods are not permitted._ +> +> This means that you cannot automate the creation of accounts. But if you want to create a single machine user for automating tasks such as deploy scripts in your project or organization, that is totally cool. {% endif %} diff --git a/content/authentication/connecting-to-github-with-ssh/testing-your-ssh-connection.md b/content/authentication/connecting-to-github-with-ssh/testing-your-ssh-connection.md index 4a5108d8de02..b4bb6b5e89c8 100644 --- a/content/authentication/connecting-to-github-with-ssh/testing-your-ssh-connection.md +++ b/content/authentication/connecting-to-github-with-ssh/testing-your-ssh-connection.md @@ -65,10 +65,7 @@ You'll need to authenticate this action using your password, which is the SSH ke {% endlinux %} - {% note %} - - **Note:** The remote command should exit with code 1. - - {% endnote %} + > [!NOTE] + > The remote command should exit with code 1. 1. Verify that the resulting message contains your username. If you receive a "permission denied" message, see "[AUTOTITLE](/authentication/troubleshooting-ssh/error-permission-denied-publickey)." diff --git a/content/authentication/connecting-to-github-with-ssh/using-ssh-agent-forwarding.md b/content/authentication/connecting-to-github-with-ssh/using-ssh-agent-forwarding.md index 7430f64bc87e..e0988ff8b25d 100644 --- a/content/authentication/connecting-to-github-with-ssh/using-ssh-agent-forwarding.md +++ b/content/authentication/connecting-to-github-with-ssh/using-ssh-agent-forwarding.md @@ -45,11 +45,8 @@ We're off to a great start. Let's set up SSH to allow agent forwarding to your s Host example.com ForwardAgent yes -{% warning %} - -**Warning:** You may be tempted to use a wildcard like `Host *` to just apply this setting to all SSH connections. That's not really a good idea, as you'd be sharing your local SSH keys with _every_ server you SSH into. They won't have direct access to the keys, but they will be able to use them _as you_ while the connection is established. **You should only add servers you trust and that you intend to use with agent forwarding.** - -{% endwarning %} +> [!WARNING] +> You may be tempted to use a wildcard like `Host *` to just apply this setting to all SSH connections. That's not really a good idea, as you'd be sharing your local SSH keys with _every_ server you SSH into. They won't have direct access to the keys, but they will be able to use them _as you_ while the connection is established. **You should only add servers you trust and that you intend to use with agent forwarding.** ## Testing SSH agent forwarding diff --git a/content/authentication/connecting-to-github-with-ssh/working-with-ssh-key-passphrases.md b/content/authentication/connecting-to-github-with-ssh/working-with-ssh-key-passphrases.md index aa481da8395b..636de271b983 100644 --- a/content/authentication/connecting-to-github-with-ssh/working-with-ssh-key-passphrases.md +++ b/content/authentication/connecting-to-github-with-ssh/working-with-ssh-key-passphrases.md @@ -67,11 +67,8 @@ unset env If your private key is not stored in one of the default locations (like `~/.ssh/id_rsa`), you'll need to tell your SSH authentication agent where to find it. To add your key to ssh-agent, type `ssh-add ~/path/to/my_key`. For more information, see "[AUTOTITLE](/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent)" -{% tip %} - -**Tip:** If you want `ssh-agent` to forget your key after some time, you can configure it to do so by running `ssh-add -t `. - -{% endtip %} +> [!TIP] +> If you want `ssh-agent` to forget your key after some time, you can configure it to do so by running `ssh-add -t `. Now, when you first run Git Bash, you are prompted for your passphrase: diff --git a/content/authentication/keeping-your-account-and-data-secure/about-authentication-to-github.md b/content/authentication/keeping-your-account-and-data-secure/about-authentication-to-github.md index 16d775747322..fcfa4480af2a 100644 --- a/content/authentication/keeping-your-account-and-data-secure/about-authentication-to-github.md +++ b/content/authentication/keeping-your-account-and-data-secure/about-authentication-to-github.md @@ -58,11 +58,10 @@ If you need to use multiple accounts on {% data variables.location.product_locat * In addition to authentication with a TOTP application{% ifversion fpt or ghec %} or a text message{% endif %}, you can optionally add an alternative method of authentication with {% ifversion fpt or ghec %}{% data variables.product.prodname_mobile %} or{% endif %} a security key using WebAuthn. For more information, see {% ifversion fpt or ghec %}"[AUTOTITLE](/authentication/securing-your-account-with-two-factor-authentication-2fa/configuring-two-factor-authentication#configuring-two-factor-authentication-using-github-mobile)" and {% endif %}"[AUTOTITLE](/authentication/securing-your-account-with-two-factor-authentication-2fa/configuring-two-factor-authentication#configuring-two-factor-authentication-using-a-security-key)." {% ifversion fpt or ghec %} - {% note %} - **Note:** {% data reusables.two_fa.unlink-email-address %} + > [!NOTE] + > {% data reusables.two_fa.unlink-email-address %} - {% endnote %} {% endif %}{% ifversion passkeys %} * **Passkey** * You can add a passkey to your account to enable a secure, passwordless login. Passkeys satisfy both password and 2FA requirements, so you can complete your sign in with a single step. See "[AUTOTITLE](/authentication/authenticating-with-a-passkey/about-passkeys)."{% endif %} diff --git a/content/authentication/keeping-your-account-and-data-secure/about-githubs-ip-addresses.md b/content/authentication/keeping-your-account-and-data-secure/about-githubs-ip-addresses.md index 6c98ad6fefef..376741ee4bce 100644 --- a/content/authentication/keeping-your-account-and-data-secure/about-githubs-ip-addresses.md +++ b/content/authentication/keeping-your-account-and-data-secure/about-githubs-ip-addresses.md @@ -27,11 +27,8 @@ If you access {% data variables.product.github %} on a subdomain of {% data vari {% endif %} -{% note %} - -**Note:** The list of {% data variables.product.prodname_dotcom %} IP addresses returned by the Meta API is not intended to be an exhaustive list. For example, IP addresses for some {% data variables.product.prodname_dotcom %} services might not be listed, such as LFS or {% data variables.product.prodname_registry %}. - -{% endnote %} +> [!NOTE] +> The list of {% data variables.product.prodname_dotcom %} IP addresses returned by the Meta API is not intended to be an exhaustive list. For example, IP addresses for some {% data variables.product.prodname_dotcom %} services might not be listed, such as LFS or {% data variables.product.prodname_registry %}. These IP addresses are used by {% data variables.product.prodname_dotcom %} to serve our content, deliver webhooks, and perform hosted {% data variables.product.prodname_actions %} builds. diff --git a/content/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens.md b/content/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens.md index ae005010cb40..b05d59632b51 100644 --- a/content/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens.md +++ b/content/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens.md @@ -20,11 +20,8 @@ topics: - Access management --- -{% warning %} - -**Warning**: Treat your access tokens like passwords. For more information, see "[Keeping your {% data variables.product.pat_generic %}s secure](#keeping-your-personal-access-tokens-secure)." - -{% endwarning %} +> [!WARNING] +> Treat your access tokens like passwords. For more information, see "[Keeping your {% data variables.product.pat_generic %}s secure](#keeping-your-personal-access-tokens-secure)." ## About {% data variables.product.pat_generic %}s @@ -76,11 +73,8 @@ For more information about best practices, see "[AUTOTITLE](/rest/overview/keepi ## Creating a {% data variables.product.pat_v2 %} -{% note %} - -**Note**: {% data reusables.user-settings.pat-v2-beta %} - -{% endnote %} +> [!NOTE] +> {% data reusables.user-settings.pat-v2-beta %} {% ifversion fpt or ghec %}1. [Verify your email address](/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/verifying-your-email-address), if it hasn't been verified yet.{% endif %} {% data reusables.user-settings.access_settings %} @@ -104,17 +98,11 @@ If you selected an organization as the resource owner and the organization requi ## Creating a {% data variables.product.pat_v1 %} -{% note %} - -**Note**: Organization owners can restrict the access of {% data variables.product.pat_v1 %} to their organization. If you try to use a {% data variables.product.pat_v1 %} to access resources in an organization that has disabled {% data variables.product.pat_v1 %} access, your request will fail with a 403 response. Instead, you must use a {% data variables.product.prodname_github_app %}, {% data variables.product.prodname_oauth_app %}, or {% data variables.product.pat_v2 %}. - -{% endnote %} - -{% warning %} - -**Note**: Your {% data variables.product.pat_v1 %} can access every repository that you can access. {% data variables.product.company_short %} recommends that you use {% data variables.product.pat_v2 %}s instead, which you can restrict to specific repositories. {% data variables.product.pat_v2_caps %}s also enable you to specify fine-grained permissions instead of broad scopes. +> [!NOTE] +> Organization owners can restrict the access of {% data variables.product.pat_v1 %} to their organization. If you try to use a {% data variables.product.pat_v1 %} to access resources in an organization that has disabled {% data variables.product.pat_v1 %} access, your request will fail with a 403 response. Instead, you must use a {% data variables.product.prodname_github_app %}, {% data variables.product.prodname_oauth_app %}, or {% data variables.product.pat_v2 %}. -{% endwarning %} +> [!WARNING] +> Your {% data variables.product.pat_v1 %} can access every repository that you can access. {% data variables.product.company_short %} recommends that you use {% data variables.product.pat_v2 %}s instead, which you can restrict to specific repositories. {% data variables.product.pat_v2_caps %}s also enable you to specify fine-grained permissions instead of broad scopes. {% ifversion fpt or ghec %}1. [Verify your email address](/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/verifying-your-email-address), if it hasn't been verified yet.{% endif %} {% data reusables.user-settings.access_settings %} diff --git a/content/authentication/keeping-your-account-and-data-secure/reviewing-your-ssh-keys.md b/content/authentication/keeping-your-account-and-data-secure/reviewing-your-ssh-keys.md index 952a864efa6e..962434f5bf63 100644 --- a/content/authentication/keeping-your-account-and-data-secure/reviewing-your-ssh-keys.md +++ b/content/authentication/keeping-your-account-and-data-secure/reviewing-your-ssh-keys.md @@ -23,11 +23,8 @@ You can delete unauthorized (or possibly compromised) SSH keys to ensure that an {% data reusables.user-settings.ssh %} 1. Under "SSH keys", take note of the SSH keys associated with your account. For those that you don't recognize, or that are out-of-date, click **Delete**. If there are valid SSH keys you'd like to keep, click **Approve**. - {% note %} - - **Note:** If you're auditing your SSH keys due to an unsuccessful Git operation, the unverified key that caused the [SSH key audit error](/articles/error-we-re-doing-an-ssh-key-audit) will be highlighted in the list of SSH keys. - - {% endnote %} + > [!NOTE] + > If you're auditing your SSH keys due to an unsuccessful Git operation, the unverified key that caused the [SSH key audit error](/articles/error-we-re-doing-an-ssh-key-audit) will be highlighted in the list of SSH keys. 1. Open Terminal. @@ -50,11 +47,8 @@ You can delete unauthorized (or possibly compromised) SSH keys to ensure that an {% data reusables.user-settings.ssh %} 1. Under "SSH keys", take note of the SSH keys associated with your account. For those that you don't recognize, or that are out-of-date, click **Delete**. If there are valid SSH keys you'd like to keep, click **Approve**. - {% note %} - - **Note:** If you're auditing your SSH keys due to an unsuccessful Git operation, the unverified key that caused the [SSH key audit error](/articles/error-we-re-doing-an-ssh-key-audit) will be highlighted in the list of SSH keys. - - {% endnote %} + > [!NOTE] + > If you're auditing your SSH keys due to an unsuccessful Git operation, the unverified key that caused the [SSH key audit error](/articles/error-we-re-doing-an-ssh-key-audit) will be highlighted in the list of SSH keys. 1. Open Git Bash. @@ -81,11 +75,8 @@ You can delete unauthorized (or possibly compromised) SSH keys to ensure that an {% data reusables.user-settings.ssh %} 1. Under "SSH keys", take note of the SSH keys associated with your account. For those that you don't recognize, or that are out-of-date, click **Delete**. If there are valid SSH keys you'd like to keep, click **Approve**. - {% note %} - - **Note:** If you're auditing your SSH keys due to an unsuccessful Git operation, the unverified key that caused the [SSH key audit error](/articles/error-we-re-doing-an-ssh-key-audit) will be highlighted in the list of SSH keys. - - {% endnote %} + > [!NOTE] + > If you're auditing your SSH keys due to an unsuccessful Git operation, the unverified key that caused the [SSH key audit error](/articles/error-we-re-doing-an-ssh-key-audit) will be highlighted in the list of SSH keys. 1. Open Terminal. @@ -102,8 +93,5 @@ You can delete unauthorized (or possibly compromised) SSH keys to ensure that an {% endlinux %} -{% warning %} - -**Warning**: If you see an SSH key you're not familiar with on {% data variables.product.product_name %}, delete it immediately and contact {% data variables.contact.contact_support %} for further help. An unidentified public key may indicate a possible security concern. - -{% endwarning %} +> [!WARNING] +> If you see an SSH key you're not familiar with on {% data variables.product.product_name %}, delete it immediately and contact {% data variables.contact.contact_support %} for further help. An unidentified public key may indicate a possible security concern. diff --git a/content/authentication/keeping-your-account-and-data-secure/security-log-events.md b/content/authentication/keeping-your-account-and-data-secure/security-log-events.md index c32cda4c9b21..3fa0218da763 100644 --- a/content/authentication/keeping-your-account-and-data-secure/security-log-events.md +++ b/content/authentication/keeping-your-account-and-data-secure/security-log-events.md @@ -11,13 +11,10 @@ topics: autogenerated: audit-logs --- -{% note %} - -{% ifversion ghes %}**Notes**: -* This article contains the events available in the latest version of {% data variables.product.prodname_ghe_server %}. Some of the events may not be available in previous versions. --{% else %}**Note:**{% endif %} This article contains the events that may appear in your user account's security log. For the events that can appear in an organization's audit log{% ifversion ghec or ghes %} or the audit log for an enterprise{% endif %}, see "[AUTOTITLE](/organizations/keeping-your-organization-secure/managing-security-settings-for-your-organization/audit-log-events-for-your-organization){% ifversion ghec or ghes %}" and "[AUTOTITLE](/admin/monitoring-activity-in-your-enterprise/reviewing-audit-logs-for-your-enterprise/audit-log-events-for-your-enterprise)."{% else %}."{% endif %} - -{% endnote %} +> [!NOTE] +{% ifversion ghes %} +> * This article contains the events available in the latest version of {% data variables.product.prodname_ghe_server %}. Some of the events may not be available in previous versions. +> *{% else %}>{% endif %} This article contains the events that may appear in your user account's security log. For the events that can appear in an organization's audit log{% ifversion ghec or ghes %} or the audit log for an enterprise{% endif %}, see "[AUTOTITLE](/organizations/keeping-your-organization-secure/managing-security-settings-for-your-organization/audit-log-events-for-your-organization){% ifversion ghec or ghes %}" and "[AUTOTITLE](/admin/monitoring-activity-in-your-enterprise/reviewing-audit-logs-for-your-enterprise/audit-log-events-for-your-enterprise)."{% else %}."{% endif %} ## About security log events diff --git a/content/authentication/keeping-your-account-and-data-secure/sudo-mode.md b/content/authentication/keeping-your-account-and-data-secure/sudo-mode.md index 3a91d89edece..529634af2058 100644 --- a/content/authentication/keeping-your-account-and-data-secure/sudo-mode.md +++ b/content/authentication/keeping-your-account-and-data-secure/sudo-mode.md @@ -27,20 +27,14 @@ After you authenticate to perform a sensitive action, your session is temporaril {% ifversion ghes %} -{% note %} - -**Note**: If {% data variables.location.product_location_enterprise %} uses an external authentication method like CAS or SAML SSO, you will not receive prompts to enter sudo mode. For more information, contact your site administrator. - -{% endnote %} +> [!NOTE] +> If {% data variables.location.product_location_enterprise %} uses an external authentication method like CAS or SAML SSO, you will not receive prompts to enter sudo mode. For more information, contact your site administrator. {% endif %} {% ifversion ghec %} -{% note %} - -**Note**: If your enterprise uses {% data variables.product.prodname_emus %}, only the setup user will receive prompts to enter sudo mode, as {% data variables.enterprise.prodname_managed_users %} don't have credentials stored on {% data variables.product.product_name %}. - -{% endnote %} +> [!NOTE] +> If your enterprise uses {% data variables.product.prodname_emus %}, only the setup user will receive prompts to enter sudo mode, as {% data variables.enterprise.prodname_managed_users %} don't have credentials stored on {% data variables.product.product_name %}. {% endif %} diff --git a/content/authentication/keeping-your-account-and-data-secure/switching-between-accounts.md b/content/authentication/keeping-your-account-and-data-secure/switching-between-accounts.md index aa392720ac0d..76a3dc6acb35 100644 --- a/content/authentication/keeping-your-account-and-data-secure/switching-between-accounts.md +++ b/content/authentication/keeping-your-account-and-data-secure/switching-between-accounts.md @@ -31,11 +31,8 @@ When you add a new account to the account switcher, both the account you are cur When you have added accounts to the account switcher, you can quickly change between them without always needing to reauthenticate. -{% note %} - -**Note:** The "{% octicon "arrow-switch" aria-hidden="true" %} Switch account" option will not be available if all sessions have expired. You can instead click on {% octicon "arrow-switch" aria-hidden="true" %} **See all accounts** in the menu to reauthenticate. - -{% endnote %} +> [!NOTE] +> The "{% octicon "arrow-switch" aria-hidden="true" %} Switch account" option will not be available if all sessions have expired. You can instead click on {% octicon "arrow-switch" aria-hidden="true" %} **See all accounts** in the menu to reauthenticate. 1. In the upper-right corner of any page, click your profile photo to open the menu. 1. In the menu, click {% octicon "arrow-switch" aria-hidden="true" %} **Switch account**. diff --git a/content/authentication/keeping-your-account-and-data-secure/token-expiration-and-revocation.md b/content/authentication/keeping-your-account-and-data-secure/token-expiration-and-revocation.md index 2b63dee86781..fa862465d515 100644 --- a/content/authentication/keeping-your-account-and-data-secure/token-expiration-and-revocation.md +++ b/content/authentication/keeping-your-account-and-data-secure/token-expiration-and-revocation.md @@ -17,11 +17,8 @@ When a token has expired or has been revoked, it can no longer be used to authen This article explains the possible reasons your {% data variables.product.product_name %} token might be revoked or expire. -{% note %} - -**Note:** When a {% data variables.product.pat_generic %} or OAuth token expires or is revoked, you may see an `oauth_authorization.destroy` action in your security log. For more information, see "[AUTOTITLE](/authentication/keeping-your-account-and-data-secure/reviewing-your-security-log)." - -{% endnote %} +> [!NOTE] +> When a {% data variables.product.pat_generic %} or OAuth token expires or is revoked, you may see an `oauth_authorization.destroy` action in your security log. For more information, see "[AUTOTITLE](/authentication/keeping-your-account-and-data-secure/reviewing-your-security-log)." ## Token revoked after reaching its expiration date diff --git a/content/authentication/keeping-your-account-and-data-secure/viewing-and-managing-your-sessions.md b/content/authentication/keeping-your-account-and-data-secure/viewing-and-managing-your-sessions.md index 67750dea72b4..fabdefc97282 100644 --- a/content/authentication/keeping-your-account-and-data-secure/viewing-and-managing-your-sessions.md +++ b/content/authentication/keeping-your-account-and-data-secure/viewing-and-managing-your-sessions.md @@ -22,11 +22,8 @@ You can view a list of devices that have logged into your account, and revoke an {% ifversion fpt or ghec %} 1. Optionally, to revoke a {% data variables.product.prodname_mobile %} session, go back to the Sessions overview page and click **Revoke** next to the device you want to revoke. - {% note %} - - **Note:** Revoking a mobile session signs you out of the {% data variables.product.prodname_mobile %} application on that device and removes it as a second-factor option. - - {% endnote %} + > [!NOTE] + > Revoking a mobile session signs you out of the {% data variables.product.prodname_mobile %} application on that device and removes it as a second-factor option. {% endif %} diff --git a/content/authentication/managing-commit-signature-verification/checking-for-existing-gpg-keys.md b/content/authentication/managing-commit-signature-verification/checking-for-existing-gpg-keys.md index b24c0a4457e7..ec0e4600e092 100644 --- a/content/authentication/managing-commit-signature-verification/checking-for-existing-gpg-keys.md +++ b/content/authentication/managing-commit-signature-verification/checking-for-existing-gpg-keys.md @@ -16,11 +16,8 @@ shortTitle: Existing GPG keys --- {% data reusables.gpg.supported-gpg-key-algorithms %} -{% note %} - -**Note:** GPG does not come installed by default on macOS or Windows. To install GPG command line tools, see [GnuPG's Download page](https://www.gnupg.org/download/). - -{% endnote %} +> [!NOTE] +> GPG does not come installed by default on macOS or Windows. To install GPG command line tools, see [GnuPG's Download page](https://www.gnupg.org/download/). {% data reusables.command_line.open_the_multi_os_terminal %} {% data reusables.gpg.list-keys-with-note %} diff --git a/content/authentication/managing-commit-signature-verification/generating-a-new-gpg-key.md b/content/authentication/managing-commit-signature-verification/generating-a-new-gpg-key.md index 5a9fc3a12b4b..a3c944e51e6f 100644 --- a/content/authentication/managing-commit-signature-verification/generating-a-new-gpg-key.md +++ b/content/authentication/managing-commit-signature-verification/generating-a-new-gpg-key.md @@ -17,11 +17,8 @@ topics: ## Generating a GPG key -{% note %} - -**Note:** Before generating a new GPG key, make sure you've verified your email address. If you haven't verified your email address, you won't be able to sign commits and tags with GPG.{% ifversion fpt or ghec %} For more information, see "[AUTOTITLE](/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/verifying-your-email-address)."{% endif %} - -{% endnote %} +> [!NOTE] +> Before generating a new GPG key, make sure you've verified your email address. If you haven't verified your email address, you won't be able to sign commits and tags with GPG.{% ifversion fpt or ghec %} For more information, see "[AUTOTITLE](/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/verifying-your-email-address)."{% endif %} 1. Download and install [the GPG command line tools](https://www.gnupg.org/download/) for your operating system. We generally recommend installing the latest version for your operating system. {% data reusables.command_line.open_the_multi_os_terminal %} @@ -44,11 +41,8 @@ topics: 1. Verify that your selections are correct. 1. Enter your user ID information. - {% note %} - - **Note:** When asked to enter your email address, ensure that you enter the verified email address for your GitHub account. {% data reusables.gpg.private-email %} {% ifversion fpt or ghec %} For more information, see "[AUTOTITLE](/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/verifying-your-email-address)" and "[AUTOTITLE](/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/setting-your-commit-email-address)."{% endif %} - - {% endnote %} + > [!NOTE] + > When asked to enter your email address, ensure that you enter the verified email address for your GitHub account. {% data reusables.gpg.private-email %} {% ifversion fpt or ghec %} For more information, see "[AUTOTITLE](/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/verifying-your-email-address)" and "[AUTOTITLE](/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/setting-your-commit-email-address)."{% endif %} 1. Type a secure passphrase. {% data reusables.gpg.list-keys-with-note %} diff --git a/content/authentication/managing-commit-signature-verification/signing-commits.md b/content/authentication/managing-commit-signature-verification/signing-commits.md index 20ee61f97d80..38749a0b0771 100644 --- a/content/authentication/managing-commit-signature-verification/signing-commits.md +++ b/content/authentication/managing-commit-signature-verification/signing-commits.md @@ -17,19 +17,14 @@ topics: --- {% data reusables.gpg.desktop-support-for-commit-signing %} -{% tip %} - -**Tips:** - -To configure your Git client to sign commits by default for a local repository, in Git versions 2.0.0 and above, run `git config commit.gpgsign true`. To sign all commits by default in any local repository on your computer, run `git config --global commit.gpgsign true`. - -To store your GPG key passphrase so you don't have to enter it every time you sign a commit, we recommend using the following tools: -* For Mac users, the [GPG Suite](https://gpgtools.org/) allows you to store your GPG key passphrase in the macOS Keychain. -* For Windows users, the [Gpg4win](https://www.gpg4win.org/) integrates with other Windows tools. - -You can also manually configure [gpg-agent](http://linux.die.net/man/1/gpg-agent) to save your GPG key passphrase, but this doesn't integrate with macOS Keychain like ssh-agent and requires more setup. - -{% endtip %} +> [!TIP] +> To configure your Git client to sign commits by default for a local repository, in Git versions 2.0.0 and above, run `git config commit.gpgsign true`. To sign all commits by default in any local repository on your computer, run `git config --global commit.gpgsign true`. +> +> To store your GPG key passphrase so you don't have to enter it every time you sign a commit, we recommend using the following tools: +> * For Mac users, the [GPG Suite](https://gpgtools.org/) allows you to store your GPG key passphrase in the macOS Keychain. +> * For Windows users, the [Gpg4win](https://www.gpg4win.org/) integrates with other Windows tools. +> +> You can also manually configure [gpg-agent](http://linux.die.net/man/1/gpg-agent) to save your GPG key passphrase, but this doesn't integrate with macOS Keychain like ssh-agent and requires more setup. If you have multiple keys or are attempting to sign commits or tags with a key that doesn't match your committer identity, you should [tell Git about your signing key](/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key). diff --git a/content/authentication/securing-your-account-with-two-factor-authentication-2fa/about-mandatory-two-factor-authentication.md b/content/authentication/securing-your-account-with-two-factor-authentication-2fa/about-mandatory-two-factor-authentication.md index 8cfc0bc03f45..3959040db48d 100644 --- a/content/authentication/securing-your-account-with-two-factor-authentication-2fa/about-mandatory-two-factor-authentication.md +++ b/content/authentication/securing-your-account-with-two-factor-authentication-2fa/about-mandatory-two-factor-authentication.md @@ -24,11 +24,8 @@ Your account is selected for mandatory 2FA if you have taken some action on {% d {% data variables.product.prodname_dotcom %} is continually assessing improvements to our account security features and 2FA requirements, so these criteria may change over time. -{% note %} - -**Note:** If your account has an education coupon active, it is exempt from mandatory 2FA. - -{% endnote %} +> [!NOTE] +> If your account has an education coupon active, it is exempt from mandatory 2FA. ### About mandatory 2FA for organizations and enterprises @@ -36,11 +33,8 @@ Mandatory 2FA is required by {% data variables.product.prodname_dotcom %} itself Your account's eligibility for mandatory 2FA **does not** impact the eligibility of other individuals. For example, if you are an organization owner, and your account is eligible for mandatory 2FA, that does not impact the eligibility of other accounts within your organization. -{% note %} - -**Note:** {% data variables.product.prodname_dotcom %} {% data variables.product.prodname_emus %} and on-premise {% data variables.product.prodname_ghe_server %} users are **not** required to enable 2FA. Mandatory 2FA enablement only applies to users with a password on {% data variables.product.prodname_dotcom_the_website %}. - -{% endnote %} +> [!NOTE] +> {% data variables.product.prodname_dotcom %} {% data variables.product.prodname_emus %} and on-premise {% data variables.product.prodname_ghe_server %} users are **not** required to enable 2FA. Mandatory 2FA enablement only applies to users with a password on {% data variables.product.prodname_dotcom_the_website %}. ## About failure to enable mandatory 2FA @@ -58,11 +52,8 @@ Currently, we don't support passkeys or security keys as primary 2FA methods sin * [About SAML SSO and mandatory 2FA](#about-saml-sso-and-mandatory-2fa) * [About email verification and mandatory 2FA](#about-email-verification-and-mandatory-2fa) -{% note %} - -**Note:** We recommend retaining cookies on {% data variables.product.prodname_dotcom_the_website %}. If you set your browser to wipe your cookies every day, you'll never have a verified device for account recovery purposes, as the [`_device_id` cookie](https://github.com/privacy/cookies) is used to securely prove you've used that device previously. For more information, see "[AUTOTITLE](/authentication/securing-your-account-with-two-factor-authentication-2fa/recovering-your-account-if-you-lose-your-2fa-credentials#authenticating-with-a-verified-device-ssh-token-or-personal-access-token)." - -{% endnote %} +> [!NOTE] +> We recommend retaining cookies on {% data variables.product.prodname_dotcom_the_website %}. If you set your browser to wipe your cookies every day, you'll never have a verified device for account recovery purposes, as the [`_device_id` cookie](https://github.com/privacy/cookies) is used to securely prove you've used that device previously. For more information, see "[AUTOTITLE](/authentication/securing-your-account-with-two-factor-authentication-2fa/recovering-your-account-if-you-lose-your-2fa-credentials#authenticating-with-a-verified-device-ssh-token-or-personal-access-token)." ### About TOTP apps and mandatory 2FA @@ -72,11 +63,8 @@ If you do not want to download an app on your mobile device, there are multiple You can also manually set up any app that generates a code compatible with RFC 6238. For more information on manually setting up a TOTP app, see "[AUTOTITLE](/authentication/securing-your-account-with-two-factor-authentication-2fa/configuring-two-factor-authentication#configuring-two-factor-authentication-using-a-totp-app)." For more information on RFC 6238, see [TOTP: Time-Based One-Time Password Algorithm](https://datatracker.ietf.org/doc/html/rfc6238) in the IETF documentation. -{% note %} - -**Note:** If you are using FreeOTP for 2FA, you may see a warning about weak cryptographic parameters. {% data variables.product.prodname_dotcom %} uses an 80 bit secret to ensure compatibility with older versions of Google Authenticator. 80 bits is lower than the 128 bits recommended by the HOTP RFC, but at this time we have no plans to change this and recommend ignoring this message. For more information, see [HOTP: An HMAC-Based One-Time Password Algorithm](https://www.ietf.org/rfc/rfc4226.txt) in the IETF documentation. - -{% endnote %} +> [!NOTE] +> If you are using FreeOTP for 2FA, you may see a warning about weak cryptographic parameters. {% data variables.product.prodname_dotcom %} uses an 80 bit secret to ensure compatibility with older versions of Google Authenticator. 80 bits is lower than the 128 bits recommended by the HOTP RFC, but at this time we have no plans to change this and recommend ignoring this message. For more information, see [HOTP: An HMAC-Based One-Time Password Algorithm](https://www.ietf.org/rfc/rfc4226.txt) in the IETF documentation. ### About SAML SSO and mandatory 2FA @@ -96,8 +84,5 @@ Unattended or shared access accounts in your organization, such as bots and serv If you have been selected for mandatory 2FA, that **does not** mean you have to provide {% data variables.product.prodname_dotcom %} with your phone number. You only have to provide your phone number if you use SMS for 2FA. Instead, we recommend configuring a TOTP app as your primary 2FA method. For more information, see "[AUTOTITLE](/authentication/securing-your-account-with-two-factor-authentication-2fa/configuring-two-factor-authentication#configuring-two-factor-authentication-using-a-totp-app)." -{% note %} - -**Note:** Your region may not be listed in the available SMS options. We monitor SMS delivery success rates on a per region basis, and disallow setup for regions that have poor delivery rates. If you don't see your region on the list, you must set up a TOTP app instead. For more information on supported regions for SMS, see "[AUTOTITLE](/authentication/securing-your-account-with-two-factor-authentication-2fa/countries-where-sms-authentication-is-supported)." - -{% endnote %} +> [!NOTE] +> Your region may not be listed in the available SMS options. We monitor SMS delivery success rates on a per region basis, and disallow setup for regions that have poor delivery rates. If you don't see your region on the list, you must set up a TOTP app instead. For more information on supported regions for SMS, see "[AUTOTITLE](/authentication/securing-your-account-with-two-factor-authentication-2fa/countries-where-sms-authentication-is-supported)." diff --git a/content/authentication/securing-your-account-with-two-factor-authentication-2fa/about-two-factor-authentication.md b/content/authentication/securing-your-account-with-two-factor-authentication-2fa/about-two-factor-authentication.md index b3b52fb9ec61..13b12e83d6e6 100644 --- a/content/authentication/securing-your-account-with-two-factor-authentication-2fa/about-two-factor-authentication.md +++ b/content/authentication/securing-your-account-with-two-factor-authentication-2fa/about-two-factor-authentication.md @@ -33,11 +33,10 @@ You can also use {% data variables.product.prodname_mobile %} for 2FA after conf You can also configure additional recovery methods in case you lose access to your two-factor authentication credentials. For more information on setting up 2FA, see "[AUTOTITLE](/authentication/securing-your-account-with-two-factor-authentication-2fa/configuring-two-factor-authentication)" and "[AUTOTITLE](/authentication/securing-your-account-with-two-factor-authentication-2fa/configuring-two-factor-authentication-recovery-methods)." {% ifversion fpt or ghec %} -{% note %} -**Note:** {% data reusables.two_fa.unlink-email-address %} +> [!NOTE] +> {% data reusables.two_fa.unlink-email-address %} -{% endnote %} {% endif %} We **strongly** urge you to enable 2FA for the safety of your account, not only on {% data variables.product.product_name %}, but on other websites and apps that support 2FA. You can enable 2FA to access {% data variables.product.product_name %} and {% data variables.product.prodname_desktop %}. @@ -54,11 +53,8 @@ For more information, see "[AUTOTITLE](/authentication/securing-your-account-wit {% ifversion fpt or ghec %} -{% warning %} - -**Warning**: {% data reusables.two_fa.support-may-not-help %} For more information, see "[AUTOTITLE](/authentication/securing-your-account-with-two-factor-authentication-2fa/recovering-your-account-if-you-lose-your-2fa-credentials)." - -{% endwarning %} +> [!WARNING] +> {% data reusables.two_fa.support-may-not-help %} For more information, see "[AUTOTITLE](/authentication/securing-your-account-with-two-factor-authentication-2fa/recovering-your-account-if-you-lose-your-2fa-credentials)." {% endif %} diff --git a/content/authentication/securing-your-account-with-two-factor-authentication-2fa/accessing-github-using-two-factor-authentication.md b/content/authentication/securing-your-account-with-two-factor-authentication-2fa/accessing-github-using-two-factor-authentication.md index cc6270fd1509..e8f391caedc0 100644 --- a/content/authentication/securing-your-account-with-two-factor-authentication-2fa/accessing-github-using-two-factor-authentication.md +++ b/content/authentication/securing-your-account-with-two-factor-authentication-2fa/accessing-github-using-two-factor-authentication.md @@ -70,11 +70,9 @@ If you have installed and signed in to {% data variables.product.prodname_mobile 1. Sign in to {% data variables.product.product_name %} with your browser, using your username and password. 1. {% data variables.product.product_name %} will send you a push notification to verify your sign in attempt. Opening the push notification or opening the {% data variables.product.prodname_mobile %} app will display a prompt, asking you to approve or reject this sign in attempt. - {% note %} - **Note**: This prompt may require you to enter a two-digit number displayed within the browser you are signing in to. - - {% endnote %} + > [!NOTE] + > This prompt may require you to enter a two-digit number displayed within the browser you are signing in to. * Upon approving the login attempt using {% data variables.product.prodname_mobile %}, your browser will complete the sign in attempt automatically. * Rejecting the sign in attempt will prevent the authentication from finishing. For more information, see "[AUTOTITLE](/authentication/keeping-your-account-and-data-secure)." @@ -119,11 +117,10 @@ When you access a repository via Subversion, you must provide a {% data variable If you lose access to your two-factor authentication credentials, you can use your recovery codes or another recovery method (if you've set one up) to regain access to your account. For more information, see "[AUTOTITLE](/authentication/securing-your-account-with-two-factor-authentication-2fa/recovering-your-account-if-you-lose-your-2fa-credentials)." {% ifversion fpt or ghec %} -{% note %} -**Note:** {% data reusables.two_fa.unlink-email-address %} +> [!NOTE] +> {% data reusables.two_fa.unlink-email-address %} -{% endnote %} {% endif %} If your authentication fails several times, you may wish to synchronize your phone's clock with your mobile provider. Often, this involves checking the "Set automatically" option on your phone's clock, rather than providing your own time zone. diff --git a/content/authentication/securing-your-account-with-two-factor-authentication-2fa/changing-your-two-factor-authentication-method.md b/content/authentication/securing-your-account-with-two-factor-authentication-2fa/changing-your-two-factor-authentication-method.md index 722044e9fefe..3f2dec5e1d2d 100644 --- a/content/authentication/securing-your-account-with-two-factor-authentication-2fa/changing-your-two-factor-authentication-method.md +++ b/content/authentication/securing-your-account-with-two-factor-authentication-2fa/changing-your-two-factor-authentication-method.md @@ -34,11 +34,8 @@ You can change the time-based one-time password (TOTP) application you use to ge {% data reusables.two_fa.enable-totp-app-method %} {% data reusables.two_fa.save-2fa-method-when-editing %} -{% warning %} - -**Warning:** Changes to an existing 2FA method will only take effect after you have provided a valid code from the new method and clicked **Save**. Only replace the existing 2FA method on your device (e.g. the {% data variables.product.prodname_dotcom %} entry in your TOTP app) after your new method is saved to your {% data variables.product.prodname_dotcom %} account completely. - -{% endwarning %} +> [!WARNING] +> Changes to an existing 2FA method will only take effect after you have provided a valid code from the new method and clicked **Save**. Only replace the existing 2FA method on your device (e.g. the {% data variables.product.prodname_dotcom %} entry in your TOTP app) after your new method is saved to your {% data variables.product.prodname_dotcom %} account completely. {% data reusables.two_fa.manual-totp-app-setup %} diff --git a/content/authentication/securing-your-account-with-two-factor-authentication-2fa/configuring-two-factor-authentication.md b/content/authentication/securing-your-account-with-two-factor-authentication-2fa/configuring-two-factor-authentication.md index 9331c16cf5c7..162159172a04 100644 --- a/content/authentication/securing-your-account-with-two-factor-authentication-2fa/configuring-two-factor-authentication.md +++ b/content/authentication/securing-your-account-with-two-factor-authentication-2fa/configuring-two-factor-authentication.md @@ -36,31 +36,23 @@ If you're a member of an {% data variables.enterprise.prodname_emu_enterprise %} {% endif %} {% endif %} -{% warning %} - -**Warning:** -* If you're a member{% ifversion fpt or ghec %}, billing manager,{% endif %} or outside collaborator to a private repository of an organization that requires two-factor authentication, you must leave the organization before you can disable 2FA. -* If you disable 2FA, you will automatically lose access to the organization and any private forks you have of the organization's private repositories. To regain access to the organization and your forks, re-enable two-factor authentication and contact an organization owner. - -{% endwarning %} +> [!WARNING] +> * If you're a member{% ifversion fpt or ghec %}, billing manager,{% endif %} or outside collaborator to a private repository of an organization that requires two-factor authentication, you must leave the organization before you can disable 2FA. +> * If you disable 2FA, you will automatically lose access to the organization and any private forks you have of the organization's private repositories. To regain access to the organization and your forks, re-enable two-factor authentication and contact an organization owner. {% ifversion 2fa-reconfiguration-inline-update %} -{% note %} -**Note:** You can reconfigure your 2FA settings without disabling 2FA entirely, allowing you to keep both your recovery codes and your membership in organizations that require 2FA. +> [!NOTE] +> You can reconfigure your 2FA settings without disabling 2FA entirely, allowing you to keep both your recovery codes and your membership in organizations that require 2FA. -{% endnote %} {% endif %} ## Configuring two-factor authentication using a TOTP app A time-based one-time password (TOTP) application automatically generates an authentication code that changes after a certain period of time. These apps can be downloaded to your phone or desktop. We recommend using cloud-based TOTP apps. {% data variables.product.prodname_dotcom %} is app-agnostic when it comes to TOTP apps, so you have the freedom to choose any TOTP app you prefer. Just search for `TOTP app` in your browser to find various options. You can also refine your search by adding keywords like `free` or `open source` to match your preferences. -{% tip %} - -**Tip**: To configure authentication via TOTP on multiple devices, during setup, scan the QR code using each device at the same time or save the "setup key", which is the TOTP secret. If 2FA is already enabled and you want to add another device, you must re-configure your TOTP app from your security settings. - -{% endtip %} +> [!TIP] +> To configure authentication via TOTP on multiple devices, during setup, scan the QR code using each device at the same time or save the "setup key", which is the TOTP secret. If 2FA is already enabled and you want to add another device, you must re-configure your TOTP app from your security settings. 1. Download a TOTP app of your choice to your phone or desktop. {% data reusables.user-settings.access_settings %} @@ -97,11 +89,8 @@ If you're unable to configure a TOTP app, you can also register your phone numbe {% data reusables.passkeys.about-passkeys %} See "[AUTOTITLE](/authentication/authenticating-with-a-passkey/about-passkeys)." -{% note %} - -**Note:** Platform authenticators like Windows Hello, Face ID, or Touch ID can be registered as a passkey instead. - -{% endnote %} +> [!NOTE] +> Platform authenticators like Windows Hello, Face ID, or Touch ID can be registered as a passkey instead. 1. You must have already configured 2FA via a TOTP mobile app{% ifversion fpt or ghec %} or via SMS{% endif %}. {% data reusables.passkeys.adding-a-passkey %} diff --git a/content/authentication/securing-your-account-with-two-factor-authentication-2fa/disabling-two-factor-authentication-for-your-personal-account.md b/content/authentication/securing-your-account-with-two-factor-authentication-2fa/disabling-two-factor-authentication-for-your-personal-account.md index 00bf0a6e41c6..1afa0c626a9f 100644 --- a/content/authentication/securing-your-account-with-two-factor-authentication-2fa/disabling-two-factor-authentication-for-your-personal-account.md +++ b/content/authentication/securing-your-account-with-two-factor-authentication-2fa/disabling-two-factor-authentication-for-your-personal-account.md @@ -18,11 +18,8 @@ shortTitle: Disable 2FA {% data reusables.two_fa.mandatory-2fa-contributors-2023 %} {% endif %} -{% warning %} - -**Warning:** If you're a member{% ifversion fpt or ghec %}, billing manager,{% endif %} or outside collaborator to a public repository of an organization that requires two-factor authentication and you disable 2FA, you'll be automatically removed from the organization, and you'll lose your access to their repositories. To regain access to the organization, re-enable two-factor authentication and contact an organization owner. - -{% endwarning %} +> [!WARNING] +> If you're a member{% ifversion fpt or ghec %}, billing manager,{% endif %} or outside collaborator to a public repository of an organization that requires two-factor authentication and you disable 2FA, you'll be automatically removed from the organization, and you'll lose your access to their repositories. To regain access to the organization, re-enable two-factor authentication and contact an organization owner. We strongly recommend using two-factor authentication (2FA) to secure your account. If you need to disable 2FA, we recommend re-enabling it as soon as possible. diff --git a/content/authentication/securing-your-account-with-two-factor-authentication-2fa/recovering-your-account-if-you-lose-your-2fa-credentials.md b/content/authentication/securing-your-account-with-two-factor-authentication-2fa/recovering-your-account-if-you-lose-your-2fa-credentials.md index b68c0cf1d0a4..33ebb5b56a96 100644 --- a/content/authentication/securing-your-account-with-two-factor-authentication-2fa/recovering-your-account-if-you-lose-your-2fa-credentials.md +++ b/content/authentication/securing-your-account-with-two-factor-authentication-2fa/recovering-your-account-if-you-lose-your-2fa-credentials.md @@ -17,19 +17,11 @@ shortTitle: Recover an account with 2FA --- {% ifversion fpt or ghec %} -{% warning %} +> [!WARNING] +> * {% data reusables.two_fa.support-may-not-help %} -**Warnings**: - -* {% data reusables.two_fa.support-may-not-help %} - -{% endwarning %} - -{% note %} - -**Note:** {% data reusables.two_fa.unlink-email-address %} - -{% endnote %} +> [!NOTE] +> {% data reusables.two_fa.unlink-email-address %} {% endif %} @@ -39,11 +31,8 @@ Use one of your recovery codes to automatically regain entry into your account. 1. Type your username and password to prompt authentication. - {% warning %} - - **Warning**: {% data reusables.accounts.you-must-know-your-password %} - - {% endwarning %} + > [!WARNING] + > {% data reusables.accounts.you-must-know-your-password %} 1. Under "Having problems?", click **Use a recovery code{% ifversion fpt or ghec %} or begin 2FA account recovery{% endif %}**. 1. Type one of your recovery codes, then click **Verify**. @@ -64,11 +53,8 @@ If you configured two-factor authentication using a security key, you can use yo ## Authenticating with a fallback number -{% note %} - -**Note:** Configuring a fallback SMS number in addition to your primary SMS number is no longer supported. Instead, we strongly recommend registering multiple authentication methods. - -{% endnote %} +> [!NOTE] +> Configuring a fallback SMS number in addition to your primary SMS number is no longer supported. Instead, we strongly recommend registering multiple authentication methods. If you lose access to your preferred TOTP app or phone number, you can provide a two-factor authentication code sent to your fallback number to automatically regain access to your account. @@ -76,21 +62,16 @@ If you lose access to your preferred TOTP app or phone number, you can provide a If you know your password for {% data variables.product.prodname_dotcom %} but don't have the two-factor authentication credentials or your two-factor authentication recovery codes, you can have a one-time password sent to your verified email address to begin the verification process and regain access to your account. -{% note %} - -**Note**: For security reasons, regaining access to your account by authenticating with a one-time password can take up to three business days. {% data variables.product.company_short %} will not review additional requests submitted during this time. - -{% endnote %} +> [!NOTE] +> For security reasons, regaining access to your account by authenticating with a one-time password can take up to three business days. {% data variables.product.company_short %} will not review additional requests submitted during this time. You can use your two-factor authentication credentials or two-factor authentication recovery codes to regain access to your account anytime during the 3-5 day waiting period. 1. Type your username and password to prompt authentication. - {% warning %} - - **Warning**: {% data reusables.accounts.you-must-know-your-password %} + > [!WARNING] + > {% data reusables.accounts.you-must-know-your-password %} - {% endwarning %} 1. Under "Having problems?", click **Use a recovery code or begin 2FA account recovery**. 1. Under "Locked out?", click **Try 2FA account recovery, or unlink your account email address(es)**. 1. Click **I understand, get started** to request a reset of your authentication settings. diff --git a/content/authentication/troubleshooting-ssh/error-permission-denied-publickey.md b/content/authentication/troubleshooting-ssh/error-permission-denied-publickey.md index d49f490632da..29db4a93664a 100644 --- a/content/authentication/troubleshooting-ssh/error-permission-denied-publickey.md +++ b/content/authentication/troubleshooting-ssh/error-permission-denied-publickey.md @@ -119,11 +119,8 @@ You should see this output: The `ssh-add` command _should_ print out a long string of numbers and letters. If it does not print anything, you will need to [generate a new SSH key](/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent) and associate it with {% data variables.product.product_name %}. -{% tip %} - -**Tip**: On most systems the default private keys (`~/.ssh/id_rsa` and `~/.ssh/identity`) are automatically added to the SSH authentication agent. You shouldn't need to run `ssh-add path/to/key` unless you override the file name when you generate a key. - -{% endtip %} +> [!TIP] +> On most systems the default private keys (`~/.ssh/id_rsa` and `~/.ssh/identity`) are automatically added to the SSH authentication agent. You shouldn't need to run `ssh-add path/to/key` unless you override the file name when you generate a key. ### Getting more details @@ -247,8 +244,5 @@ You must provide your public key to {% data variables.product.product_name %} to If you don't see your public key in {% data variables.product.product_name %}, you'll need to [add your SSH key to {% data variables.product.product_name %}](/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account) to associate it with your computer. -{% warning %} - -**Warning**: If you see an SSH key you're not familiar with on {% data variables.product.product_name %}, delete it immediately and contact {% data variables.contact.contact_support %} for further help. An unidentified public key may indicate a possible security concern. For more information, see "[AUTOTITLE](/authentication/keeping-your-account-and-data-secure/reviewing-your-ssh-keys)." - -{% endwarning %} +> [!WARNING] +> If you see an SSH key you're not familiar with on {% data variables.product.product_name %}, delete it immediately and contact {% data variables.contact.contact_support %} for further help. An unidentified public key may indicate a possible security concern. For more information, see "[AUTOTITLE](/authentication/keeping-your-account-and-data-secure/reviewing-your-ssh-keys)." diff --git a/content/authentication/troubleshooting-ssh/error-ssh-add-illegal-option----apple-use-keychain.md b/content/authentication/troubleshooting-ssh/error-ssh-add-illegal-option----apple-use-keychain.md index f5fc673447c0..df8ac233b42f 100644 --- a/content/authentication/troubleshooting-ssh/error-ssh-add-illegal-option----apple-use-keychain.md +++ b/content/authentication/troubleshooting-ssh/error-ssh-add-illegal-option----apple-use-keychain.md @@ -25,14 +25,9 @@ To add your SSH private key to the ssh-agent, you can specify the path to the Ap /usr/bin/ssh-add --apple-use-keychain ~/.ssh/id_ed25519 ``` -{% note %} - -**Notes:** - -* The `--apple-use-keychain` option is in Apple's standard version of `ssh-add`. In macOS versions prior to Monterey (12.0), use `-K` instead of `--apple-use-keychain`. -* {% data reusables.ssh.add-ssh-key-to-ssh-agent %} - -{% endnote %} +> [!NOTE] +> * The `--apple-use-keychain` option is in Apple's standard version of `ssh-add`. In macOS versions prior to Monterey (12.0), use `-K` instead of `--apple-use-keychain`. +> * {% data reusables.ssh.add-ssh-key-to-ssh-agent %} ## Further reading diff --git a/content/authentication/troubleshooting-ssh/error-unknown-key-type.md b/content/authentication/troubleshooting-ssh/error-unknown-key-type.md index d736a9b0b31f..6f1502c9d0b1 100644 --- a/content/authentication/troubleshooting-ssh/error-unknown-key-type.md +++ b/content/authentication/troubleshooting-ssh/error-unknown-key-type.md @@ -21,13 +21,10 @@ You must have Homebrew installed. For more information, see the [installation gu ## Solving the issue -{% warning %} - -**Warning:** If you install OpenSSH, your computer will not be able to retrieve passphrases that are stored in the Apple keychain. You will need to enter your passphrase or interact with your hardware security key every time you authenticate with SSH to {% data variables.product.prodname_dotcom %} or another web service. - -If you remove OpenSSH, the passphrases that are stored in your keychain will once again be retrievable. You can remove OpenSSH by entering the command `brew uninstall openssh` in Terminal. - -{% endwarning %} +> [!WARNING] +> If you install OpenSSH, your computer will not be able to retrieve passphrases that are stored in the Apple keychain. You will need to enter your passphrase or interact with your hardware security key every time you authenticate with SSH to {% data variables.product.prodname_dotcom %} or another web service. +> +> If you remove OpenSSH, the passphrases that are stored in your keychain will once again be retrievable. You can remove OpenSSH by entering the command `brew uninstall openssh` in Terminal. 1. Open Terminal. 1. Enter the command `brew install openssh`.