diff --git a/components/product/SidebarProduct.tsx b/components/product/SidebarProduct.tsx index ca9b01bf44..a4472a6a49 100644 --- a/components/product/SidebarProduct.tsx +++ b/components/product/SidebarProduct.tsx @@ -39,7 +39,7 @@ export const SidebarProduct = () => { const isStandaloneCategory = childPage.page.documentType === 'article' const childTitle = childPage.renderedShortTitle || childPage.renderedFullTitle - const isActive = routePath.includes(routePath) + const isActive = routePath.includes(childPage.href) const isCurrent = routePath === childPage.href return (
  • - - - - - - - - - - diff --git a/content/admin/overview/about-upgrades-to-new-releases.md b/content/admin/overview/about-upgrades-to-new-releases.md index f36e855192..fbcaefad55 100644 --- a/content/admin/overview/about-upgrades-to-new-releases.md +++ b/content/admin/overview/about-upgrades-to-new-releases.md @@ -45,5 +45,5 @@ To upgrade your enterprise to a new release, see "[Release notes](/enterprise-se - [ {% data variables.product.prodname_roadmap %} ]( {% data variables.product.prodname_roadmap_link %} ) in the `github/roadmap` repository {% if currentVersion == "github-ae@latest" %} -- [ {% data variables.product.prodname_ghe_managed %} release notes](/admin/overview/github-ae-release-notes) +- [ {% data variables.product.prodname_ghe_managed %} release notes](/admin/release-notes) {% endif %} diff --git a/content/admin/overview/github-ae-release-notes.md b/content/admin/overview/github-ae-release-notes.md deleted file mode 100644 index dbd6bc961a..0000000000 --- a/content/admin/overview/github-ae-release-notes.md +++ /dev/null @@ -1,89 +0,0 @@ ---- -title: GitHub AE release notes -intro: March 1, 2021 -versions: - github-ae: '*' ---- - -### Features - -#### {% data variables.product.prodname_actions %} beta - -[{% data variables.product.prodname_actions %}](https://github.com/features/actions) is a powerful, flexible solution for CI/CD and workflow automation. For more information, see "[Introduction to {% data variables.product.prodname_actions %}](/actions/learn-github-actions/introduction-to-github-actions)." - -{% data variables.product.prodname_actions %} on {% data variables.product.product_name %} uses a new [{% data variables.actions.hosted_runner %}](/actions/using-github-hosted-runners/about-ae-hosted-runners), only available for {% data variables.product.product_name %}, that enables you to customize the size, image, and networking configuration of the runners. These runners are a finished-service CI compute environment with auto-scaling and management, fully managed by {% data variables.product.company_short %}. During the beta, the use {% data variables.actions.hosted_runner %}s is free of charge. For more information, see "[Adding {% data variables.actions.hosted_runner %}s](/actions/using-github-hosted-runners/adding-ae-hosted-runners)." - -Please note that when {% data variables.product.prodname_actions %} is enabled during this upgrade, two organizations named "GitHub Actions" (@**actions** and @**github**) will appear in {% data variables.product.product_location %}. These organizations are required by {% data variables.product.prodname_actions %}. Users named @**ghost** and @**actions** appear as the actors for creation of these organizations in the audit log. - -#### {% data variables.product.prodname_registry %} beta - -[{% data variables.product.prodname_registry %}](https://github.com/features/packages) is a package hosting service, natively integrated with {% data variables.product.prodname_actions %}, APIs, and webhooks. Create an [end-to-end DevOps workflow](/github-ae@latest/packages/quickstart) that includes your code, continuous integration, and deployment solutions. For more information, see "[About {% data variables.product.prodname_registry %}](/packages/learn-github-packages/about-github-packages)." - -During this beta, {% data variables.product.prodname_registry %} is free of charge to {% data variables.product.product_name %} customers. - -#### {% data variables.product.prodname_GH_advanced_security %} beta - -{% data variables.product.prodname_GH_advanced_security %} is available in beta and includes both {% data variables.product.prodname_code_scanning %} and {% data variables.product.prodname_secret_scanning %}. Repository administrators and organization owners can opt into {% data variables.product.prodname_advanced_security %} features in the settings for a repository or organization, within the **Security and analysis** tab. For more information, see "[About {% data variables.product.prodname_GH_advanced_security %}](/github/getting-started-with-github/about-github-advanced-security)." - -During this beta, {% data variables.product.prodname_advanced_security %} features are free of charge to {% data variables.product.product_name %} customers. - -#### Manage teams from your identity provider (IdP) - -Customers using SCIM (System for Cross-domain Identity Management) can now sync security groups in Azure Active Directory with {% data variables.product.company_short %} teams. Once a team has been linked to a security group, membership will be automatically updated in {% data variables.product.product_name %} when a user is added or removed from their assigned security group. For more information, see "[Synchronizing a team with an identity provider group](/organizations/organizing-members-into-teams/synchronizing-a-team-with-an-identity-provider-group)." - -#### IP allow lists beta - -Enterprise and organization owners can now use IP allow lists to restrict traffic to the enterprise or specific organizations. After you configure an IP allow list, only visitors from IP addresses on the list are permitted to access the resources protected by the list. - -This functionality is provided in addition to the ability to request network security group changes that filter traffic to the entirety of the {% data variables.product.product_name %} tenant. - -For more information, see "[Restricting network traffic to your enterprise](/admin/configuration/restricting-network-traffic-to-your-enterprise)" and "[Managing allowed IP addresses for your organization](/organizations/keeping-your-organization-secure/managing-allowed-ip-addresses-for-your-organization)." - -#### Pull request auto-merge - -With auto-merge, pull requests can be set to merge automatically when all merge requirements have been satisfied. This saves users from needing to constantly check the state of their pull requests just to merge them. Auto-merge can be enabled by a user with permission to merge and on pull requests that have unsatisfied merge requirements (like missing approvals or pending or failing required status checks). For more information, see "[Automatically merging a pull request](/github/collaborating-with-issues-and-pull-requests/automatically-merging-a-pull-request)." - -### Changes - -#### Developer changes - -- [Organization owners can now disable publication](/organizations/managing-organization-settings/managing-the-publication-of-github-pages-sites-for-your-organization) of {% data variables.product.prodname_pages %} sites from repositories in the organization. This will not unpublish existing sites. -- Repositories that use {% data variables.product.prodname_pages %} can now [build and deploy from any branch](/pages/getting-started-with-github-pages/about-github-pages#publishing-sources-for-github-pages-sites). -- When writing an issue or pull request, the list syntax for bullets, numbers, and tasks will now be autocompleted after you press `return` or `enter`. -- You can now delete a directory in a repository from the repository page. When navigating to a directory, a new kebab button next to the "Add file" button gives the option to delete the directory. -- It’s now easier and faster to [reference issues or pull requests](/github/writing-on-github/basic-writing-and-formatting-syntax#referencing-issues-and-pull-requests), with search across multiple words after the "#". - -##### Administration changes - -- Enterprise owners can now [publish a mandatory message](/admin/user-management/customizing-user-messages-for-your-enterprise#creating-a-mandatory-message). The message is shown to all users and they must acknowledge it. This can be used to display important information, terms of service or policies. -- The {% data variables.product.prodname_github_app%} single file path permission can now [support up to ten files](/developers/apps/creating-a-github-app-using-url-parameters). -- When configuring a {% data variables.product.prodname_github_app%}, the authorization callback URL is a required field. Now we will permit the integrator to specify multiple callback URLs. {% data variables.product.product_name %} denies authorization if the callback URL from the request is not listed. -- A [new API endpoint](/rest/reference/apps#create-a-scoped-access-token) enables the exchange of a user to server token for a user to server token scoped to specific repositories. -- Events are now logged in the audit log on [promoting a team member to be a team maintainer and on demoting a team maintainer to be a team member](/admin/user-management/audited-actions#teams). -- The [OAuth device authorization flow](/developers/apps/authorizing-oauth-apps#device-flow) is now supported. This allows any CLI client or developer tool to authenticate using a secondary system. -- A user can no longer delete their account if SCIM provisioning is enabled. - -##### Default branch renaming - -Enterprise and organization owners can now set the default branch name for new repositories. Enterprise owners can also enforce their choice of default branch name across all organizations or allow individual organizations to choose their own. For more information, see "[Enforcing repository management policies in your enterprise](/admin/policies/enforcing-repository-management-policies-in-your-enterprise#enforcing-a-policy-on-the-default-branch-name)" and "[Managing the default branch name for repositories in your organization](/organizations/managing-organization-settings/managing-the-default-branch-name-for-repositories-in-your-organization)." - -Existing repositories are unaffected by these settings, and their default branch name will not be changed. - -This change is one of many changes {% data variables.product.company_short %} is making to support projects and maintainers that want to rename their default branch. To learn more, see [github/renaming](https://github.com/github/renaming). - - -### Bug fixes -- Users can no longer set a backup email address on their profile. Their email address is set through the IdP only. -- You can no longer enable two-factor authentication after configuring authentication through your IdP. -- {% data variables.product.product_name %} can now connect to Azure Boards. -- Version headers were missing from the APIs, and have now been set to "GitHub AE." -- Links to documentation have been fixed. -- Configuration of audit log forwarding within the enterprise's settings was failing. -- Navigating to gists could result in a 500 error. -- The Support email or URL was failing to save. It now saves after a period of a few minutes. -- Organization level pull request templates were not being applied to all pull requests in the organization. - -### Known issues - -- Geographic location data is not shown in the audit log. Location information can otherwise be discerned from the IP address associated with each event. -- The link to {% data variables.product.prodname_registry %} from a repository page shows an incorrect search page when that repository does not have any packages. diff --git a/content/admin/overview/index.md b/content/admin/overview/index.md index 9f047fd8bd..7c88265d86 100644 --- a/content/admin/overview/index.md +++ b/content/admin/overview/index.md @@ -8,6 +8,7 @@ versions: github-ae: '*' children: - /about-github-ae + - /about-upgrades-to-new-releases - /about-data-residency - /about-enterprise-accounts - /managing-your-github-enterprise-license @@ -15,6 +16,5 @@ children: - /about-upgrades-to-new-releases - /system-overview - /about-the-github-enterprise-api - - /github-ae-release-notes --- For more information, or to purchase {% data variables.product.prodname_enterprise %}, see [{% data variables.product.prodname_enterprise %}](https://github.com/enterprise). diff --git a/content/admin/release-notes.md b/content/admin/release-notes.md index 7dfc63a71c..da80bfef2a 100644 --- a/content/admin/release-notes.md +++ b/content/admin/release-notes.md @@ -1,11 +1,12 @@ --- title: Release notes -intro: 'The release notes for {{ allVersions[currentVersion].versionTitle }}.' layout: release-notes versions: enterprise-server: '*' + github-ae: '*' topics: - Enterprise - Upgrades +redirect_from: + - /admin/overview/github-ae-release-notes --- - diff --git a/data/release-notes/2-20/0.yml b/data/release-notes/enterprise-server/2-20/0.yml similarity index 100% rename from data/release-notes/2-20/0.yml rename to data/release-notes/enterprise-server/2-20/0.yml diff --git a/data/release-notes/2-20/1.yml b/data/release-notes/enterprise-server/2-20/1.yml similarity index 100% rename from data/release-notes/2-20/1.yml rename to data/release-notes/enterprise-server/2-20/1.yml diff --git a/data/release-notes/2-20/10.yml b/data/release-notes/enterprise-server/2-20/10.yml similarity index 100% rename from data/release-notes/2-20/10.yml rename to data/release-notes/enterprise-server/2-20/10.yml diff --git a/data/release-notes/2-20/11.yml b/data/release-notes/enterprise-server/2-20/11.yml similarity index 100% rename from data/release-notes/2-20/11.yml rename to data/release-notes/enterprise-server/2-20/11.yml diff --git a/data/release-notes/2-20/12.yml b/data/release-notes/enterprise-server/2-20/12.yml similarity index 100% rename from data/release-notes/2-20/12.yml rename to data/release-notes/enterprise-server/2-20/12.yml diff --git a/data/release-notes/2-20/13.yml b/data/release-notes/enterprise-server/2-20/13.yml similarity index 100% rename from data/release-notes/2-20/13.yml rename to data/release-notes/enterprise-server/2-20/13.yml diff --git a/data/release-notes/2-20/14.yml b/data/release-notes/enterprise-server/2-20/14.yml similarity index 100% rename from data/release-notes/2-20/14.yml rename to data/release-notes/enterprise-server/2-20/14.yml diff --git a/data/release-notes/2-20/15.yml b/data/release-notes/enterprise-server/2-20/15.yml similarity index 100% rename from data/release-notes/2-20/15.yml rename to data/release-notes/enterprise-server/2-20/15.yml diff --git a/data/release-notes/2-20/16.yml b/data/release-notes/enterprise-server/2-20/16.yml similarity index 100% rename from data/release-notes/2-20/16.yml rename to data/release-notes/enterprise-server/2-20/16.yml diff --git a/data/release-notes/2-20/17.yml b/data/release-notes/enterprise-server/2-20/17.yml similarity index 100% rename from data/release-notes/2-20/17.yml rename to data/release-notes/enterprise-server/2-20/17.yml diff --git a/data/release-notes/2-20/18.yml b/data/release-notes/enterprise-server/2-20/18.yml similarity index 100% rename from data/release-notes/2-20/18.yml rename to data/release-notes/enterprise-server/2-20/18.yml diff --git a/data/release-notes/2-20/19.yml b/data/release-notes/enterprise-server/2-20/19.yml similarity index 100% rename from data/release-notes/2-20/19.yml rename to data/release-notes/enterprise-server/2-20/19.yml diff --git a/data/release-notes/2-20/2.yml b/data/release-notes/enterprise-server/2-20/2.yml similarity index 100% rename from data/release-notes/2-20/2.yml rename to data/release-notes/enterprise-server/2-20/2.yml diff --git a/data/release-notes/2-20/20.yml b/data/release-notes/enterprise-server/2-20/20.yml similarity index 100% rename from data/release-notes/2-20/20.yml rename to data/release-notes/enterprise-server/2-20/20.yml diff --git a/data/release-notes/2-20/21.yml b/data/release-notes/enterprise-server/2-20/21.yml similarity index 100% rename from data/release-notes/2-20/21.yml rename to data/release-notes/enterprise-server/2-20/21.yml diff --git a/data/release-notes/2-20/22.yml b/data/release-notes/enterprise-server/2-20/22.yml similarity index 100% rename from data/release-notes/2-20/22.yml rename to data/release-notes/enterprise-server/2-20/22.yml diff --git a/data/release-notes/2-20/23.yml b/data/release-notes/enterprise-server/2-20/23.yml similarity index 100% rename from data/release-notes/2-20/23.yml rename to data/release-notes/enterprise-server/2-20/23.yml diff --git a/data/release-notes/2-20/24.yml b/data/release-notes/enterprise-server/2-20/24.yml similarity index 100% rename from data/release-notes/2-20/24.yml rename to data/release-notes/enterprise-server/2-20/24.yml diff --git a/data/release-notes/2-20/3.yml b/data/release-notes/enterprise-server/2-20/3.yml similarity index 100% rename from data/release-notes/2-20/3.yml rename to data/release-notes/enterprise-server/2-20/3.yml diff --git a/data/release-notes/2-20/4.yml b/data/release-notes/enterprise-server/2-20/4.yml similarity index 100% rename from data/release-notes/2-20/4.yml rename to data/release-notes/enterprise-server/2-20/4.yml diff --git a/data/release-notes/2-20/5.yml b/data/release-notes/enterprise-server/2-20/5.yml similarity index 100% rename from data/release-notes/2-20/5.yml rename to data/release-notes/enterprise-server/2-20/5.yml diff --git a/data/release-notes/2-20/6.yml b/data/release-notes/enterprise-server/2-20/6.yml similarity index 100% rename from data/release-notes/2-20/6.yml rename to data/release-notes/enterprise-server/2-20/6.yml diff --git a/data/release-notes/2-20/7.yml b/data/release-notes/enterprise-server/2-20/7.yml similarity index 100% rename from data/release-notes/2-20/7.yml rename to data/release-notes/enterprise-server/2-20/7.yml diff --git a/data/release-notes/2-20/8.yml b/data/release-notes/enterprise-server/2-20/8.yml similarity index 100% rename from data/release-notes/2-20/8.yml rename to data/release-notes/enterprise-server/2-20/8.yml diff --git a/data/release-notes/2-20/9.yml b/data/release-notes/enterprise-server/2-20/9.yml similarity index 100% rename from data/release-notes/2-20/9.yml rename to data/release-notes/enterprise-server/2-20/9.yml diff --git a/data/release-notes/2-21/0.yml b/data/release-notes/enterprise-server/2-21/0.yml similarity index 100% rename from data/release-notes/2-21/0.yml rename to data/release-notes/enterprise-server/2-21/0.yml diff --git a/data/release-notes/2-21/1.yml b/data/release-notes/enterprise-server/2-21/1.yml similarity index 100% rename from data/release-notes/2-21/1.yml rename to data/release-notes/enterprise-server/2-21/1.yml diff --git a/data/release-notes/2-21/10.yml b/data/release-notes/enterprise-server/2-21/10.yml similarity index 100% rename from data/release-notes/2-21/10.yml rename to data/release-notes/enterprise-server/2-21/10.yml diff --git a/data/release-notes/2-21/11.yml b/data/release-notes/enterprise-server/2-21/11.yml similarity index 100% rename from data/release-notes/2-21/11.yml rename to data/release-notes/enterprise-server/2-21/11.yml diff --git a/data/release-notes/2-21/12.yml b/data/release-notes/enterprise-server/2-21/12.yml similarity index 100% rename from data/release-notes/2-21/12.yml rename to data/release-notes/enterprise-server/2-21/12.yml diff --git a/data/release-notes/2-21/13.yml b/data/release-notes/enterprise-server/2-21/13.yml similarity index 100% rename from data/release-notes/2-21/13.yml rename to data/release-notes/enterprise-server/2-21/13.yml diff --git a/data/release-notes/2-21/14.yml b/data/release-notes/enterprise-server/2-21/14.yml similarity index 100% rename from data/release-notes/2-21/14.yml rename to data/release-notes/enterprise-server/2-21/14.yml diff --git a/data/release-notes/2-21/15.yml b/data/release-notes/enterprise-server/2-21/15.yml similarity index 100% rename from data/release-notes/2-21/15.yml rename to data/release-notes/enterprise-server/2-21/15.yml diff --git a/data/release-notes/2-21/16.yml b/data/release-notes/enterprise-server/2-21/16.yml similarity index 100% rename from data/release-notes/2-21/16.yml rename to data/release-notes/enterprise-server/2-21/16.yml diff --git a/data/release-notes/2-21/17.yml b/data/release-notes/enterprise-server/2-21/17.yml similarity index 100% rename from data/release-notes/2-21/17.yml rename to data/release-notes/enterprise-server/2-21/17.yml diff --git a/data/release-notes/2-21/18.yml b/data/release-notes/enterprise-server/2-21/18.yml similarity index 100% rename from data/release-notes/2-21/18.yml rename to data/release-notes/enterprise-server/2-21/18.yml diff --git a/data/release-notes/2-21/19.yml b/data/release-notes/enterprise-server/2-21/19.yml similarity index 100% rename from data/release-notes/2-21/19.yml rename to data/release-notes/enterprise-server/2-21/19.yml diff --git a/data/release-notes/2-21/2.yml b/data/release-notes/enterprise-server/2-21/2.yml similarity index 100% rename from data/release-notes/2-21/2.yml rename to data/release-notes/enterprise-server/2-21/2.yml diff --git a/data/release-notes/2-21/20.yml b/data/release-notes/enterprise-server/2-21/20.yml similarity index 100% rename from data/release-notes/2-21/20.yml rename to data/release-notes/enterprise-server/2-21/20.yml diff --git a/data/release-notes/2-21/21.yml b/data/release-notes/enterprise-server/2-21/21.yml similarity index 100% rename from data/release-notes/2-21/21.yml rename to data/release-notes/enterprise-server/2-21/21.yml diff --git a/data/release-notes/2-21/22.yml b/data/release-notes/enterprise-server/2-21/22.yml similarity index 100% rename from data/release-notes/2-21/22.yml rename to data/release-notes/enterprise-server/2-21/22.yml diff --git a/data/release-notes/2-21/3.yml b/data/release-notes/enterprise-server/2-21/3.yml similarity index 100% rename from data/release-notes/2-21/3.yml rename to data/release-notes/enterprise-server/2-21/3.yml diff --git a/data/release-notes/2-21/4.yml b/data/release-notes/enterprise-server/2-21/4.yml similarity index 100% rename from data/release-notes/2-21/4.yml rename to data/release-notes/enterprise-server/2-21/4.yml diff --git a/data/release-notes/2-21/5.yml b/data/release-notes/enterprise-server/2-21/5.yml similarity index 100% rename from data/release-notes/2-21/5.yml rename to data/release-notes/enterprise-server/2-21/5.yml diff --git a/data/release-notes/2-21/6.yml b/data/release-notes/enterprise-server/2-21/6.yml similarity index 100% rename from data/release-notes/2-21/6.yml rename to data/release-notes/enterprise-server/2-21/6.yml diff --git a/data/release-notes/2-21/7.yml b/data/release-notes/enterprise-server/2-21/7.yml similarity index 100% rename from data/release-notes/2-21/7.yml rename to data/release-notes/enterprise-server/2-21/7.yml diff --git a/data/release-notes/2-21/8.yml b/data/release-notes/enterprise-server/2-21/8.yml similarity index 100% rename from data/release-notes/2-21/8.yml rename to data/release-notes/enterprise-server/2-21/8.yml diff --git a/data/release-notes/2-21/9.yml b/data/release-notes/enterprise-server/2-21/9.yml similarity index 100% rename from data/release-notes/2-21/9.yml rename to data/release-notes/enterprise-server/2-21/9.yml diff --git a/data/release-notes/2-22/0.yml b/data/release-notes/enterprise-server/2-22/0.yml similarity index 100% rename from data/release-notes/2-22/0.yml rename to data/release-notes/enterprise-server/2-22/0.yml diff --git a/data/release-notes/2-22/1.yml b/data/release-notes/enterprise-server/2-22/1.yml similarity index 100% rename from data/release-notes/2-22/1.yml rename to data/release-notes/enterprise-server/2-22/1.yml diff --git a/data/release-notes/2-22/10.yml b/data/release-notes/enterprise-server/2-22/10.yml similarity index 100% rename from data/release-notes/2-22/10.yml rename to data/release-notes/enterprise-server/2-22/10.yml diff --git a/data/release-notes/2-22/11.yml b/data/release-notes/enterprise-server/2-22/11.yml similarity index 100% rename from data/release-notes/2-22/11.yml rename to data/release-notes/enterprise-server/2-22/11.yml diff --git a/data/release-notes/2-22/12.yml b/data/release-notes/enterprise-server/2-22/12.yml similarity index 100% rename from data/release-notes/2-22/12.yml rename to data/release-notes/enterprise-server/2-22/12.yml diff --git a/data/release-notes/2-22/13.yml b/data/release-notes/enterprise-server/2-22/13.yml similarity index 100% rename from data/release-notes/2-22/13.yml rename to data/release-notes/enterprise-server/2-22/13.yml diff --git a/data/release-notes/2-22/14.yml b/data/release-notes/enterprise-server/2-22/14.yml similarity index 100% rename from data/release-notes/2-22/14.yml rename to data/release-notes/enterprise-server/2-22/14.yml diff --git a/data/release-notes/2-22/2.yml b/data/release-notes/enterprise-server/2-22/2.yml similarity index 100% rename from data/release-notes/2-22/2.yml rename to data/release-notes/enterprise-server/2-22/2.yml diff --git a/data/release-notes/2-22/3.yml b/data/release-notes/enterprise-server/2-22/3.yml similarity index 100% rename from data/release-notes/2-22/3.yml rename to data/release-notes/enterprise-server/2-22/3.yml diff --git a/data/release-notes/2-22/4.yml b/data/release-notes/enterprise-server/2-22/4.yml similarity index 100% rename from data/release-notes/2-22/4.yml rename to data/release-notes/enterprise-server/2-22/4.yml diff --git a/data/release-notes/2-22/5.yml b/data/release-notes/enterprise-server/2-22/5.yml similarity index 100% rename from data/release-notes/2-22/5.yml rename to data/release-notes/enterprise-server/2-22/5.yml diff --git a/data/release-notes/2-22/6.yml b/data/release-notes/enterprise-server/2-22/6.yml similarity index 100% rename from data/release-notes/2-22/6.yml rename to data/release-notes/enterprise-server/2-22/6.yml diff --git a/data/release-notes/2-22/7.yml b/data/release-notes/enterprise-server/2-22/7.yml similarity index 100% rename from data/release-notes/2-22/7.yml rename to data/release-notes/enterprise-server/2-22/7.yml diff --git a/data/release-notes/2-22/8.yml b/data/release-notes/enterprise-server/2-22/8.yml similarity index 100% rename from data/release-notes/2-22/8.yml rename to data/release-notes/enterprise-server/2-22/8.yml diff --git a/data/release-notes/2-22/9.yml b/data/release-notes/enterprise-server/2-22/9.yml similarity index 100% rename from data/release-notes/2-22/9.yml rename to data/release-notes/enterprise-server/2-22/9.yml diff --git a/data/release-notes/3-0/0-rc1.yml b/data/release-notes/enterprise-server/3-0/0-rc1.yml similarity index 100% rename from data/release-notes/3-0/0-rc1.yml rename to data/release-notes/enterprise-server/3-0/0-rc1.yml diff --git a/data/release-notes/3-0/0-rc2.yml b/data/release-notes/enterprise-server/3-0/0-rc2.yml similarity index 100% rename from data/release-notes/3-0/0-rc2.yml rename to data/release-notes/enterprise-server/3-0/0-rc2.yml diff --git a/data/release-notes/3-0/0.yml b/data/release-notes/enterprise-server/3-0/0.yml similarity index 100% rename from data/release-notes/3-0/0.yml rename to data/release-notes/enterprise-server/3-0/0.yml diff --git a/data/release-notes/3-0/1.yml b/data/release-notes/enterprise-server/3-0/1.yml similarity index 100% rename from data/release-notes/3-0/1.yml rename to data/release-notes/enterprise-server/3-0/1.yml diff --git a/data/release-notes/3-0/2.yml b/data/release-notes/enterprise-server/3-0/2.yml similarity index 100% rename from data/release-notes/3-0/2.yml rename to data/release-notes/enterprise-server/3-0/2.yml diff --git a/data/release-notes/3-0/3.yml b/data/release-notes/enterprise-server/3-0/3.yml similarity index 100% rename from data/release-notes/3-0/3.yml rename to data/release-notes/enterprise-server/3-0/3.yml diff --git a/data/release-notes/3-0/4.yml b/data/release-notes/enterprise-server/3-0/4.yml similarity index 100% rename from data/release-notes/3-0/4.yml rename to data/release-notes/enterprise-server/3-0/4.yml diff --git a/data/release-notes/3-0/5.yml b/data/release-notes/enterprise-server/3-0/5.yml similarity index 100% rename from data/release-notes/3-0/5.yml rename to data/release-notes/enterprise-server/3-0/5.yml diff --git a/data/release-notes/3-0/6.yml b/data/release-notes/enterprise-server/3-0/6.yml similarity index 100% rename from data/release-notes/3-0/6.yml rename to data/release-notes/enterprise-server/3-0/6.yml diff --git a/data/release-notes/3-0/7.yml b/data/release-notes/enterprise-server/3-0/7.yml similarity index 100% rename from data/release-notes/3-0/7.yml rename to data/release-notes/enterprise-server/3-0/7.yml diff --git a/data/release-notes/3-0/8.yml b/data/release-notes/enterprise-server/3-0/8.yml similarity index 100% rename from data/release-notes/3-0/8.yml rename to data/release-notes/enterprise-server/3-0/8.yml diff --git a/data/release-notes/3-1/0-rc1.yml b/data/release-notes/enterprise-server/3-1/0-rc1.yml similarity index 95% rename from data/release-notes/3-1/0-rc1.yml rename to data/release-notes/enterprise-server/3-1/0-rc1.yml index 3d06e0e705..5b28c70d8d 100644 --- a/data/release-notes/3-1/0-rc1.yml +++ b/data/release-notes/enterprise-server/3-1/0-rc1.yml @@ -18,6 +18,11 @@ sections: Administrators using {% data variables.product.prodname_GH_advanced_security %} can [enable and configure](/enterprise-server@3.1/admin/configuration/configuring-secret-scanning-for-your-appliance) {% data variables.product.prodname_GH_advanced_security %} secret scanning. You can review the [updated minimum requirements for your platform](/enterprise-server@3.1/admin/installation/setting-up-a-github-enterprise-server-instance) before you turn on {% data variables.product.prodname_GH_advanced_security %} secret scanning. + - heading: GitHub Advanced Security Code Scanning + notes: + - | + [{% data variables.product.prodname_GH_advanced_security %} code scanning](https://github.com/features/security) is now generally available on {% data variables.product.prodname_ghe_server %} 3.1+. Organizations who have purchased {% data variables.product.prodname_GH_advanced_security %} can use this capability to do static analysis security testing against their code, and prevent vulnerabilities from making it to their production code using CodeQL, our semantic analysis engine. For more information, see "[Configuring code scanning on your appliance](/en/enterprise-server@3.1/admin/configuration/configuring-code-scanning-for-your-appliance#running-code-scanning-using-github-actions)." + - heading: GitHub Advanced Security billing improvements notes: - | diff --git a/data/release-notes/enterprise-server/README.md b/data/release-notes/enterprise-server/README.md new file mode 100644 index 0000000000..5a488316c8 --- /dev/null +++ b/data/release-notes/enterprise-server/README.md @@ -0,0 +1,37 @@ +# Release notes for GitHub Enterprise Server + +Rendered here: https://docs.github.com/en/enterprise-server@latest/admin/release-notes + +## How it works + +### Placeholder content file + +A content file exists in `content/admin/release-notes.md`. It has a special frontmatter property `layout: release-notes` and no Markdown content. The source of the release notes comes from YAML data. + +### YAML source + +The source data for the release notes lives in this directory (`data/release-notes/enterprise-server`). + +The directories are named by GHES release number (with a hyphen instead of a period). + +The YAML files in each directory are named by patch number. Some patch filenames may end with `-rc.yml`, which means it's a release candidate. A release candidate file also requires `release_candidate: true` in the YAML data. + +Release notes of deprecated GHES versions (see `lib/enterprise-server-releases.js`) are **not** removed from the site and will always be displayed alongside currently supported versions. + +Note that patch files can be deprecated individually (i.e., hidden on the docs site) by an optional `deprecated: true` property. + +### Middleware processing + +The YAML data is processed and sorted by `middleware/contextualizers/release-notes.js` and added to the `context` object. + +### Layouts + +The `context` object data is rendered by `layouts/release-notes.html` and `includes/enterprise-server-release-notes.html`. + +The release notes page has a custom design with CSS in `stylesheets/release-notes.scss` and client-side JavaScript in `javascripts/release-notes.js`. + +### Schema + +The schema that validates the YAML data lives in `tests/helpers/schemas/ghes-release-notes-schema.js`. See the schema file to find out the required and optional properties. + +The schema is exercised by a test in `tests/linting/lint-files.js`. The test will fail if the data does not pass validation. diff --git a/data/release-notes/github-ae/2021-03/2021-03-03.yml b/data/release-notes/github-ae/2021-03/2021-03-03.yml new file mode 100644 index 0000000000..7185cd78c4 --- /dev/null +++ b/data/release-notes/github-ae/2021-03/2021-03-03.yml @@ -0,0 +1,83 @@ +date: '2021-03-03' +friendlyDate: 'March 3, 2021' +title: 'Week of March 3, 2021' +currentWeek: false +sections: + features: + - heading: 'GitHub Actions beta' + notes: + - | + [{% data variables.product.prodname_actions %}](https://github.com/features/actions) is a powerful, flexible solution for CI/CD and workflow automation. For more information, see "[Introduction to {% data variables.product.prodname_actions %}](/actions/learn-github-actions/introduction-to-github-actions)." + + {% data variables.product.prodname_actions %} on {% data variables.product.product_name %} uses a new [{% data variables.actions.hosted_runner %}](/actions/using-github-hosted-runners/about-ae-hosted-runners), only available for {% data variables.product.product_name %}, that enables you to customize the size, image, and networking configuration of the runners. These runners are a finished-service CI compute environment with auto-scaling and management, fully managed by {% data variables.product.company_short %}. During the beta, the use {% data variables.actions.hosted_runner %}s is free of charge. For more information, see "[Adding {% data variables.actions.hosted_runner %}s](/actions/using-github-hosted-runners/adding-ae-hosted-runners)." + + Please note that when {% data variables.product.prodname_actions %} is enabled during this upgrade, two organizations named "GitHub Actions" (@**actions** and @**github**) will appear in {% data variables.product.product_location %}. These organizations are required by {% data variables.product.prodname_actions %}. Users named @**ghost** and @**actions** appear as the actors for creation of these organizations in the audit log. + - heading: 'GitHub Packages beta' + notes: + - | + [{% data variables.product.prodname_registry %}](https://github.com/features/packages) is a package hosting service, natively integrated with {% data variables.product.prodname_actions %}, APIs, and webhooks. Create an [end-to-end DevOps workflow](/github-ae@latest/packages/quickstart) that includes your code, continuous integration, and deployment solutions. During this beta, {% data variables.product.prodname_registry %} is offered free of charge to {% data variables.product.product_name %} customers. + - heading: 'GitHub Advanced Security beta' + notes: + - | + {% data variables.product.prodname_GH_advanced_security %} is available in beta and includes both code scanning and secret scanning. During this beta, {% data variables.product.prodname_GH_advanced_security %} features are being offered free of charge to {% data variables.product.product_name %} customers. Repository and organization administrators can opt-in to use {% data variables.product.prodname_GH_advanced_security %} in the Security and Analysis tab under settings. + + Learn more about {% data variables.product.prodname_GH_advanced_security %} [code scanning](/github/finding-security-vulnerabilities-and-errors-in-your-code/about-code-scanning) and [secret scanning](/github/administering-a-repository/about-secret-scanning) on {% data variables.product.prodname_ghe_managed %}. + + - heading: Manage teams from your identity provider (IdP) + notes: + - | + Customers using SCIM (System for Cross-domain Identity Management) can now sync security groups in Azure Active Directory with {% data variables.product.company_short %} teams. Once a team has been linked to a security group, membership will be automatically updated in {% data variables.product.product_name %} when a user is added or removed from their assigned security group. + + - heading: IP allow lists beta + notes: + - | + [{% data variables.product.company_short %} IP allow lists](/admin/configuration/restricting-network-traffic-to-your-enterprise) provide the ability to filter traffic from administrator-specified IP ranges, defined by CIDR notation. The allow list is defined at the enterprise or organization account level in Security > Settings. All traffic that attempts to reach resources within the enterprise account and organizations are filtered by the IP allow lists. This functionality is provided in addition to the ability to request network security group changes that filter traffic to the entirety of the GHAE tenant. + + changes: + - heading: Developer Changes + notes: + - | + [Organization owners can now disable publication](/github/setting-up-and-managing-organizations-and-teams/managing-the-publication-of-github-pages-sites-for-your-organization) of {% data variables.product.prodname_pages %} sites from repositories in the organization. This will not unpublish existing sites. + - Repositories that use {% data variables.product.prodname_pages %} can now [build and deploy from any branch](/github/working-with-github-pages/about-github-pages#publishing-sources-for-github-pages-sites). + - When writing an issue or pull request, the list syntax for bullets, numbers, and tasks will now be autocompleted after you press `return` or `enter`. + - You can now delete a directory in a repository from the repository page. When navigating to a directory, a new kebab button next to the "Add file" button gives the option to delete the directory. + - It's now easier and faster to [reference issues or pull requests](/github/writing-on-github/basic-writing-and-formatting-syntax#referencing-issues-and-pull-requests), with search across multiple words after the "#". + + - heading: Administration changes + notes: + - Enterprise owners can now publish a mandatory message. The message is shown to all users and they must acknowledge it. This can be used to display important information, terms of service or policies. + - The {% data variables.product.prodname_github_app%} single file path permission can now [support up to ten files](/developers/apps/creating-a-github-app-using-url-parameters). + - When configuring a {% data variables.product.prodname_github_app%}, the authorization callback URL is a required field. Now we will permit the integrator to specify multiple callback URLs. {% data variables.product.product_name %} denies authorization if the callback URL from the request is not listed. + - A [new API endpoint](/rest/reference/apps#create-a-scoped-access-token) enables the exchange of a user to server token for a user to server token scoped to specific repositories. + - Events are now logged in the audit log on [promoting a team member to be a team maintainer and on demoting a team maintainer to be a team member](/admin/user-management/audited-actions#teams). + - The [OAuth device authorization flow](/developers/apps/authorizing-oauth-apps#device-flow) is now supported. This allows any CLI client or developer tool to authenticate using a secondary system. + - A user can no longer delete their account if SCIM provisioning is enabled. + + - heading: Default branch renaming + notes: + - | + Enterprise and organization owners can now set the default branch name for new repositories. Enterprise owners can also enforce their choice of default branch name across all organizations or allow individual organizations to choose their own. + + Existing repositories are unaffected by these settings, and their default branch name will not be changed. + + This change is one of many changes {% data variables.product.company_short %} is making to support projects and maintainers that want to rename their default branch. To learn more, see [github/renaming](https://github.com/github/renaming). + + bugs: + - heading: Bug fixes + notes: + - Users can no longer set a backup email address on their profile. Their email address is set through the IdP only. + - You can no longer enable two-factor authentication after configuring authentication through your IdP. + - | + {% data variables.product.product_name %} can now connect to Azure Boards. + - Version headers were missing from the APIs, and have now been set to "GitHub AE." + - Links to documentation have been fixed. + - Configuration of audit log forwarding within the enterprise's settings was failing. + - Navigating to gists could result in a 500 error. + - The Support email or URL was failing to save. It now saves after a period of a few minutes. + - Organization level pull request templates were not being applied to all pull requests in the organization. + + known_issues: + - heading: Known issues + notes: + - Geographic location data is not shown in the audit log. Location information can otherwise be discerned from the IP address associated with each event. + - The link to {% data variables.product.prodname_registry %} from a repository page shows an incorrect search page when that repository does not have any packages. diff --git a/data/release-notes/github-ae/README.md b/data/release-notes/github-ae/README.md new file mode 100644 index 0000000000..b20276bebd --- /dev/null +++ b/data/release-notes/github-ae/README.md @@ -0,0 +1,35 @@ +# Release notes for GitHub AE + +Rendered here: https://docs.github.com/en/github-ae@latest/admin/release-notes + +## How it works + +### Placeholder content file + +A content file exists in `content/admin/release-notes.md`. It has a special frontmatter property `layout: release-notes` and no Markdown content. The source of the release notes comes from YAML data. + +### YAML source + +The source data for the release notes lives in this directory (`data/release-notes/github-ae`). + +The directories are named by month. The YAML files are named by the data of a weekly release. + +A boolean property called `currentWeek` must be set in each YAML file. No more than one file at a time can have this property set to true. + +Note that patch files can be deprecated individually (i.e., hidden on the docs site) by an optional `deprecated: true` property. + +### Middleware processing + +The YAML data is processed and sorted by `middleware/contextualizers/release-notes.js` and added to the `context` object. + +### Layouts + +The `context` object data is rendered by `layouts/release-notes.html` and `includes/github-ae-release-notes.html`. + +The release notes page has a custom design with CSS in `stylesheets/release-notes.scss` and client-side JavaScript in `javascripts/release-notes.js`. + +### Schema + +The schema that validates the YAML data lives in `tests/helpers/schemas/ghae-release-notes-schema.js`. See the schema file to find out the required and optional properties. + +The schema is exercised by a test in `tests/linting/lint-files.js`. The test will fail if the data does not pass validation. diff --git a/data/ui.yml b/data/ui.yml index 261ade0760..f2bb008dea 100644 --- a/data/ui.yml +++ b/data/ui.yml @@ -18,10 +18,14 @@ header: please visit our English documentation. early_access: πŸ“£ Please do not share this URL publicly. This page contains content about an early access feature. - ghes_release_notes_use_latest: Please use the latest release for the latest security, performance, and bug fixes. - ghes_release_notes_upgrade_patch_only: πŸ“£ This is not the latest patch release of Enterprise Server. {% data ui.header.notices.ghes_release_notes_use_latest %} - ghes_release_notes_upgrade_release_only: πŸ“£ This is not the latest release of Enterprise Server. {% data ui.header.notices.ghes_release_notes_use_latest %} - ghes_release_notes_upgrade_patch_and_release: πŸ“£ This is not the latest patch release of this release series, and this is not the latest release of Enterprise Server. {% data ui.header.notices.ghes_release_notes_use_latest %} + release_notes_use_latest: Please use the latest release for the latest security, performance, and bug fixes. + # GHES release notes + ghes_release_notes_upgrade_patch_only: πŸ“£ This is not the latest patch release of Enterprise Server. {% data ui.header.notices.release_notes_use_latest %} + ghes_release_notes_upgrade_release_only: πŸ“£ This is not the latest release of Enterprise Server. {% data ui.header.notices.release_notes_use_latest %} + ghes_release_notes_upgrade_patch_and_release: πŸ“£ This is not the latest patch release of this release series, and this is not the latest release of Enterprise Server. {% data ui.header.notices.release_notes_use_latest %} +release_notes: + banner_text_current: These changes will roll out over the next one week. + banner_text_past: These changes rolled out to enterprises during the week of search: need_help: Need help? placeholder: Search topics, products... diff --git a/includes/enterprise-server-release-notes.html b/includes/enterprise-server-release-notes.html new file mode 100644 index 0000000000..88806054b0 --- /dev/null +++ b/includes/enterprise-server-release-notes.html @@ -0,0 +1,156 @@ +{% assign product = siteTree[currentLanguage][currentVersion].products[currentProduct] %} +{% assign currentVersionObject = allVersions[currentVersion] %} + +
    +
    +
    + {% if prevRelease %} + + {% octicon "chevron-left" %} {{ prevRelease }} + + {% else %} +
    + {% endif %} + +

    {{ currentVersionObject.planTitle }} {{ currentVersionObject.currentRelease }} release notes

    + + {% if nextRelease %} + + {{ nextRelease }} {% octicon "chevron-right" %} + + {% else %} +
    + {% endif %} +
    + +
    + {% for patch in releaseNotes %} +
    +
    +
    +

    + {{ currentVersionObject.versionTitle }}.{{ patch.patchVersion }} +

    + + {% if patch.release_candidate %} + Release Candidate + {% endif %} + + {% if currentVersionObject.plan == "enterprise-server" %} + + Download + + {% endif %} + + +
    + +

    {{ patch.date | date: "%B %d, %Y" }}

    + + {% if patch.version != latestPatch and currentVersionObject.currentRelease == latestRelease %} +

    {% data ui.header.notices.ghes_release_notes_upgrade_patch_only %}

    + {% endif %} + + {% if patch.version == latestPatch and currentVersionObject.currentRelease != latestRelease %} +

    {% data ui.header.notices.ghes_release_notes_upgrade_release_only %}

    + {% endif %} + + {% if patch.version != latestPatch and currentVersionObject.currentRelease != latestRelease %} +

    {% data ui.header.notices.ghes_release_notes_upgrade_patch_and_release %}

    + {% endif %} +
    + +
    +
    {{ patch.intro }}
    + + {% for section in patch.sections %} +
    +
    + {% include release-notes-category-label %} +
    + +
      + {% for note in section[1] %} +
    • + {% if note.heading %} + {% assign slug = note.heading | slugify %} + +

      + {{ note.heading }} +

      + +
        + {% for subNote in note.notes %} +
      • {{ subNote }}
      • + {% endfor %} +
      + {% else %} + {{ note }} + {% endif %} +
    • + {% endfor %} +
    +
    + {% endfor %} +
    +
    + {% endfor %} +
    +
    + + +
    diff --git a/includes/github-ae-release-notes.html b/includes/github-ae-release-notes.html new file mode 100644 index 0000000000..7446f4babc --- /dev/null +++ b/includes/github-ae-release-notes.html @@ -0,0 +1,104 @@ +{% assign product = siteTree[currentLanguage][currentVersion].products[currentProduct] %} +{% assign currentVersionObject = allVersions[currentVersion] %} + +
    +
    +
    +
    +

    {{ currentVersionObject.planTitle }} release notes

    +
    +
    + +
    + {% for patch in releaseNotes %} +
    +
    +
    +

    + {{ patch.title }} +

    + + {% if patch.release_candidate %} + Release Candidate + {% endif %} + + +
    + {% if patch.currentWeek %} + {% assign bannerText = site.data.ui.release_notes.banner_text_current %} + {% else %} + {% assign bannerText = site.data.ui.release_notes.banner_text_past | append: " " | append: patch.friendlyDate | append: "." %} + {% endif %} +

    {{ patch.friendlyDate }} - {{ bannerText }}

    +
    + +
    +
    {{ patch.intro }}
    + + {% for section in patch.sections %} +
    +
      + {% for note in section[1] %} +
    • + {% if note.heading %} + {% assign slug = note.heading | slugify %} + +

      + {{ note.heading }} +

      + +
        + {% for subNote in note.notes %} +
      • {{ subNote }}
      • + {% endfor %} +
      + {% else %} + {{ note }} + {% endif %} +
    • + {% endfor %} +
    +
    + {% endfor %} +
    +
    + {% endfor %} +
    +
    + + +
    diff --git a/includes/header-version-switcher.html b/includes/header-version-switcher.html index af5aebb0e1..b0b6b847a7 100644 --- a/includes/header-version-switcher.html +++ b/includes/header-version-switcher.html @@ -1,5 +1,5 @@ -{% if page.relativePath == 'index.md' or page.layout == 'product-landing' or page.layout == 'product-sublanding' %} +{% if page.relativePath == 'index.md' or page.layout == 'product-landing' or page.layout == 'product-sublanding' or page.layout == 'release-notes' %} {% if page.permalinks and page.permalinks.length > 1 %}
    diff --git a/layouts/release-notes.html b/layouts/release-notes.html index e7b9976d99..3678133561 100644 --- a/layouts/release-notes.html +++ b/layouts/release-notes.html @@ -1,5 +1,3 @@ -{% assign product = siteTree[currentLanguage][currentVersion].products[currentProduct] %} - {% include head %} @@ -11,156 +9,13 @@ {% include header %} {% include deprecation-banner %} -
    -
    -
    - {% if prevRelease %} - - {% octicon "chevron-left" %} {{ prevRelease }} - - {% else %} -
    - {% endif %} + {% if allVersions[currentVersion].plan == 'enterprise-server' %} + {% include enterprise-server-release-notes %} + {% endif %} -

    {{ currentVersion | version_num }} Release notes

    - - {% if nextRelease %} - - {{ nextRelease }} {% octicon "chevron-right" %} - - {% else %} -
    - {% endif %} -
    - -
    - {% for patch in releaseNotes %} -
    -
    -
    -

    - {{ allVersions[currentVersion].versionTitle }}.{{ patch.patchVersion }} -

    - - {% if patch.release_candidate %} - Release Candidate - {% endif %} - - - Download - - - -
    - -

    {{ patch.date | date: "%B %d, %Y" }}

    - - {% if patch.version != latestPatch and allVersions[currentVersion].currentRelease == latestRelease %} -

    {% data ui.header.notices.ghes_release_notes_upgrade_patch_only %}

    - {% endif %} - - {% if patch.version == latestPatch and allVersions[currentVersion].currentRelease != latestRelease %} -

    {% data ui.header.notices.ghes_release_notes_upgrade_release_only %}

    - {% endif %} - - {% if patch.version != latestPatch and allVersions[currentVersion].currentRelease != latestRelease %} -

    {% data ui.header.notices.ghes_release_notes_upgrade_patch_and_release %}

    - {% endif %} -
    - -
    -
    {{ patch.intro }}
    - - {% for section in patch.sections %} -
    -
    - {% include release-notes-category-label %} -
    - -
      - {% for note in section[1] %} -
    • - {% if note.heading %} - {% assign slug = note.heading | slugify %} - -

      - {{ note.heading }} -

      - -
        - {% for subNote in note.notes %} -
      • {{ subNote }}
      • - {% endfor %} -
      - {% else %} - {{ note }} - {% endif %} -
    • - {% endfor %} -
    -
    - {% endfor %} -
    -
    - {% endfor %} -
    -
    - - -
    + {% if allVersions[currentVersion].plan == 'github-ae' %} + {% include github-ae-release-notes %} + {% endif %} {% include support-section %} {% include small-footer %} diff --git a/lib/release-notes-utils.js b/lib/release-notes-utils.js new file mode 100644 index 0000000000..b62397e688 --- /dev/null +++ b/lib/release-notes-utils.js @@ -0,0 +1,103 @@ +const semver = require('semver') +const renderContent = require('./render-content') + +/** + * Turn { [key]: { notes, intro, date } } + * into [{ version, notes, intro, date }] + */ +function sortPatchKeys (release, version, options = {}) { + const keys = Object.keys(release) + .map(key => { + const keyWithDots = key.replace(/-/g, '.') + return { + version: `${version}.${keyWithDots}`, + patchVersion: keyWithDots, + release: version, // TODO this naming :/ we are not currently using this value, but we may want to. + ...release[key] + } + }) + // Filter out any deprecated patches + .filter(key => !key.deprecated) + + // Versions with numbered releases like GHES 2.22, 3.0, etc. need additional semver sorting; + // Versions with date releases need to be sorted by date. + return options.semverSort + ? semverSort(keys) + : keys.sort((a, b) => new Date(b.date) - new Date(a.date)) +} + +function semverSort (keys) { + return keys + .sort((a, b) => { + let aTemp = a.version + let bTemp = b.version + + // There's an RC version here, so doing regular semver + // comparisons won't work. So, we'll convert the incompatible version + // strings to real semver strings, then compare. + const [aBase, aRc] = a.version.split('.rc') + if (aRc) aTemp = `${aBase}-rc.${aRc}` + + const [bBase, bRc] = b.version.split('.rc') + if (bRc) bTemp = `${bBase}-rc.${bRc}` + + if (semver.gt(aTemp, bTemp)) return -1 + if (semver.lt(aTemp, bTemp)) return 1 + return 0 + }) +} + +/** + * Render each note in the given patch, by looping through the + * sections and rendering either `note` or `note.notes` in the + * case of a sub-section + */ +async function renderPatchNotes (patch, ctx) { + // Run the notes through the markdown rendering pipeline + for (const key in patch.sections) { + await Promise.all(patch.sections[key].map(async (noteOrHeading, index) => { + patch.sections[key][index] = typeof noteOrHeading === 'string' + ? await renderContent(noteOrHeading, ctx) + : { + ...noteOrHeading, + notes: await Promise.all(noteOrHeading.notes.map(note => renderContent(note, ctx))) + } + })) + } + + // Also render the patch's intro + if (patch.intro) { + patch.intro = await renderContent(patch.intro, ctx) + } + + return patch +} + +function sortReleasesByDate (releaseNotes) { + return Object.keys(releaseNotes) + .map(release => { + const [year, month] = release.split('-') + return { + name: release, + date: new Date(`20${year}`, month - 1, '1') + } + }) + .sort((releaseEntry1, releaseEntry2) => releaseEntry2.date - releaseEntry1.date) + .map(releaseEntry => releaseEntry.name) +} + +function getAllReleases (releases, releaseNotesPerPlan, hasNumberedReleases) { + return releases.map(version => { + const release = releaseNotesPerPlan[version.replace(/\./g, '-')] + if (!release) return { version } + const patches = sortPatchKeys(release, version, { semverSort: hasNumberedReleases }) + return { version, patches } + }) +} + +module.exports = { + sortReleasesByDate, + sortPatchKeys, + renderPatchNotes, + getAllReleases +} diff --git a/middleware/contextualizers/enterprise-release-notes.js b/middleware/contextualizers/enterprise-release-notes.js deleted file mode 100644 index 94e00e4cbf..0000000000 --- a/middleware/contextualizers/enterprise-release-notes.js +++ /dev/null @@ -1,124 +0,0 @@ -const semver = require('semver') -const renderContent = require('../../lib/render-content') -const patterns = require('../../lib/patterns') -const { all, firstReleaseNote, latest } = require('../../lib/enterprise-server-releases') - -// Display all GHES release notes, regardless of deprecation status, -// starting with the first release notes in 2.20 -const supported = all.filter(release => { - return semver.gte( - semver.coerce(release), semver.coerce(firstReleaseNote) - ) && release !== '11.10.340' -}) - -/** - * Turn { [key]: { notes, intro, date } } - * into [{ version, notes, intro, date }] - */ -function sortPatchKeys (release, version) { - const keys = Object.keys(release) - .map(key => { - const keyWithDots = key.replace(/-/g, '.') - return { - version: `${version}.${keyWithDots}`, - patchVersion: keyWithDots, - downloadVersion: `${version}.${keyWithDots.replace(/\.rc\d*$/, '')}`, - ...release[key] - } - }) - // Filter out any deprecated patches - .filter(key => !key.deprecated) - return keys - .sort((a, b) => { - let aTemp = a.version - let bTemp = b.version - - // There's an RC version here, so doing regular semver - // comparisons won't work. So, we'll convert the incompatible version - // strings to real semver strings, then compare. - const [aBase, aRc] = a.version.split('.rc') - if (aRc) aTemp = `${aBase}-rc.${aRc}` - - const [bBase, bRc] = b.version.split('.rc') - if (bRc) bTemp = `${bBase}-rc.${bRc}` - - if (semver.gt(aTemp, bTemp)) return -1 - if (semver.lt(aTemp, bTemp)) return 1 - return 0 - }) -} - -/** - * Render each note in the given patch, by looping through the - * sections and rendering either `note` or `note.notes` in the - * case of a sub-section - */ -async function renderPatchNotes (patch, ctx) { - // Run the notes through the markdown rendering pipeline - for (const key in patch.sections) { - await Promise.all(patch.sections[key].map(async (noteOrHeading, index) => { - patch.sections[key][index] = typeof noteOrHeading === 'string' - ? await renderContent(noteOrHeading, ctx) - : { - ...noteOrHeading, - notes: await Promise.all(noteOrHeading.notes.map(note => renderContent(note, ctx))) - } - })) - } - - // Also render the patch's intro - if (patch.intro) { - patch.intro = await renderContent(patch.intro, ctx) - } - - return patch -} - -module.exports = async function enterpriseReleaseNotesContext (req, res, next) { - // The `/release-notes` sub-path - if (!(req.path.endsWith('/release-notes') || req.path.endsWith('/admin'))) return next() - - // ignore paths that don't have an enterprise version number - if (!patterns.getEnterpriseServerNumber.test(req.path)) return next() - - // extract enterprise version from path, e.g. 2.16 - const requestedVersion = req.path.match(patterns.getEnterpriseServerNumber)[1] - - const versionString = `${requestedVersion.replace(/\./g, '-')}` - - const allReleaseNotes = req.context.site.data['release-notes'] - - // This version doesn't have any release notes - let's be helpful and redirect - // to the notes on `enterprise.github.com` - if (!allReleaseNotes || !allReleaseNotes[versionString]) { - return res.redirect(`https://enterprise.github.com/releases/${requestedVersion}.0/notes`) - } - - const releaseNotes = allReleaseNotes[versionString] - const patches = sortPatchKeys(releaseNotes, requestedVersion) - - req.context.releaseNotes = await Promise.all(patches.map(async patch => renderPatchNotes(patch, req.context))) - - // Put together information about other releases - req.context.releases = supported.map(version => { - const ret = { version } - if (!req.context.site.data['release-notes']) return ret - const release = req.context.site.data['release-notes'][version.replace(/\./g, '-')] - if (!release) return ret - const patches = sortPatchKeys(release, version) - - const firstPreviousRelease = all[all.findIndex(v => v === version) + 1] - const secondPreviousRelease = all[all.findIndex(v => v === firstPreviousRelease) + 1] - - return { ...ret, patches, firstPreviousRelease, secondPreviousRelease } - }) - - const releaseIndex = supported.findIndex(release => release === requestedVersion) - req.context.nextRelease = supported[releaseIndex - 1] - req.context.prevRelease = supported[releaseIndex + 1] - - req.context.latestPatch = patches[0].version - req.context.latestRelease = latest - - return next() -} diff --git a/middleware/contextualizers/release-notes.js b/middleware/contextualizers/release-notes.js new file mode 100644 index 0000000000..2a54833aab --- /dev/null +++ b/middleware/contextualizers/release-notes.js @@ -0,0 +1,74 @@ +const semver = require('semver') +const { all, latest, firstReleaseNote } = require('../../lib/enterprise-server-releases') +const { sortReleasesByDate, sortPatchKeys, renderPatchNotes, getAllReleases } = require('../../lib/release-notes-utils') + +// Display all GHES release notes, regardless of deprecation status, +// starting with the first release notes in 2.20 +const supported = all.filter(release => { + return semver.gte( + semver.coerce(release), semver.coerce(firstReleaseNote) + ) && release !== '11.10.340' +}) + +module.exports = async function releaseNotesContext (req, res, next) { + // The `/release-notes` sub-path + if (!(req.path.endsWith('/release-notes') || req.path.endsWith('/admin'))) return next() + + const [requestedPlan, requestedRelease] = req.context.currentVersion.split('@') + const releaseNotesPerPlan = req.context.site.data['release-notes'][requestedPlan] + + // 404 if no release notes can be found + if (!releaseNotesPerPlan) return next() + + // Release notes handling differs if version has numbered releases (like GHES) or not (like GHAE) + const hasNumberedReleases = !(requestedRelease === 'latest') + + // GHES gets handled here... + if (hasNumberedReleases) { + const currentReleaseNotes = releaseNotesPerPlan[`${requestedRelease.replace(/\./g, '-')}`] + + if (!currentReleaseNotes && req.path.endsWith('/release-notes')) { + // If the GHES version doesn't have any release notes, let's be helpful and redirect to `enterprise.github.com` + return requestedPlan === 'enterprise-server' + ? res.redirect(`https://enterprise.github.com/releases/${requestedRelease}.0/notes`) + : next() + } + + const patches = sortPatchKeys(currentReleaseNotes, requestedRelease, { semverSort: hasNumberedReleases }) + req.context.releaseNotes = await Promise.all(patches.map(async patch => renderPatchNotes(patch, req.context))) + req.context.releases = getAllReleases(supported, releaseNotesPerPlan, hasNumberedReleases) + + // Add firstPreviousRelease and secondPreviousRelease convenience props for use in includes/product-releases.html + req.context.releases.forEach(release => { + release.firstPreviousRelease = all[all.findIndex(v => v === release.version) + 1] + release.secondPreviousRelease = all[all.findIndex(v => v === release.firstPreviousRelease) + 1] + }) + + const releaseIndex = supported.findIndex(release => release === requestedRelease) + req.context.nextRelease = supported[releaseIndex - 1] + req.context.prevRelease = supported[releaseIndex + 1] + + req.context.latestPatch = patches[0].version + req.context.latestRelease = latest + } + + // GHAE gets handled here... + if (!hasNumberedReleases) { + const sortedReleases = sortReleasesByDate(releaseNotesPerPlan) + const sortedNotes = sortedReleases.map(release => sortPatchKeys(releaseNotesPerPlan[release], release, { semverSort: false })).flat() + + req.context.releaseNotes = await Promise.all(sortedNotes.map(async patch => renderPatchNotes(patch, req.context))) + req.context.releases = getAllReleases(sortedReleases, releaseNotesPerPlan, hasNumberedReleases) + // do some date format massaging, since we want the friendly date to render as the "version" + .map(r => { + const d = r.patches[0].friendlyDate.split(' ') + d.splice(1, 1) + r.version = d.join(' ') + return r + }) + + req.context.latestPatch = sortedNotes[0].date + } + + return next() +} diff --git a/middleware/index.js b/middleware/index.js index 2de057aa5f..7cffb68e5d 100644 --- a/middleware/index.js +++ b/middleware/index.js @@ -111,7 +111,7 @@ module.exports = function (app) { app.use(haltOnDroppedConnection) // *** Preparation for render-page: contextualizers *** - app.use(asyncMiddleware(instrument('./contextualizers/enterprise-release-notes'))) + app.use(asyncMiddleware(instrument('./contextualizers/release-notes'))) app.use(instrument('./contextualizers/graphql')) app.use(instrument('./contextualizers/rest')) app.use(instrument('./contextualizers/webhooks')) diff --git a/tests/helpers/schemas/ghae-release-notes-schema.js b/tests/helpers/schemas/ghae-release-notes-schema.js new file mode 100644 index 0000000000..4fcbc5e939 --- /dev/null +++ b/tests/helpers/schemas/ghae-release-notes-schema.js @@ -0,0 +1,71 @@ +const section = { + anyOf: [ + { + type: 'array', + items: { type: 'string' }, + minItems: 1 + }, + { + type: 'object', + properties: { + heading: { + type: 'string', + required: true + }, + notes: { + type: 'array', + items: { type: 'string' }, + required: true, + minItems: 1 + } + } + } + ] +} + +module.exports = { + properties: { + intro: { + type: 'string' + }, + date: { + type: 'string', + format: 'date', + required: true + }, + friendlyDate: { + type: 'string', + required: true + }, + title: { + type: 'string', + required: true + }, + currentWeek: { + type: 'boolean', + required: true + }, + release_candidate: { + type: 'boolean', + default: false + }, + deprecated: { + type: 'boolean', + default: false + }, + sections: { + required: true, + type: 'object', + minProperties: 1, + properties: [ + 'bugs', + 'known_issues', + 'features', + 'changes', + 'deprecations', + 'security_fixes', + 'backups' + ].reduce((prev, curr) => ({ ...prev, [curr]: section }), {}) + } + } +} diff --git a/tests/helpers/schemas/release-notes-schema.js b/tests/helpers/schemas/ghes-release-notes-schema.js similarity index 100% rename from tests/helpers/schemas/release-notes-schema.js rename to tests/helpers/schemas/ghes-release-notes-schema.js diff --git a/tests/linting/lint-files.js b/tests/linting/lint-files.js index 8db8a1c03e..5fcc88f212 100644 --- a/tests/linting/lint-files.js +++ b/tests/linting/lint-files.js @@ -10,7 +10,8 @@ const readFileAsync = require('../../lib/readfile-async') const frontmatter = require('../../lib/frontmatter') const languages = require('../../lib/languages') const { tags } = require('../../lib/liquid-tags/extended-markdown') -const ghesReleaseNotesSchema = require('../helpers/schemas/release-notes-schema') +const ghesReleaseNotesSchema = require('../helpers/schemas/ghes-release-notes-schema') +const ghaeReleaseNotesSchema = require('../helpers/schemas/ghae-release-notes-schema') const learningTracksSchema = require('../helpers/schemas/learning-tracks-schema') const renderContent = require('../../lib/render-content') const { execSync } = require('child_process') @@ -22,7 +23,8 @@ const contentDir = path.join(rootDir, 'content') const reusablesDir = path.join(rootDir, 'data/reusables') const variablesDir = path.join(rootDir, 'data/variables') const glossariesDir = path.join(rootDir, 'data/glossaries') -const ghesReleaseNotesDir = path.join(rootDir, 'data/release-notes') +const ghesReleaseNotesDir = path.join(rootDir, 'data/release-notes/enterprise-server') +const ghaeReleaseNotesDir = path.join(rootDir, 'data/release-notes/github-ae') const learningTracks = path.join(rootDir, 'data/learning-tracks') const languageCodes = Object.keys(languages) @@ -171,7 +173,7 @@ const yamlWalkOptions = { } // different lint rules apply to different content types -let mdToLint, ymlToLint, releaseNotesToLint, learningTracksToLint +let mdToLint, ymlToLint, ghesReleaseNotesToLint, ghaeReleaseNotesToLint, learningTracksToLint if (!process.env.TEST_TRANSLATION) { // compile lists of all the files we want to lint @@ -201,7 +203,12 @@ if (!process.env.TEST_TRANSLATION) { // GHES release notes const ghesReleaseNotesYamlAbsPaths = walk(ghesReleaseNotesDir, yamlWalkOptions).sort() const ghesReleaseNotesYamlRelPaths = ghesReleaseNotesYamlAbsPaths.map(p => slash(path.relative(rootDir, p))) - releaseNotesToLint = zip(ghesReleaseNotesYamlRelPaths, ghesReleaseNotesYamlAbsPaths) + ghesReleaseNotesToLint = zip(ghesReleaseNotesYamlRelPaths, ghesReleaseNotesYamlAbsPaths) + + // GHAE release notes + const ghaeReleaseNotesYamlAbsPaths = walk(ghaeReleaseNotesDir, yamlWalkOptions).sort() + const ghaeReleaseNotesYamlRelPaths = ghaeReleaseNotesYamlAbsPaths.map(p => slash(path.relative(rootDir, p))) + ghaeReleaseNotesToLint = zip(ghaeReleaseNotesYamlRelPaths, ghaeReleaseNotesYamlAbsPaths) // Learning tracks const learningTracksYamlAbsPaths = walk(learningTracks, yamlWalkOptions).sort() @@ -216,7 +223,7 @@ if (!process.env.TEST_TRANSLATION) { console.log(`Found ${changedFilesRelPaths.length} translated files.`) - const { mdRelPaths = [], ymlRelPaths = [], releaseNotesRelPaths = [], learningTracksRelPaths = [] } = groupBy(changedFilesRelPaths, (path) => { + const { mdRelPaths = [], ymlRelPaths = [], ghesReleaseNotesRelPaths = [], ghaeReleaseNotesRelPaths = [], learningTracksRelPaths = [] } = groupBy(changedFilesRelPaths, (path) => { // separate the changed files to different groups if (path.endsWith('README.md')) { return 'throwAway' @@ -224,8 +231,10 @@ if (!process.env.TEST_TRANSLATION) { return 'mdRelPaths' } else if (path.match(/\/data\/(variables|glossaries)\//i)) { return 'ymlRelPaths' - } else if (path.match(/\/data\/release-notes\//i)) { - return 'releaseNotesRelPaths' + } else if (path.match(/\/data\/release-notes\/enterprise-server/i)) { + return 'ghesReleaseNotesRelPaths' + } else if (path.match(/\/data\/release-notes\/github-ae/i)) { + return 'ghaeReleaseNotesRelPaths' } else if (path.match(/\data\/learning-tracks/)) { return 'learningTracksRelPaths' } else { @@ -234,14 +243,15 @@ if (!process.env.TEST_TRANSLATION) { } }) - const [mdTuples, ymlTuples, releaseNotesTuples, learningTracksTuples] = [mdRelPaths, ymlRelPaths, releaseNotesRelPaths, learningTracksRelPaths].map(relPaths => { + const [mdTuples, ymlTuples, ghesReleaseNotesTuples, ghaeReleaseNotesTuples, learningTracksTuples] = [mdRelPaths, ymlRelPaths, ghesReleaseNotesRelPaths, ghaeReleaseNotesRelPaths, learningTracksRelPaths].map(relPaths => { const absPaths = relPaths.map(p => path.join(rootDir, p)) return zip(relPaths, absPaths) }) mdToLint = mdTuples ymlToLint = ymlTuples - releaseNotesToLint = releaseNotesTuples + ghesReleaseNotesToLint = ghesReleaseNotesTuples + ghaeReleaseNotesToLint = ghaeReleaseNotesTuples learningTracksToLint = learningTracksTuples } @@ -661,9 +671,9 @@ describe('lint yaml content', () => { ) }) -describe('lint release notes', () => { - if (releaseNotesToLint.length < 1) return - describe.each(releaseNotesToLint)( +describe('lint GHES release notes', () => { + if (ghesReleaseNotesToLint.length < 1) return + describe.each(ghesReleaseNotesToLint)( '%s', (yamlRelPath, yamlAbsPath) => { let dictionary @@ -707,6 +717,59 @@ describe('lint release notes', () => { ) }) +describe('lint GHAE release notes', () => { + if (ghaeReleaseNotesToLint.length < 1) return + const currentWeeksFound = [] + describe.each(ghaeReleaseNotesToLint)( + '%s', + (yamlRelPath, yamlAbsPath) => { + let dictionary + + beforeAll(async () => { + const fileContents = await readFileAsync(yamlAbsPath, 'utf8') + dictionary = yaml.load(fileContents, { filename: yamlRelPath }) + }) + + it('matches the schema', () => { + const { errors } = revalidator.validate(dictionary, ghaeReleaseNotesSchema) + const errorMessage = errors.map(error => `- [${error.property}]: ${error.actual}, ${error.message}`).join('\n') + expect(errors.length, errorMessage).toBe(0) + }) + + it('does not have more than one yaml file with currentWeek set to true', () => { + if (dictionary.currentWeek) currentWeeksFound.push(yamlRelPath) + const errorMessage = `Found more than one file with currentWeek set to true: ${currentWeeksFound.join('\n')}` + expect(currentWeeksFound.length, errorMessage).not.toBeGreaterThan(1) + }) + + it('contains valid liquid', () => { + const { intro, sections } = dictionary + let toLint = { intro } + for (const key in sections) { + const section = sections[key] + const label = `sections.${key}` + section.forEach((part) => { + if (Array.isArray(part)) { + toLint = { ...toLint, ...{ [label]: section.join('\n') } } + } else { + for (const prop in section) { + toLint = { ...toLint, ...{ [`${label}.${prop}`]: section[prop] } } + } + } + }) + } + + for (const key in toLint) { + if (!toLint[key]) continue + expect(() => renderContent.liquid.parse(toLint[key]), `${key} contains invalid liquid`) + .not + .toThrow() + } + }) + } + ) +}) + describe('lint learning tracks', () => { if (learningTracksToLint.length < 1) return describe.each(learningTracksToLint)( diff --git a/tests/routing/enterprise-release-notes.js b/tests/routing/release-notes.js similarity index 80% rename from tests/routing/enterprise-release-notes.js rename to tests/routing/release-notes.js index 6a8f6c26b3..bbf08d1e2a 100644 --- a/tests/routing/enterprise-release-notes.js +++ b/tests/routing/release-notes.js @@ -1,6 +1,6 @@ const { get } = require('../helpers/supertest') -describe('enterprise release notes', () => { +describe('release notes', () => { jest.setTimeout(60 * 1000) beforeAll(async () => { @@ -16,9 +16,13 @@ describe('enterprise release notes', () => { expect(res.headers.location).toBe('https://enterprise.github.com/releases/2.19.0/notes') }) - // We can't write this test until we have real release notes it('renders the release-notes layout if this version\'s release notes are in this repo', async () => { const res = await get('/en/enterprise-server@2.22/admin/release-notes') expect(res.statusCode).toBe(200) }) + + it('renders the release-notes layout for GitHub AE', async () => { + const res = await get('/en/github-ae@latest/admin/release-notes') + expect(res.statusCode).toBe(200) + }) })