@@ -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 (
|
||||
<li
|
||||
|
||||
@@ -22,7 +22,7 @@ featuredLinks:
|
||||
- '{% if currentVersion == "github-ae@latest" %}/admin/user-management/customizing-user-messages-for-your-enterprise{% endif %}'
|
||||
- '{% if currentVersion == "github-ae@latest" %}/admin/github-actions/getting-started-with-github-actions-for-github-ae{% endif %}'
|
||||
popular:
|
||||
- '{% if currentVersion == "github-ae@latest" %}/admin/overview/github-ae-release-notes{% endif %}'
|
||||
- '{% if currentVersion == "github-ae@latest" %}/admin/release-notes{% endif %}'
|
||||
- '{% if enterpriseServerVersions contains currentVersion %}/github/getting-started-with-github/setting-up-a-trial-of-github-enterprise-server{% endif %}'
|
||||
- '{% if enterpriseServerVersions contains currentVersion %}/admin/installation{% endif %}'
|
||||
- '{% if currentVersion == "github-ae@latest" %}/admin/authentication/configuring-authentication-and-provisioning-for-your-enterprise-using-azure-ad{% endif %}'
|
||||
@@ -50,14 +50,3 @@ children:
|
||||
- /release-notes
|
||||
- /all-releases
|
||||
---
|
||||
<!-- . -->
|
||||
<!-- . -->
|
||||
<!-- . -->
|
||||
<!-- . -->
|
||||
<!-- . -->
|
||||
<!-- . -->
|
||||
<!-- . -->
|
||||
<!-- . -->
|
||||
<!-- . -->
|
||||
<!-- . -->
|
||||
<!-- . -->
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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.
|
||||
@@ -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).
|
||||
|
||||
@@ -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
|
||||
---
|
||||
|
||||
|
||||
@@ -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:
|
||||
- |
|
||||
37
data/release-notes/enterprise-server/README.md
Normal file
37
data/release-notes/enterprise-server/README.md
Normal file
@@ -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<num>.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.
|
||||
83
data/release-notes/github-ae/2021-03/2021-03-03.yml
Normal file
83
data/release-notes/github-ae/2021-03/2021-03-03.yml
Normal file
@@ -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.
|
||||
35
data/release-notes/github-ae/README.md
Normal file
35
data/release-notes/github-ae/README.md
Normal file
@@ -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.
|
||||
12
data/ui.yml
12
data/ui.yml
@@ -18,10 +18,14 @@ header:
|
||||
please visit our
|
||||
<a id="to-english-doc" href="/en">English documentation</a>.
|
||||
early_access: 📣 Please <b>do not share</b> 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 <a href="#{{ latestPatch }}">latest patch release</a> of Enterprise Server. {% data ui.header.notices.ghes_release_notes_use_latest %}
|
||||
ghes_release_notes_upgrade_release_only: 📣 This is not the <a href="/enterprise-server@{{ latestRelease }}/admin/release-notes">latest release</a> of Enterprise Server. {% data ui.header.notices.ghes_release_notes_use_latest %}
|
||||
ghes_release_notes_upgrade_patch_and_release: 📣 This is not the <a href="#{{ latestPatch }}">latest patch release</a> of this release series, and this is not the <a href="/enterprise-server@{{ latestRelease }}/admin/release-notes">latest release</a> 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 <a href="#{{ latestPatch }}">latest patch release</a> of Enterprise Server. {% data ui.header.notices.release_notes_use_latest %}
|
||||
ghes_release_notes_upgrade_release_only: 📣 This is not the <a href="/enterprise-server@{{ latestRelease }}/admin/release-notes">latest release</a> of Enterprise Server. {% data ui.header.notices.release_notes_use_latest %}
|
||||
ghes_release_notes_upgrade_patch_and_release: 📣 This is not the <a href="#{{ latestPatch }}">latest patch release</a> of this release series, and this is not the <a href="/enterprise-server@{{ latestRelease }}/admin/release-notes">latest release</a> 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...
|
||||
|
||||
156
includes/enterprise-server-release-notes.html
Normal file
156
includes/enterprise-server-release-notes.html
Normal file
@@ -0,0 +1,156 @@
|
||||
{% assign product = siteTree[currentLanguage][currentVersion].products[currentProduct] %}
|
||||
{% assign currentVersionObject = allVersions[currentVersion] %}
|
||||
|
||||
<div class="d-flex">
|
||||
<article class="min-width-0 flex-1">
|
||||
<div class="d-flex flex-items-center flex-justify-between color-bg-primary text-bold px-5 py-2">
|
||||
{% if prevRelease %}
|
||||
<a
|
||||
class="btn btn-outline"
|
||||
href="/{{ currentLanguage }}/{{ currentVersionObject.plan }}@{{ prevRelease }}/{{ currentProduct }}/release-notes">
|
||||
{% octicon "chevron-left" %} {{ prevRelease }}
|
||||
</a>
|
||||
{% else %}
|
||||
<div></div>
|
||||
{% endif %}
|
||||
|
||||
<h1 class="f4 py-3 m-0">{{ currentVersionObject.planTitle }} {{ currentVersionObject.currentRelease }} release notes</h1>
|
||||
|
||||
{% if nextRelease %}
|
||||
<a
|
||||
class="btn btn-outline"
|
||||
href="/{{ currentLanguage }}/{{ currentVersionObject.plan }}@{{ nextRelease }}/{{ currentProduct }}/release-notes">
|
||||
{{ nextRelease }} {% octicon "chevron-right" %}
|
||||
</a>
|
||||
{% else %}
|
||||
<div></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="markdown-body">
|
||||
{% for patch in releaseNotes %}
|
||||
<div class="js-release-notes-patch mb-10 color-bg-secondary pb-6 border-bottom border-top" data-version="{{ patch.version }}" id="{{ patch.version }}">
|
||||
<header style="z-index: 1;" class="container-xl position-sticky top-0 color-bg-secondary border-bottom px-3 pt-4 pb-2 js-release-notes-patch-heading" data-version="{{ patch.version }}">
|
||||
<div class="d-flex flex-items-center">
|
||||
<h2 class="border-bottom-0 m-0 p-0">
|
||||
{{ currentVersionObject.versionTitle }}.{{ patch.patchVersion }}
|
||||
</h2>
|
||||
|
||||
{% if patch.release_candidate %}
|
||||
<span class="IssueLabel color-bg-warning-inverse color-text-inverse ml-3" style="white-space: pre">Release Candidate</span>
|
||||
{% endif %}
|
||||
|
||||
{% if currentVersionObject.plan == "enterprise-server" %}
|
||||
<a href="https://enterprise.github.com/releases/{{ patch.downloadVersion }}/download" class="ml-3 text-small text-bold">
|
||||
Download
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
<button class="js-print btn-link ml-3 text-small text-bold">
|
||||
Print
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p class="color-text-secondary mt-1">{{ patch.date | date: "%B %d, %Y" }}</p>
|
||||
|
||||
{% if patch.version != latestPatch and currentVersionObject.currentRelease == latestRelease %}
|
||||
<p class="color-text-secondary mt-1">{% data ui.header.notices.ghes_release_notes_upgrade_patch_only %}</p>
|
||||
{% endif %}
|
||||
|
||||
{% if patch.version == latestPatch and currentVersionObject.currentRelease != latestRelease %}
|
||||
<p class="color-text-secondary mt-1">{% data ui.header.notices.ghes_release_notes_upgrade_release_only %}</p>
|
||||
{% endif %}
|
||||
|
||||
{% if patch.version != latestPatch and currentVersionObject.currentRelease != latestRelease %}
|
||||
<p class="color-text-secondary mt-1">{% data ui.header.notices.ghes_release_notes_upgrade_patch_and_release %}</p>
|
||||
{% endif %}
|
||||
</header>
|
||||
|
||||
<div class="container-xl px-3">
|
||||
<div class="mt-3">{{ patch.intro }}</div>
|
||||
|
||||
{% for section in patch.sections %}
|
||||
<div class="release-notes-section-{{ section[0] }} py-6 d-block d-xl-flex gutter-xl flex-items-baseline {% unless forloop.last %}border-bottom{% endunless %}">
|
||||
<div class="col-12 col-xl-3 mb-5">
|
||||
{% include release-notes-category-label %}
|
||||
</div>
|
||||
|
||||
<ul class="col-12 col-xl-9 release-notes-list">
|
||||
{% for note in section[1] %}
|
||||
<li class="release-notes-list-item {% if note.heading %}list-style-none{% endif %}">
|
||||
{% if note.heading %}
|
||||
{% assign slug = note.heading | slugify %}
|
||||
|
||||
<h4 id="{{ slug }}" class="release-notes-section-heading text-uppercase text-bold">
|
||||
<a href="#{{ slug }}" class="text-inherit">{{ note.heading }}</a>
|
||||
</h4>
|
||||
|
||||
<ul class="pl-0 pb-4 mt-2 release-notes-list">
|
||||
{% for subNote in note.notes %}
|
||||
<li class="release-notes-list-item">{{ subNote }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
{{ note }}
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<aside
|
||||
class="markdown-body position-sticky top-0 d-none d-md-block border-left no-print color-bg-primary flex-shrink-0"
|
||||
style="width: 260px; height: 100vh;"
|
||||
>
|
||||
<nav class="height-full overflow-auto">
|
||||
<ul class="list-style-none pl-0 text-bold">
|
||||
{% for release in releases %}
|
||||
{% capture releaseLink %}/{{ currentLanguage }}/{{ currentVersionObject.plan }}@{{ release.version }}/{{ currentProduct }}/release-notes{% endcapture %}
|
||||
<li class="border-bottom">
|
||||
{% if release.patches %}
|
||||
{% if release.version == currentVersionObject.currentRelease %}
|
||||
<details class="my-0 details-reset release-notes-version-picker" aria-current="page" open>
|
||||
<summary class="px-3 py-4 my-0 d-flex flex-items-center flex-justify-between">
|
||||
{{ release.version }}
|
||||
<div class="d-flex">
|
||||
<span class="color-text-tertiary text-mono text-small text-normal mr-1">{{ release.patches | size }} releases</span>
|
||||
{% octicon "chevron-down" %}
|
||||
</div>
|
||||
</summary>
|
||||
<ul class="color-bg-tertiary border-top list-style-none py-4 px-0 my-0">
|
||||
{% for patch in release.patches %}
|
||||
<li class="js-release-notes-patch-link px-3 my-0 py-1" data-version="{{ patch.version }}">
|
||||
<a
|
||||
href="{{ releaseLink }}#{{ patch.version }}"
|
||||
class="d-flex flex-items-center flex-justify-between">
|
||||
{{ patch.version }}
|
||||
<span class="color-text-tertiary text-mono text-small text-normal">{{ patch.date | date: "%B %d, %Y" }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</details>
|
||||
{% else %}
|
||||
<a class="px-3 py-4 my-0 d-flex flex-items-center flex-justify-between" href="{{ releaseLink }}">
|
||||
{{ release.version }}
|
||||
<span class="color-text-tertiary text-mono text-small text-normal mr-1">{{ release.patches | size }} releases</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<a href="{{ releaseLink }}" class="Link--primary no-underline px-3 py-4 my-0 d-flex flex-items-center flex-justify-between">
|
||||
{{ release.version }}
|
||||
{% octicon "link-external" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</nav>
|
||||
</aside>
|
||||
</div>
|
||||
104
includes/github-ae-release-notes.html
Normal file
104
includes/github-ae-release-notes.html
Normal file
@@ -0,0 +1,104 @@
|
||||
{% assign product = siteTree[currentLanguage][currentVersion].products[currentProduct] %}
|
||||
{% assign currentVersionObject = allVersions[currentVersion] %}
|
||||
|
||||
<div class="d-flex">
|
||||
<article class="min-width-0 flex-1">
|
||||
<div class="d-flex flex-items-center flex-justify-between color-bg-primary px-5 py-2">
|
||||
<div></div>
|
||||
<h1 class="f4 py-3 m-0">{{ currentVersionObject.planTitle }} release notes</h1>
|
||||
<div></div>
|
||||
</div>
|
||||
|
||||
<div class="markdown-body">
|
||||
{% for patch in releaseNotes %}
|
||||
<div class="js-release-notes-patch mb-10 color-bg-secondary pb-6 border-bottom border-top" data-version="{{ patch.date }}" id="{{ patch.date }}">
|
||||
<header style="z-index: 1;" class="container-xl position-sticky top-0 color-bg-secondary border-bottom px-3 pt-4 pb-2 js-release-notes-patch-heading" data-version="{{ patch.date }}">
|
||||
<div class="d-flex flex-items-center">
|
||||
<h2 class="border-bottom-0 m-0 p-0">
|
||||
{{ patch.title }}
|
||||
</h2>
|
||||
|
||||
{% if patch.release_candidate %}
|
||||
<span class="IssueLabel color-bg-warning-inverse color-text-inverse ml-3" style="white-space: pre">Release Candidate</span>
|
||||
{% endif %}
|
||||
|
||||
<button class="js-print btn-link ml-3 text-small text-bold">
|
||||
Print
|
||||
</button>
|
||||
</div>
|
||||
{% 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 %}
|
||||
<p class="color-text-secondary mt-1">{{ patch.friendlyDate }} - {{ bannerText }}</p>
|
||||
</header>
|
||||
|
||||
<div class="container-xl px-3">
|
||||
<div class="mt-3">{{ patch.intro }}</div>
|
||||
|
||||
{% for section in patch.sections %}
|
||||
<div class="release-notes-section-{{ section[0] }} py-6 mx-6 d-block d-xl-flex gutter-xl flex-items-baseline {% unless forloop.last %}border-bottom{% endunless %}">
|
||||
<ul class="col-12 release-notes-list">
|
||||
{% for note in section[1] %}
|
||||
<li class="release-notes-list-item {% if note.heading %}list-style-none{% endif %}">
|
||||
{% if note.heading %}
|
||||
{% assign slug = note.heading | slugify %}
|
||||
|
||||
<h4 id="{{ slug }}" class="release-notes-section-heading text-uppercase text-bold">
|
||||
<a href="#{{ slug }}" class="text-inherit">{{ note.heading }}</a>
|
||||
</h4>
|
||||
|
||||
<ul class="pl-0 pb-4 mt-2 release-notes-list">
|
||||
{% for subNote in note.notes %}
|
||||
<li class="release-notes-list-item">{{ subNote }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
{{ note }}
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<aside
|
||||
class="markdown-body position-sticky top-0 d-none d-md-block border-left no-print color-bg-primary flex-shrink-0"
|
||||
style="width: 260px; height: 100vh;"
|
||||
>
|
||||
<nav class="height-full overflow-auto">
|
||||
<ul class="list-style-none pl-0 text-bold">
|
||||
{% for release in releases %}
|
||||
<li class="border-bottom">
|
||||
<details class="my-0 details-reset release-notes-version-picker" aria-current="page" open>
|
||||
<summary class="px-3 py-4 my-0 d-flex flex-items-center flex-justify-between">
|
||||
{{ release.version }}
|
||||
<div class="d-flex">
|
||||
<span class="color-text-tertiary text-mono text-small text-normal mr-1">{{ release.patches | size }} releases</span>
|
||||
{% octicon "chevron-down" %}
|
||||
</div>
|
||||
</summary>
|
||||
<ul class="color-bg-tertiary border-top list-style-none py-4 px-0 my-0">
|
||||
{% for patch in release.patches %}
|
||||
<li class="js-release-notes-patch-link px-3 my-0 py-1" data-version="{{ patch.date }}">
|
||||
<a
|
||||
href="#{{ patch.date }}"
|
||||
class="d-flex flex-items-center flex-justify-between">
|
||||
{{ patch.friendlyDate }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</details>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</nav>
|
||||
</aside>
|
||||
</div>
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- Versions picker that only appears in the header on homepage or product landing -->
|
||||
{% 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 %}
|
||||
<div class="d-md-inline-block">
|
||||
<div class="border-top border-md-top-0 py-2 py-md-0 d-md-inline-block">
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
{% assign product = siteTree[currentLanguage][currentVersion].products[currentProduct] %}
|
||||
|
||||
<!doctype html>
|
||||
<html lang="{{currentLanguage}}" data-color-mode="$COLORMODE$" data-dark-theme="$DARKTHEME$" data-light-theme="$LIGHTTHEME$">
|
||||
{% include head %}
|
||||
@@ -11,156 +9,13 @@
|
||||
{% include header %}
|
||||
{% include deprecation-banner %}
|
||||
|
||||
<div class="d-flex">
|
||||
<article class="min-width-0 flex-1">
|
||||
<div class="d-flex flex-items-center flex-justify-between color-bg-primary text-bold px-5 py-2">
|
||||
{% if prevRelease %}
|
||||
<a
|
||||
class="btn btn-outline"
|
||||
href="/{{ currentLanguage }}/{{ allVersions[currentVersion].plan }}@{{ prevRelease }}/{{ currentProduct }}/release-notes">
|
||||
{% octicon "chevron-left" %} {{ prevRelease }}
|
||||
</a>
|
||||
{% else %}
|
||||
<div></div>
|
||||
{% endif %}
|
||||
{% if allVersions[currentVersion].plan == 'enterprise-server' %}
|
||||
{% include enterprise-server-release-notes %}
|
||||
{% endif %}
|
||||
|
||||
<h1 class="f4 py-3 m-0">{{ currentVersion | version_num }} Release notes</h1>
|
||||
|
||||
{% if nextRelease %}
|
||||
<a
|
||||
class="btn btn-outline"
|
||||
href="/{{ currentLanguage }}/{{ allVersions[currentVersion].plan }}@{{ nextRelease }}/{{ currentProduct }}/release-notes">
|
||||
{{ nextRelease }} {% octicon "chevron-right" %}
|
||||
</a>
|
||||
{% else %}
|
||||
<div></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="markdown-body">
|
||||
{% for patch in releaseNotes %}
|
||||
<div class="js-release-notes-patch mb-10 color-bg-secondary pb-6 border-bottom border-top" data-version="{{ patch.version }}" id="{{ patch.version }}">
|
||||
<header style="z-index: 1;" class="container-xl position-sticky top-0 color-bg-secondary border-bottom px-3 pt-4 pb-2 js-release-notes-patch-heading" data-version="{{ patch.version }}">
|
||||
<div class="d-flex flex-items-center">
|
||||
<h2 class="border-bottom-0 m-0 p-0">
|
||||
{{ allVersions[currentVersion].versionTitle }}.{{ patch.patchVersion }}
|
||||
</h2>
|
||||
|
||||
{% if patch.release_candidate %}
|
||||
<span class="IssueLabel color-bg-warning-inverse color-text-inverse ml-3" style="white-space: pre">Release Candidate</span>
|
||||
{% endif %}
|
||||
|
||||
<a href="https://enterprise.github.com/releases/{{ patch.downloadVersion }}/download" class="ml-3 text-small text-bold">
|
||||
Download
|
||||
</a>
|
||||
|
||||
<button class="js-print btn-link ml-3 text-small text-bold">
|
||||
Print
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p class="color-text-secondary mt-1">{{ patch.date | date: "%B %d, %Y" }}</p>
|
||||
|
||||
{% if patch.version != latestPatch and allVersions[currentVersion].currentRelease == latestRelease %}
|
||||
<p class="color-text-secondary mt-1">{% data ui.header.notices.ghes_release_notes_upgrade_patch_only %}</p>
|
||||
{% endif %}
|
||||
|
||||
{% if patch.version == latestPatch and allVersions[currentVersion].currentRelease != latestRelease %}
|
||||
<p class="color-text-secondary mt-1">{% data ui.header.notices.ghes_release_notes_upgrade_release_only %}</p>
|
||||
{% endif %}
|
||||
|
||||
{% if patch.version != latestPatch and allVersions[currentVersion].currentRelease != latestRelease %}
|
||||
<p class="color-text-secondary mt-1">{% data ui.header.notices.ghes_release_notes_upgrade_patch_and_release %}</p>
|
||||
{% endif %}
|
||||
</header>
|
||||
|
||||
<div class="container-xl px-3">
|
||||
<div class="mt-3">{{ patch.intro }}</div>
|
||||
|
||||
{% for section in patch.sections %}
|
||||
<div class="release-notes-section-{{ section[0] }} py-6 d-block d-xl-flex gutter-xl flex-items-baseline {% unless forloop.last %}border-bottom{% endunless %}">
|
||||
<div class="col-12 col-xl-3 mb-5">
|
||||
{% include release-notes-category-label %}
|
||||
</div>
|
||||
|
||||
<ul class="col-12 col-xl-9 release-notes-list">
|
||||
{% for note in section[1] %}
|
||||
<li class="release-notes-list-item {% if note.heading %}list-style-none{% endif %}">
|
||||
{% if note.heading %}
|
||||
{% assign slug = note.heading | slugify %}
|
||||
|
||||
<h4 id="{{ slug }}" class="release-notes-section-heading text-uppercase text-bold">
|
||||
<a href="#{{ slug }}" class="text-inherit">{{ note.heading }}</a>
|
||||
</h4>
|
||||
|
||||
<ul class="pl-0 pb-4 mt-2 release-notes-list">
|
||||
{% for subNote in note.notes %}
|
||||
<li class="release-notes-list-item">{{ subNote }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
{{ note }}
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<aside
|
||||
class="markdown-body position-sticky top-0 d-none d-md-block border-left no-print color-bg-primary flex-shrink-0"
|
||||
style="width: 260px; height: 100vh;"
|
||||
>
|
||||
<nav class="height-full overflow-auto">
|
||||
<ul class="list-style-none pl-0 text-bold">
|
||||
{% for release in releases %}
|
||||
<li class="border-bottom">
|
||||
{% if release.patches %}
|
||||
{% if release.version == allVersions[currentVersion].currentRelease %}
|
||||
<details class="my-0 details-reset release-notes-version-picker" aria-current="page" open>
|
||||
<summary class="px-3 py-4 my-0 d-flex flex-items-center flex-justify-between">
|
||||
{{ release.version }}
|
||||
<div class="d-flex">
|
||||
<span class="color-text-tertiary text-mono text-small text-normal mr-1">{{ release.patches | size }} releases</span>
|
||||
{% octicon "chevron-down" %}
|
||||
</div>
|
||||
</summary>
|
||||
<ul class="color-bg-tertiary border-top list-style-none py-4 px-0 my-0">
|
||||
{% for patch in release.patches %}
|
||||
<li class="js-release-notes-patch-link px-3 my-0 py-1" data-version="{{ patch.version }}">
|
||||
<a
|
||||
href="/{{ currentLanguage }}/{{ allVersions[currentVersion].plan }}@{{ release.version }}/{{ currentProduct }}/release-notes#{{ patch.version }}"
|
||||
class="d-flex flex-items-center flex-justify-between">
|
||||
{{ patch.version }}
|
||||
<span class="color-text-tertiary text-mono text-small text-normal">{{ patch.date | date: "%B %d, %Y" }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</details>
|
||||
{% else %}
|
||||
<a class="px-3 py-4 my-0 d-flex flex-items-center flex-justify-between" href="/{{ currentLanguage }}/{{ allVersions[currentVersion].plan }}@{{ release.version }}/{{ currentProduct }}/release-notes">
|
||||
{{ release.version }}
|
||||
<span class="color-text-tertiary text-mono text-small text-normal mr-1">{{ release.patches | size }} releases</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<a href="/{{ currentLanguage }}/{{ allVersions[currentVersion].plan }}@{{ release.version }}/{{ currentProduct }}/release-notes" class="Link--primary no-underline px-3 py-4 my-0 d-flex flex-items-center flex-justify-between">
|
||||
{{ release.version }}
|
||||
{% octicon "link-external" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</nav>
|
||||
</aside>
|
||||
</div>
|
||||
{% if allVersions[currentVersion].plan == 'github-ae' %}
|
||||
{% include github-ae-release-notes %}
|
||||
{% endif %}
|
||||
|
||||
{% include support-section %}
|
||||
{% include small-footer %}
|
||||
|
||||
103
lib/release-notes-utils.js
Normal file
103
lib/release-notes-utils.js
Normal file
@@ -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
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
74
middleware/contextualizers/release-notes.js
Normal file
74
middleware/contextualizers/release-notes.js
Normal file
@@ -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()
|
||||
}
|
||||
@@ -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'))
|
||||
|
||||
71
tests/helpers/schemas/ghae-release-notes-schema.js
Normal file
71
tests/helpers/schemas/ghae-release-notes-schema.js
Normal file
@@ -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 }), {})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)(
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user