1
0
mirror of synced 2025-12-23 21:07:12 -05:00

Branch was updated using the 'autoupdate branch' Actions workflow.

This commit is contained in:
Octomerger Bot
2020-12-14 13:03:55 -08:00
committed by GitHub
20 changed files with 146 additions and 184 deletions

View File

@@ -4,7 +4,7 @@ versions:
free-pro-team: '*' free-pro-team: '*'
--- ---
Version Effective Date: December 7, 2020 Version Effective Date: December 14, 2020
When you create an Account, you're given access to lots of different features and products that are all a part of the Service. Because many of these features and products offer different functionality, they may require additional terms and conditions specific to that feature or product. Below, we've listed those features and products, along with the corresponding additional terms that apply to your use of them. When you create an Account, you're given access to lots of different features and products that are all a part of the Service. Because many of these features and products offer different functionality, they may require additional terms and conditions specific to that feature or product. Below, we've listed those features and products, along with the corresponding additional terms that apply to your use of them.
@@ -46,13 +46,11 @@ GitHub Pages are subject to some specific bandwidth and usage limits, and may no
### 5. Actions and Packages ### 5. Actions and Packages
#### a. Actions Usage #### a. Actions Usage
GitHub Actions enables you to create custom software development lifecycle workflows directly in your GitHub repository. Each Account comes with included compute and storage quantities for use with Actions, depending on your Account plan, which can be found in the [Actions documentation](/actions). Your Actions compute usage is displayed within [your account settings](https://github.com/settings/billing), and you will be notified by email in advance of reaching the limit of your included quantities. If you want to use Actions beyond your included quantities, then you may [enable overages](/github/setting-up-and-managing-billing-and-payments-on-github/about-billing-for-github-actions). GitHub Actions is billed on a usage basis. The [Actions documentation](/actions) includes details, including compute and storage quantities (depending on your Account plan), and how to monitor your Actions minutes usage and set usage limits.
Compute usage for included and paid quantities is calculated in minutes based on the type of Actions you execute (e.g. Linux, Windows, macOS). The minutes used for each Action's job or task will be rounded up to the nearest minute. For included quantities and depending on the type of Action, a multiplier may be applied to the amount of time used to execute each job or task, prior to being rounded up to the nearest minute. Overage minutes are charged at the [stated per minute price](https://github.com/features/actions) based on the type of Actions you execute. Actions and Packages share storage, and your storage usage is displayed within your [account settings](https://github.com/settings/billing). For additional details about included quantity usage calculations, see the [Actions documentation](/actions). Actions and any elements of the Actions service may not be used in violation of the Agreement, the [GitHub Acceptable Use Polices](/github/site-policy/github-acceptable-use-policies), or the GitHub Actions service limitations set forth in the [Actions documentation](/actions). Additionally, Actions should not be used for:
Actions and any elements of the Action service may not be used in violation of the Agreement, the [Acceptable Use Policy](/github/site-policy/github-acceptable-use-policies), or the GitHub Actions [service limitations](/actions/reference/usage-limits-billing-and-administration/#usage-limits). Additionally, Actions should not be used for:
- cryptomining; - cryptomining;
- using our servers to disrupt, or to gain or to attempt to gain unauthorized access to, any service, device, data, account or network (other than those authorized by the [GitHub Bug Bounty program](https://bounty.github.com)) - using our servers to disrupt, or to gain or to attempt to gain unauthorized access to, any service, device, data, account, or network (other than those authorized by the [GitHub Bug Bounty program](https://bounty.github.com));
- the provision of a stand-alone or integrated application or service offering Actions or any elements of Actions for commercial purposes; - the provision of a stand-alone or integrated application or service offering Actions or any elements of Actions for commercial purposes;
- any activity that places a burden on our servers, where that burden is disproportionate to the benefits provided to users (for example, don't use Actions as a content delivery network or as part of a serverless application, but a low benefit Action could be ok if its also low burden); or - any activity that places a burden on our servers, where that burden is disproportionate to the benefits provided to users (for example, don't use Actions as a content delivery network or as part of a serverless application, but a low benefit Action could be ok if its also low burden); or
- any other activity unrelated to the production, testing, deployment, or publication of the software project associated with the repository where GitHub Actions are used. - any other activity unrelated to the production, testing, deployment, or publication of the software project associated with the repository where GitHub Actions are used.
@@ -60,16 +58,7 @@ Actions and any elements of the Action service may not be used in violation of t
In order to prevent violations of these limitations and abuse of GitHub Actions, GitHub may monitor your use of GitHub Actions. Misuse of GitHub Actions may result in termination of jobs, or restrictions in your ability to use GitHub Actions. In order to prevent violations of these limitations and abuse of GitHub Actions, GitHub may monitor your use of GitHub Actions. Misuse of GitHub Actions may result in termination of jobs, or restrictions in your ability to use GitHub Actions.
#### b. Packages Usage #### b. Packages Usage
GitHub Packages may be used to download, publish, and manage packages of Content. Each Account plan comes with included bandwidth and storage quantities for use with Packages, which can be found in the [Packages documentation](/github/managing-packages-with-github-package-registry/about-github-package-registry). Actions and Packages share storage between the two Service features. Storage and bandwidth usage are displayed within your [account settings](https://github.com/settings/billing), and you will be notified by email in advance of reaching the limit of your included quantities. If you want to use Packages beyond your included bandwidth and storage quantities, then you may [enable overages](/github/setting-up-and-managing-billing-and-payments-on-github/about-billing-for-github-packages). GitHub Packages is billed on a usage basis. The [Packages documentation](/packages) includes details, including bandwidth and storage quantities (depending on your Account plan), and how to monitor your Packages usage and set usage limits. Packages bandwidth usage is limited by the [GitHub Acceptable Use Polices](/github/site-policy/github-acceptable-use-policies).
Bandwidth usage is calculated based on the amount of data transferred out of your repositories through Packages, but package transfers through Actions will not count toward your included or paid quantities. Packages bandwidth usage is limited by the [Acceptable Use Policy](/github/site-policy/github-acceptable-use-policies#7-excessive-bandwidth-use), and included bandwidth quantities are determined by your [account plan](https://github.com/pricing).
Actions and Packages storage usage is shared between the two Service features. Storage usage is calculated as a weighted average over the course of the month and does not reset each month. Public and private repositories have different included storage quantities, and the included quantities for private repositories depend on your [account plan](https://github.com/pricing).
#### c. Payment and Billing for Actions and Packages
Billing for Actions and Packages is usage-based. Additional quantities of Actions or Packages cannot be purchased separately. For monthly-billed customers, you must have a payment method on file to choose to pay for additional quantities of these Service features. You will be charged on a monthly, recurring basis based on your usage in the preceding month, unless you are billed by invoice. For invoiced customers, you must pay the fees within thirty (30) days of the GitHub invoice date. For customers paying for Service feature overages in advance, unused prepaid minutes will not roll over to the next billing period and will not be refunded.
You can set a monthly spending limit in your [account settings](https://github.com/settings/billing/cost_management). You will be notified by email in advance of reaching the limit of your included quantities and the designated spending limit of your paid additional quantities.
### 6. Learning Lab ### 6. Learning Lab

View File

@@ -13418,6 +13418,11 @@ type MarketplaceListing implements Node {
""" """
hasTermsOfService: Boolean! hasTermsOfService: Boolean!
"""
Whether the creator of the app is a verified org
"""
hasVerifiedOwner: Boolean!
""" """
A technical description of how this app works with GitHub. A technical description of how this app works with GitHub.
""" """

View File

@@ -3,7 +3,7 @@
{% if breadcrumb[1].href == '' %} {% if breadcrumb[1].href == '' %}
<span>{{breadcrumb[1].title}}</span> <span>{{breadcrumb[1].title}}</span>
{% else %} {% else %}
<a title="{{ breadcrumb[0]}}: {{breadcrumb[1].title}}" href="/{{currentLanguage}}{{breadcrumb[1].href}}" class="d-inline-block {% if breadcrumb[1].href == currentPathWithoutLanguage %}text-gray-light{% endif %}"> <a title="{{ breadcrumb[0]}}: {{breadcrumb[1].title}}" href="{{{breadcrumb[1].href}}" class="d-inline-block {% if breadcrumb[1].href == currentPath %}text-gray-light{% endif %}">
{{breadcrumb[1].title}}</a> {{breadcrumb[1].title}}</a>
{% endif %} {% endif %}
{% endfor %} {% endfor %}

View File

@@ -1,17 +1,17 @@
<ul class="sidebar-categories"> <ul class="sidebar-categories">
{% for category in product[1].categories %} {% for category in product[1].categories %}
<li class="sidebar-category {% if breadcrumbs.category.href == category[1].href %}active{% endif %}"> <li class="sidebar-category {% if breadcrumbs.category.href == category[1].href %}active{% endif %}">
<a href="/{{currentLanguage}}{{category[1].href}}">{{ category[1].title }}</a> <a href="{{category[1].href}}">{{ category[1].title }}</a>
<!-- some categories have topics with child articles --> <!-- some categories have topics with child articles -->
{% if category[1].maptopics %} {% if category[1].maptopics %}
<ul class="sidebar-maptopics"> <ul class="sidebar-maptopics">
{% for maptopic in category[1].maptopics %} {% for maptopic in category[1].maptopics %}
<li class="sidebar-maptopic {% if breadcrumbs.maptopic.href == maptopic[1].href %}active{% endif %}"> <li class="sidebar-maptopic {% if breadcrumbs.maptopic.href == maptopic[1].href %}active{% endif %}">
<a href="/{{currentLanguage}}{{maptopic[1].href}}">{{ maptopic[1].title }}</a> <a href="{{maptopic[1].href}}">{{ maptopic[1].title }}</a>
<ul class="sidebar-articles"> <ul class="sidebar-articles">
{% for article in maptopic[1].articles %} {% for article in maptopic[1].articles %}
<li class="sidebar-article {% if currentPath == article[1].href %}active{% endif %}"> <li class="sidebar-article {% if currentPath == article[1].href %}active{% endif %}">
<a href="/{{currentLanguage}}{{article[1].href}}">{{ article[1].title }}</a> <a href="{{article[1].href}}">{{ article[1].title }}</a>
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>
@@ -23,7 +23,7 @@
<ul class="sidebar-articles"> <ul class="sidebar-articles">
{% for article in category[1].articles %} {% for article in category[1].articles %}
<li class="sidebar-article{% if currentPath == article[1].href %} active{% endif %}"> <li class="sidebar-article{% if currentPath == article[1].href %} active{% endif %}">
<a href="/{{currentLanguage}}{{article[1].href}}">{{ article[1].title }}</a> <a href="{{article[1].href}}">{{ article[1].title }}</a>
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>

View File

@@ -10,12 +10,12 @@
{% include all-products-link %} {% include all-products-link %}
<li title="{{product.title}}" class="sidebar-product mb-2"> <li title="{{product.title}}" class="sidebar-product mb-2">
{% unless page.hidden %} {% unless page.hidden %}
<a href="/{{currentLanguage}}{{product.href}}" class="pl-4 pr-5 pb-1 f4">{{ product.title }}</a> <a href="{{product.href}}" class="pl-4 pr-5 pb-1 f4">{{ product.title }}</a>
{% endunless %} {% endunless %}
</li> </li>
<ul class="sidebar-categories list-style-none"> <ul class="sidebar-categories list-style-none">
{% for category in product.categories %} {% for category in product.categories %}
{% capture fullPathToCategory %}/{{currentLanguage}}{{category[1].href}}{% endcapture %} {% capture fullPathToCategory %}{{category[1].href}}{% endcapture %}
<li class="sidebar-category py-1 {% if breadcrumbs.category.href == category[1].href %}active {% if currentPath == fullPathToCategory %}is-current-page {% endif %}{% endif %}{% if category[1].standalone %}standalone-category{% endif %}"> <li class="sidebar-category py-1 {% if breadcrumbs.category.href == category[1].href %}active {% if currentPath == fullPathToCategory %}is-current-page {% endif %}{% endif %}{% if category[1].standalone %}standalone-category{% endif %}">
{% if category[1].standalone %} {% if category[1].standalone %}
@@ -37,13 +37,13 @@
<ul class="sidebar-topics list-style-none position-relative"> <ul class="sidebar-topics list-style-none position-relative">
{% for maptopic in category[1].maptopics %} {% for maptopic in category[1].maptopics %}
{% unless maptopic[1].hidden %} {% unless maptopic[1].hidden %}
{% capture fullPathToMaptopic %}/{{currentLanguage}}{{maptopic[1].href}}{% endcapture %} {% capture fullPathToMaptopic %}{{maptopic[1].href}}{% endcapture %}
<li class="sidebar-maptopic {% if breadcrumbs.maptopic.href == maptopic[1].href %}active {% if currentPath == fullPathToMaptopic %}is-current-page{% endif %}{% endif %}"> <li class="sidebar-maptopic {% if breadcrumbs.maptopic.href == maptopic[1].href %}active {% if currentPath == fullPathToMaptopic %}is-current-page{% endif %}{% endif %}">
<a href="{{fullPathToMaptopic}}" class="pl-4 pr-5 py-2">{{ maptopic[1].title }}</a> <a href="{{fullPathToMaptopic}}" class="pl-4 pr-5 py-2">{{ maptopic[1].title }}</a>
<ul class="sidebar-articles my-2"> <ul class="sidebar-articles my-2">
{% for article in maptopic[1].articles %} {% for article in maptopic[1].articles %}
{% capture fullPathToArticle %}/{{currentLanguage}}{{article[1].href}}{% endcapture %} {% capture fullPathToArticle %}{{article[1].href}}{% endcapture %}
<li class="sidebar-article {% if breadcrumbs.article.href == article[1].href %}active {% if currentPath == fullPathToArticle %}is-current-page{% endif %}{% endif %}"> <li class="sidebar-article {% if breadcrumbs.article.href == article[1].href %}active {% if currentPath == fullPathToArticle %}is-current-page{% endif %}{% endif %}">
<a href="{{fullPathToArticle}}" class="pl-6 pr-5 py-1{% if forloop.last %} pb-2{% endif %}">{{ article[1].title }}</a> <a href="{{fullPathToArticle}}" class="pl-6 pr-5 py-1{% if forloop.last %} pb-2{% endif %}">{{ article[1].title }}</a>
@@ -58,7 +58,7 @@
{% else %} {% else %}
<ul class="sidebar-articles list-style-none"> <ul class="sidebar-articles list-style-none">
{% for article in category[1].articles %} {% for article in category[1].articles %}
{% capture fullPathToArticle %}/{{currentLanguage}}{{article[1].href}}{% endcapture %} {% capture fullPathToArticle %}{{article[1].href}}{% endcapture %}
<li class="sidebar-article {% if breadcrumbs.article.href == article[1].href %}active {% if currentPath == fullPathToArticle %}is-current-page{% endif %}{% endif %}"> <li class="sidebar-article {% if breadcrumbs.article.href == article[1].href %}active {% if currentPath == fullPathToArticle %}is-current-page{% endif %}{% endif %}">
<a href="{{fullPathToArticle}}" class="pl-4 pr-5 py-1{% if forloop.last %} pb-2{% endif %}">{{ article[1].title }}</a> <a href="{{fullPathToArticle}}" class="pl-4 pr-5 py-1{% if forloop.last %} pb-2{% endif %}">{{ article[1].title }}</a>
</li> </li>

View File

@@ -92,7 +92,7 @@
"deprecationDate": "2021-09-23" "deprecationDate": "2021-09-23"
}, },
"3.0": { "3.0": {
"releaseDate": "2020-12-08", "releaseDate": "2020-12-16",
"deprecationDate": "2021-12-08" "deprecationDate": "2021-12-08"
} }
} }

View File

@@ -8,6 +8,10 @@ module.exports = function findPage (href, pageMap, redirects = {}, languageCode
// remove trailing slash // remove trailing slash
href = slash(href).replace(patterns.trailingSlash, '$1') href = slash(href).replace(patterns.trailingSlash, '$1')
// do an initial lookup on the path as-is
let page = pageMap[removeFragment(href)]
if (page) return page
// check all potential versions // check all potential versions
const versionedPathsToCheck = [...new Set(allVersions.map(version => { const versionedPathsToCheck = [...new Set(allVersions.map(version => {
return getVersionedPathWithLanguage(href, version, languageCode) return getVersionedPathWithLanguage(href, version, languageCode)
@@ -22,8 +26,8 @@ module.exports = function findPage (href, pageMap, redirects = {}, languageCode
// need to account for redirects again // need to account for redirects again
pathToPage = redirects[pathToPage] || pathToPage pathToPage = redirects[pathToPage] || pathToPage
// find the page // try finding the page again
const page = pageMap[removeFragment(pathToPage)] page = pageMap[removeFragment(pathToPage)]
if (page) return page if (page) return page

View File

@@ -1,22 +1,12 @@
const findPage = require('./find-page') // get the childArticles set on map topics in lib/site-tree.js
module.exports = function getMapTopicContent (parentProductId, breadcrumbs, siteTree) {
const childArticles = siteTree.products[parentProductId].categories[breadcrumbs.category.href].maptopics[breadcrumbs.maptopic.href].childArticles
// get the page.childArticles set on english map topics in lib/site-tree.js if (!childArticles) {
module.exports = function getMapTopicContent (page, pageMap, redirects) { console.error(`can't find child articles from siteTree for map topic '${breadcrumbs.maptopic.href}'`)
const englishPage = page.languageCode !== 'en'
? findPage(`/${page.relativePath.replace(/.md$/, '')}`, pageMap, redirects, 'en')
: page
if (!englishPage) {
console.error(`cannot find english page: ${page.fullPath}`)
return
} }
if (!englishPage.childArticles) { return childArticles
console.error(`error getting child articles on map topic: ${page.fullPath}`)
return
}
return englishPage.childArticles
.map(article => `{% link_with_intro /${article.href} %}`) .map(article => `{% link_with_intro /${article.href} %}`)
.join('\n\n') .join('\n\n')
} }

View File

@@ -1,4 +1,17 @@
[ [
{
"schemaChanges": [
{
"title": "The GraphQL schema includes these changes:",
"changes": [
"Field `hasVerifiedOwner` was added to object type `MarketplaceListing`"
]
}
],
"previewChanges": [],
"upcomingChanges": [],
"date": "2020-12-09"
},
{ {
"date": "2020-11-13", "date": "2020-11-13",
"schemaChanges": [ "schemaChanges": [

File diff suppressed because one or more lines are too long

View File

@@ -21683,6 +21683,14 @@
"kind": "scalars", "kind": "scalars",
"href": "/graphql/reference/scalars#boolean" "href": "/graphql/reference/scalars#boolean"
}, },
{
"name": "hasVerifiedOwner",
"description": "<p>Whether the creator of the app is a verified org.</p>",
"type": "Boolean!",
"id": "boolean",
"kind": "scalars",
"href": "/graphql/reference/scalars#boolean"
},
{ {
"name": "howItWorks", "name": "howItWorks",
"description": "<p>A technical description of how this app works with GitHub.</p>", "description": "<p>A technical description of how this app works with GitHub.</p>",

View File

@@ -148,7 +148,8 @@ class Page {
this.shortTitle = await renderContent(this.shortTitle, context, { textOnly: true, encodeEntities: true }) this.shortTitle = await renderContent(this.shortTitle, context, { textOnly: true, encodeEntities: true })
let markdown = this.mapTopic let markdown = this.mapTopic
? getMapTopicContent(this, context.pages, context.redirects) // get the map topic child articles from the siteTree
? getMapTopicContent(this.parentProduct.id, context.breadcrumbs, context.siteTree[context.currentLanguage][context.currentVersion])
: this.markdown : this.markdown
// If the article is interactive parse the React! // If the article is interactive parse the React!

View File

@@ -56,7 +56,7 @@ module.exports = function getOldPathsFromPath (currentPath, languageCode, curren
// ------ BEGIN MODERN VERSION FORMAT REPLACEMENTS ------// // ------ BEGIN MODERN VERSION FORMAT REPLACEMENTS ------//
if (currentlySupportedVersions.includes(currentVersion) || versionSatisfiesRange(currentVersion, `>${lastReleaseWithLegacyFormat}`)) { if (currentlySupportedVersions.includes(currentVersion) || versionSatisfiesRange(currentVersion, `>${lastReleaseWithLegacyFormat}`)) {
oldPaths.forEach(oldPath => { (new Set(oldPaths)).forEach(oldPath => {
// create old path /github from new path /free-pro-team@latest/github // create old path /github from new path /free-pro-team@latest/github
oldPaths.add(oldPath oldPaths.add(oldPath
.replace(`/${nonEnterpriseDefaultVersion}`, '')) .replace(`/${nonEnterpriseDefaultVersion}`, ''))
@@ -107,19 +107,22 @@ module.exports = function getOldPathsFromPath (currentPath, languageCode, curren
// ------ END MODERN VERSION FORMAT REPLACEMENTS ------// // ------ END MODERN VERSION FORMAT REPLACEMENTS ------//
// For each old path added to the set above, do the following... // For each old path added to the set above, do the following...
(new Set(oldPaths)).forEach(oldPath => {
// for English only, remove language code
if (languageCode === 'en') {
oldPaths.add(getPathWithoutLanguage(oldPath))
}
// add language code // add language code
oldPaths.forEach(oldPath => oldPaths.add(getPathWithLanguage(oldPath, languageCode))) oldPaths.add(getPathWithLanguage(oldPath, languageCode))
// for English only, remove language code // create /enterprise from /enterprise/latest
if (languageCode === 'en') { oldPaths.add(oldPath.replace(`/enterprise/${latest}`, '/enterprise'))
oldPaths.forEach(oldPath => oldPaths.add(getPathWithoutLanguage(oldPath))) })
}
// create /enterprise from /enterprise/latest // exclude any empty old paths that may have been derived
oldPaths.forEach(oldPath => oldPaths.add(oldPath.replace(`/enterprise/${latest}`, '/enterprise'))) oldPaths.delete('')
oldPaths.delete('/')
return Array.from(oldPaths) return oldPaths
// exclude any empty old paths that may have been derived
.filter(oldPath => oldPath !== '' && oldPath !== '/')
} }

View File

@@ -1,7 +1,7 @@
const path = require('path') const path = require('path')
const findPageInVersion = require('./find-page-in-version') const findPageInVersion = require('./find-page-in-version')
const products = Object.values(require('../lib/all-products')) const products = Object.values(require('../lib/all-products'))
const { getVersionedPathWithoutLanguage } = require('./path-utils') const { getVersionedPathWithLanguage, getPathWithLanguage } = require('./path-utils')
const languageCodes = Object.keys(require('./languages')) const languageCodes = Object.keys(require('./languages'))
const addTitlesToTree = require('./site-tree-titles') const addTitlesToTree = require('./site-tree-titles')
const allVersions = Object.keys(require('./all-versions')) const allVersions = Object.keys(require('./all-versions'))
@@ -35,7 +35,8 @@ module.exports = async function buildSiteTree (pageMap, site, redirects) {
return return
} }
product.href = item.href // we don't want versioned product links because these links already have a default version in them
product.href = getPathWithLanguage(item.href, languageCode)
// find the product TOC page and get TOC items // find the product TOC page and get TOC items
const page = findPageInVersion(item.href, pageMap, redirects, languageCode, version) const page = findPageInVersion(item.href, pageMap, redirects, languageCode, version)
@@ -43,7 +44,7 @@ module.exports = async function buildSiteTree (pageMap, site, redirects) {
// skip if page can't be found in this version // skip if page can't be found in this version
if (!page) return if (!page) return
product.categories = buildCategoriesTree(page.tocItems, item.href, pageMap, redirects, version, languageCode) product.categories = buildCategoriesTree(page.tocItems, product.href, pageMap, redirects, version, languageCode)
productTree[item.id] = product productTree[item.id] = product
return null return null
@@ -67,11 +68,12 @@ function buildCategoriesTree (tocItems, productHref, pageMap, redirects, version
const categoryHref = path.join(productHref, item.href) const categoryHref = path.join(productHref, item.href)
const versionedCategoryHref = getVersionedPathWithoutLanguage(categoryHref, version) // we DO want versioned category links
const versionedCategoryHref = getVersionedPathWithLanguage(categoryHref, version, languageCode)
category.href = versionedCategoryHref category.href = versionedCategoryHref
// find the category TOC page and get its TOC items // find the category TOC page and get its TOC items
const page = findPageInVersion(categoryHref, pageMap, redirects, languageCode, version) const page = findPageInVersion(versionedCategoryHref, pageMap, redirects, languageCode, version)
// skip if page can't be found in this version // skip if page can't be found in this version
if (!page) return if (!page) return
@@ -90,9 +92,9 @@ function buildCategoriesTree (tocItems, productHref, pageMap, redirects, version
// if TOC contains maptopics, build a maptopics tree // if TOC contains maptopics, build a maptopics tree
// otherwise build an articles tree // otherwise build an articles tree
if (hasMaptopics) { if (hasMaptopics) {
category.maptopics = buildMaptopicsTree(page.tocItems, categoryHref, pageMap, redirects, version, languageCode) category.maptopics = buildMaptopicsTree(page.tocItems, versionedCategoryHref, pageMap, redirects, version, languageCode)
} else { } else {
category.articles = buildArticlesTree(page.tocItems, categoryHref, pageMap, redirects, version, languageCode) category.articles = buildArticlesTree(page.tocItems, versionedCategoryHref, pageMap, redirects, version, languageCode)
} }
} }
@@ -102,7 +104,7 @@ function buildCategoriesTree (tocItems, productHref, pageMap, redirects, version
return categoryTree return categoryTree
} }
function buildMaptopicsTree (tocItems, categoryHref, pageMap, redirects, version, languageCode) { function buildMaptopicsTree (tocItems, versionedCategoryHref, pageMap, redirects, version, languageCode) {
const maptopicTree = {} const maptopicTree = {}
// for every maptopic in a category TOC... // for every maptopic in a category TOC...
@@ -111,14 +113,11 @@ function buildMaptopicsTree (tocItems, categoryHref, pageMap, redirects, version
.forEach(item => { .forEach(item => {
const maptopic = {} const maptopic = {}
const maptopicHref = path.join(categoryHref, item.href) const versionedMaptopicHref = path.join(versionedCategoryHref, item.href)
const versionedMaptopicHref = getVersionedPathWithoutLanguage(maptopicHref, version)
maptopic.href = versionedMaptopicHref maptopic.href = versionedMaptopicHref
// we already have access to the child articles via the category TOC items // find the category TOC page and get its TOC items
// but we still need the page to get the available versions const page = findPageInVersion(versionedMaptopicHref, pageMap, redirects, languageCode, version)
const page = findPageInVersion(maptopicHref, pageMap, redirects, languageCode, version)
// skip if page can't be found in this version // skip if page can't be found in this version
if (!page) return if (!page) return
@@ -126,23 +125,20 @@ function buildMaptopicsTree (tocItems, categoryHref, pageMap, redirects, version
// if this is not a maptopic, return early // if this is not a maptopic, return early
if (!page.mapTopic) return if (!page.mapTopic) return
const childArticles = getChildArticles(tocItems, item.href)
maptopic.title = page.title maptopic.title = page.title
maptopic.shortTitle = page.shortTitle maptopic.shortTitle = page.shortTitle
maptopic.hidden = page.hidden maptopic.hidden = page.hidden
// make the child articles accessible to the page object for maptopic rendering // make the child articles accessible to the page object for maptopic rendering
if (!page.childArticles) page.childArticles = childArticles maptopic.childArticles = getChildArticles(tocItems, item.href)
maptopic.articles = buildArticlesTree(maptopic.childArticles, versionedCategoryHref, pageMap, redirects, version, languageCode)
maptopic.articles = buildArticlesTree(childArticles, categoryHref, pageMap, redirects, version, languageCode)
maptopicTree[versionedMaptopicHref] = maptopic maptopicTree[versionedMaptopicHref] = maptopic
}) })
return maptopicTree return maptopicTree
} }
function buildArticlesTree (tocItems, categoryHref, pageMap, redirects, version, languageCode) { function buildArticlesTree (tocItems, versionedCategoryHref, pageMap, redirects, version, languageCode) {
const articleTree = {} const articleTree = {}
// REST categories may not have TOC items // REST categories may not have TOC items
@@ -152,12 +148,11 @@ function buildArticlesTree (tocItems, categoryHref, pageMap, redirects, version,
tocItems.forEach(item => { tocItems.forEach(item => {
const article = {} const article = {}
const articleHref = path.join(categoryHref, item.href) const versionedArticleHref = path.join(versionedCategoryHref, item.href)
const versionedArticleHref = getVersionedPathWithoutLanguage(articleHref, version)
article.href = versionedArticleHref article.href = versionedArticleHref
const page = findPageInVersion(articleHref, pageMap, redirects, languageCode, version) // find the category TOC page and get its TOC items
const page = findPageInVersion(versionedArticleHref, pageMap, redirects, languageCode, version)
// skip if page can't be found in this version // skip if page can't be found in this version
if (!page) return if (!page) return

View File

@@ -23,7 +23,7 @@ module.exports = async (req, res, next) => {
const product = req.context.siteTree[req.language][req.context.currentVersion].products[req.context.currentProduct] const product = req.context.siteTree[req.language][req.context.currentVersion].products[req.context.currentProduct]
req.context.breadcrumbs.product = { req.context.breadcrumbs.product = {
href: path.posix.join('/', req.context.currentVersion, productPath), href: path.posix.join('/', req.context.currentLanguage, req.context.currentVersion, productPath),
title: product.title title: product.title
} }
@@ -32,7 +32,7 @@ module.exports = async (req, res, next) => {
// get category path // get category path
// e.g., `getting-started-with-github` in /free-pro-team@latest/github/getting-started-with-github // e.g., `getting-started-with-github` in /free-pro-team@latest/github/getting-started-with-github
// or /enterprise-server@2.21/github/getting-started-with-github // or /enterprise-server@2.21/github/getting-started-with-github
const categoryPath = path.posix.join('/', req.context.currentVersion, productPath, pathParts[1]) const categoryPath = path.posix.join('/', req.context.currentLanguage, req.context.currentVersion, productPath, pathParts[1])
const category = product.categories[categoryPath] const category = product.categories[categoryPath]
@@ -49,7 +49,7 @@ module.exports = async (req, res, next) => {
// e.g., /github/getting-started-with-github/learning-about-github // e.g., /github/getting-started-with-github/learning-about-github
let maptopic let maptopic
if (req.context.page.mapTopic) { if (req.context.page.mapTopic) {
const maptopicPath = path.posix.join(categoryPath, pathParts[2]) const maptopicPath = req.path
maptopic = category.maptopics[maptopicPath] maptopic = category.maptopics[maptopicPath]
@@ -60,9 +60,7 @@ module.exports = async (req, res, next) => {
title: maptopic.shortTitle || maptopic.title title: maptopic.shortTitle || maptopic.title
} }
} else { } else {
// get article path const articlePath = req.path
// e.g., /github/getting-started-with-github/githubs-products
const articlePath = path.posix.join(categoryPath, pathParts[2])
// find parent maptopic if one exists // find parent maptopic if one exists
// some categories don't have maptopics, e.g. site-policy // some categories don't have maptopics, e.g. site-policy
@@ -77,17 +75,7 @@ module.exports = async (req, res, next) => {
} }
} }
let articleKey = '/' + req.language + articlePath const articlePage = req.context.page
let articlePage = req.context.pages[articleKey]
// fall back to English if localized article does not exist
if (!articlePage && req.language !== 'en') {
articleKey = '/en' + articlePath
articlePage = req.context.pages[articleKey]
}
if (!articlePage) return next()
const articleTitle = await articlePage.renderProp('shortTitle', req.context, { textOnly: true, encodeEntities: true }) const articleTitle = await articlePage.renderProp('shortTitle', req.context, { textOnly: true, encodeEntities: true })
req.context.breadcrumbs.article = { req.context.breadcrumbs.article = {

View File

@@ -40,8 +40,8 @@ module.exports = async (req, res, next) => {
// get Early Access category path // get Early Access category path
// e.g., `enforcing-best-practices-with-github-policies` in /free-pro-team@latest/early-access/github/enforcing-best-practices-with-github-policies // e.g., `enforcing-best-practices-with-github-policies` in /free-pro-team@latest/early-access/github/enforcing-best-practices-with-github-policies
const categoryPath = path.posix.join('/', req.context.currentVersion, 'early-access', pathParts[0], pathParts[1]) const categoryPath = path.posix.join('/', 'en', req.context.currentVersion, 'early-access', pathParts[0], pathParts[1])
const category = req.context.pages[path.posix.join('/en', categoryPath)] const category = req.context.pages[categoryPath]
if (!category) return next() if (!category) return next()
@@ -54,7 +54,7 @@ module.exports = async (req, res, next) => {
// for Early Access purposes, we don't need to differentiate between map topics and articles breadcrumbs // for Early Access purposes, we don't need to differentiate between map topics and articles breadcrumbs
const mapTopicOrArticlePath = path.posix.join(categoryPath, pathParts[2]) const mapTopicOrArticlePath = path.posix.join(categoryPath, pathParts[2])
const mapTopicOrArticle = req.context.pages[path.posix.join('/en', mapTopicOrArticlePath)] const mapTopicOrArticle = req.context.pages[mapTopicOrArticlePath]
if (!mapTopicOrArticle) return next() if (!mapTopicOrArticle) return next()

View File

@@ -1,79 +1,44 @@
#!/usr/bin/env node #!/usr/bin/env node
const github = require('../lib/github') const { getContents } = require('../lib/git-utils')
const fs = require('fs') const fs = require('fs')
const path = require('path') const path = require('path')
const filename = path.join(__dirname, '../lib/enterprise-dates.json') const enterpriseDatesFile = path.join(__dirname, '../lib/enterprise-dates.json')
const jsonFile = require(filename) const enterpriseDatesString = fs.readFileSync(enterpriseDatesFile, 'utf8')
// [start-readme] // [start-readme]
// //
// Run this script during Enterprise releases and deprecations. // This script fetches data from https://github.com/github/enterprise-releases/blob/master/releases.json
// It uses the GitHub API to get dates from enterprise-releases and updates `lib/enterprise-dates.json`. // and updates `lib/enterprise-dates.json`, which the site uses for various functionality.
// The help site uses this JSON to display dates at the top of some Enterprise versions.
//
// This script requires that you have a GitHub Personal Access Token in a `.env` file.
// If you don't have a token, get one [here](https://github.com/settings/tokens/new?scopes=repo&description=docs-dev).
// If you don't have an `.env` file in your docs checkout, run this command in Terminal:
//
// `cp .env.example .env`
//
// Open the `.env` file in a text editor, and find the `GITHUB_TOKEN=` placeholder. Add your token after the equals sign.
//
// Do not commit the `.env` file; just leave it in your checkout.
// //
// [end-readme] // [end-readme]
// check for required PAT
if (!process.env.GITHUB_TOKEN) {
console.error('Error! You must have a GITHUB_TOKEN set in an .env file to run this script.')
process.exit(1)
}
main() main()
// GHE Release Lifecycle Dates
async function main () { async function main () {
let raw // send owner, repo, ref, path
try { const rawDates = JSON.parse(await getContents('github', 'enterprise-releases', 'master', 'releases.json'))
raw = await getDataFromReleasesRepo()
} catch (err) { const formattedDates = {}
console.log('error getting JSON from enterprise-releases repo') Object.entries(rawDates).forEach(([releaseNumber, releaseObject]) => {
throw (err) formattedDates[releaseNumber] = {
} releaseDate: releaseObject.release_candidate || releaseObject.start,
const json = prepareData(raw) deprecationDate: releaseObject.end
if (json === prettify(jsonFile)) { }
})
const formattedDatesString = JSON.stringify(formattedDates, null, 2)
if (formattedDatesString === enterpriseDatesString) {
console.log('This repo is already in sync with enterprise-releases!') console.log('This repo is already in sync with enterprise-releases!')
} else { } else {
fs.writeFileSync(filename, json, 'utf8') fs.writeFileSync(enterpriseDatesFile, formattedDatesString)
console.log(`${filename} has been updated!`) console.log(`${enterpriseDatesFile} has been updated!`)
} }
} }
// Uses https://octokit.github.io/rest.js/#api-Repos-getContents
async function getDataFromReleasesRepo () {
const octokit = github()
const { data } = await octokit.repos.getContents({
owner: 'github',
repo: 'enterprise-releases',
path: 'releases.json',
ref: 'master',
headers: { accept: 'application/vnd.github.v3.raw+json' }
})
return data
}
// We only need some of the values from the source JSON
// We use https://github.com/zeke/browser-date-formatter on the client side to reformat the dates
function prepareData (raw) {
const data = Object.entries(JSON.parse(raw))
const obj = {}
data.forEach(versions => {
const datesObj = {}
const version = versions[0]
const releaseDate = versions[1].start
const deprecationDate = versions[1].end
datesObj.releaseDate = releaseDate
datesObj.deprecationDate = deprecationDate
obj[version] = datesObj
})
return prettify(obj)
}
function prettify (json) {
return JSON.stringify(json, null, 2)
}

View File

@@ -24,23 +24,23 @@ describe('siteTree', () => {
test('object order', () => { test('object order', () => {
expect(Object.keys(siteTree)[0]).toBe('en') expect(Object.keys(siteTree)[0]).toBe('en')
expect(Object.keys(siteTree.en)[0]).toBe(nonEnterpriseDefaultVersion) expect(Object.keys(siteTree.en)[0]).toBe(nonEnterpriseDefaultVersion)
expect(Object.keys(siteTree.en[nonEnterpriseDefaultVersion].products.github.categories)[0]).toBe(`/${nonEnterpriseDefaultVersion}/github/getting-started-with-github`) expect(Object.keys(siteTree.en[nonEnterpriseDefaultVersion].products.github.categories)[0]).toBe(`/en/${nonEnterpriseDefaultVersion}/github/getting-started-with-github`)
}) })
test('object structure', () => { test('object structure', () => {
expect(nonEnterpriseDefaultVersion in siteTree.en).toBe(true) expect(nonEnterpriseDefaultVersion in siteTree.en).toBe(true)
expect(`enterprise-server@${latestEnterpriseRelease}` in siteTree.en).toBe(true) expect(`enterprise-server@${latestEnterpriseRelease}` in siteTree.en).toBe(true)
expect(flatTree[`en.${nonEnterpriseDefaultVersion}.products.github.href`]).toBe(`/${nonEnterpriseDefaultVersion}/github`) expect(flatTree[`en.${nonEnterpriseDefaultVersion}.products.github.href`]).toBe(`/en/${nonEnterpriseDefaultVersion}/github`)
expect(flatTree[`en.${nonEnterpriseDefaultVersion}.products.github.categories./${nonEnterpriseDefaultVersion}/github/getting-started-with-github.href`]).toBe(`/${nonEnterpriseDefaultVersion}/github/getting-started-with-github`) expect(flatTree[`en.${nonEnterpriseDefaultVersion}.products.github.categories./en/${nonEnterpriseDefaultVersion}/github/getting-started-with-github.href`]).toBe(`/en/${nonEnterpriseDefaultVersion}/github/getting-started-with-github`)
}) })
describe('localized titles', () => { describe('localized titles', () => {
test('titles for categories', () => { test('titles for categories', () => {
const japaneseTitle = flatTree[`ja.${nonEnterpriseDefaultVersion}.products.github.categories./${nonEnterpriseDefaultVersion}/github/getting-started-with-github.title`] const japaneseTitle = flatTree[`ja.${nonEnterpriseDefaultVersion}.products.github.categories./ja/${nonEnterpriseDefaultVersion}/github/getting-started-with-github.title`]
expect(typeof japaneseTitle).toBe('string') expect(typeof japaneseTitle).toBe('string')
expect(japaneseCharacters.presentIn(japaneseTitle)).toBe(true) expect(japaneseCharacters.presentIn(japaneseTitle)).toBe(true)
const englishTitle = flatTree[`en.${nonEnterpriseDefaultVersion}.products.github.categories./${nonEnterpriseDefaultVersion}/github/getting-started-with-github.title`] const englishTitle = flatTree[`en.${nonEnterpriseDefaultVersion}.products.github.categories./en/${nonEnterpriseDefaultVersion}/github/getting-started-with-github.title`]
expect(typeof englishTitle).toBe('string') expect(typeof englishTitle).toBe('string')
expect(japaneseCharacters.presentIn(englishTitle)).toBe(false) expect(japaneseCharacters.presentIn(englishTitle)).toBe(false)
}) })
@@ -52,7 +52,7 @@ describe('siteTree', () => {
test('articles that include site data in liquid templating', () => { test('articles that include site data in liquid templating', () => {
const pageWithDynamicTitle = siteTree.en[`enterprise-server@${latestEnterpriseRelease}`] const pageWithDynamicTitle = siteTree.en[`enterprise-server@${latestEnterpriseRelease}`]
.products.admin .products.admin
.categories[`/enterprise-server@${latestEnterpriseRelease}/admin/enterprise-support`] .categories[`/en/enterprise-server@${latestEnterpriseRelease}/admin/enterprise-support`]
// Source frontmatter from article: // Source frontmatter from article:
// title: 'Working with {{ site.data.variables.contact.github_support }}' // title: 'Working with {{ site.data.variables.contact.github_support }}'
expect(pageWithDynamicTitle.title).toEqual('Working with GitHub Support') expect(pageWithDynamicTitle.title).toEqual('Working with GitHub Support')

View File

@@ -15,6 +15,7 @@ const ALLOW_LIST = new Set([
'platform-samples', 'platform-samples',
'github-services', 'github-services',
'explore', 'explore',
'enterprise-releases',
'markup', 'markup',
'hubot', 'hubot',
'VisualStudio', 'VisualStudio',

View File

@@ -88,7 +88,7 @@ describe('breadcrumbs', () => {
const breadcrumbs = await getJSON('/en/github?json=breadcrumbs') const breadcrumbs = await getJSON('/en/github?json=breadcrumbs')
const expected = { const expected = {
product: { product: {
href: `/${nonEnterpriseDefaultVersion}/github`, href: `/en/${nonEnterpriseDefaultVersion}/github`,
title: 'GitHub.com' title: 'GitHub.com'
} }
} }
@@ -99,11 +99,11 @@ describe('breadcrumbs', () => {
const breadcrumbs = await getJSON('/en/github/authenticating-to-github?json=breadcrumbs') const breadcrumbs = await getJSON('/en/github/authenticating-to-github?json=breadcrumbs')
const expected = { const expected = {
product: { product: {
href: `/${nonEnterpriseDefaultVersion}/github`, href: `/en/${nonEnterpriseDefaultVersion}/github`,
title: 'GitHub.com' title: 'GitHub.com'
}, },
category: { category: {
href: `/${nonEnterpriseDefaultVersion}/github/authenticating-to-github`, href: `/en/${nonEnterpriseDefaultVersion}/github/authenticating-to-github`,
title: 'Authentication' title: 'Authentication'
} }
} }
@@ -114,15 +114,15 @@ describe('breadcrumbs', () => {
const breadcrumbs = await getJSON('/en/github/authenticating-to-github/keeping-your-account-and-data-secure?json=breadcrumbs') const breadcrumbs = await getJSON('/en/github/authenticating-to-github/keeping-your-account-and-data-secure?json=breadcrumbs')
const expected = { const expected = {
product: { product: {
href: `/${nonEnterpriseDefaultVersion}/github`, href: `/en/${nonEnterpriseDefaultVersion}/github`,
title: 'GitHub.com' title: 'GitHub.com'
}, },
category: { category: {
href: `/${nonEnterpriseDefaultVersion}/github/authenticating-to-github`, href: `/en/${nonEnterpriseDefaultVersion}/github/authenticating-to-github`,
title: 'Authentication' title: 'Authentication'
}, },
maptopic: { maptopic: {
href: `/${nonEnterpriseDefaultVersion}/github/authenticating-to-github/keeping-your-account-and-data-secure`, href: `/en/${nonEnterpriseDefaultVersion}/github/authenticating-to-github/keeping-your-account-and-data-secure`,
title: 'Keeping your account and data secure' title: 'Keeping your account and data secure'
} }
} }
@@ -133,19 +133,19 @@ describe('breadcrumbs', () => {
const breadcrumbs = await getJSON('/en/github/authenticating-to-github/creating-a-strong-password?json=breadcrumbs') const breadcrumbs = await getJSON('/en/github/authenticating-to-github/creating-a-strong-password?json=breadcrumbs')
const expected = { const expected = {
product: { product: {
href: `/${nonEnterpriseDefaultVersion}/github`, href: `/en/${nonEnterpriseDefaultVersion}/github`,
title: 'GitHub.com' title: 'GitHub.com'
}, },
category: { category: {
href: `/${nonEnterpriseDefaultVersion}/github/authenticating-to-github`, href: `/en/${nonEnterpriseDefaultVersion}/github/authenticating-to-github`,
title: 'Authentication' title: 'Authentication'
}, },
maptopic: { maptopic: {
href: `/${nonEnterpriseDefaultVersion}/github/authenticating-to-github/keeping-your-account-and-data-secure`, href: `/en/${nonEnterpriseDefaultVersion}/github/authenticating-to-github/keeping-your-account-and-data-secure`,
title: 'Keeping your account and data secure' title: 'Keeping your account and data secure'
}, },
article: { article: {
href: `/${nonEnterpriseDefaultVersion}/github/authenticating-to-github/creating-a-strong-password`, href: `/en/${nonEnterpriseDefaultVersion}/github/authenticating-to-github/creating-a-strong-password`,
title: 'Creating a strong password' title: 'Creating a strong password'
} }
} }
@@ -156,15 +156,15 @@ describe('breadcrumbs', () => {
const breadcrumbs = await getJSON('/github/site-policy/github-privacy-statement?json=breadcrumbs') const breadcrumbs = await getJSON('/github/site-policy/github-privacy-statement?json=breadcrumbs')
const expected = { const expected = {
product: { product: {
href: `/${nonEnterpriseDefaultVersion}/github`, href: `/en/${nonEnterpriseDefaultVersion}/github`,
title: 'GitHub.com' title: 'GitHub.com'
}, },
category: { category: {
href: `/${nonEnterpriseDefaultVersion}/github/site-policy`, href: `/en/${nonEnterpriseDefaultVersion}/github/site-policy`,
title: 'Site policy' title: 'Site policy'
}, },
article: { article: {
href: `/${nonEnterpriseDefaultVersion}/github/site-policy/github-privacy-statement`, href: `/en/${nonEnterpriseDefaultVersion}/github/site-policy/github-privacy-statement`,
title: 'GitHub Privacy Statement' title: 'GitHub Privacy Statement'
} }
} }