From d818c9ff4d4eaa93255a150a259129d72413aa58 Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Tue, 20 Oct 2020 17:19:14 -0400 Subject: [PATCH 001/105] restore old hidden docs implementation --- .gitignore | 4 +++- crowdin.yml | 5 +++-- data/ui.yml | 2 ++ includes/header.html | 4 ++++ lib/algolia/find-indexable-pages.js | 2 ++ lib/frontmatter.js | 6 ++---- 6 files changed, 16 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index e7bc933f8e..4c960428f8 100644 --- a/.gitignore +++ b/.gitignore @@ -4,8 +4,10 @@ node_modules npm-debug.log coverage +content/early-access +content/early-access-test # blc: broken link checker blc_output.log blc_output_internal.log -dist \ No newline at end of file +dist diff --git a/crowdin.yml b/crowdin.yml index ef80b4ec26..5f0cf63ee4 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -2,7 +2,8 @@ files: - source: /content/**/*.md translation: /translations/%locale%/%original_path%/%original_file_name% ignore: [ - "/content/README.md" + "/content/README.md", + "/content/early-access" ] - source: /data/**/*.yml translation: /translations/%locale%/%original_path%/%original_file_name% @@ -22,4 +23,4 @@ project_id_env: CROWDIN_PROJECT_ID api_token_env: CROWDIN_PERSONAL_TOKEN # https://support.crowdin.com/configuration-file-v3/#saving-directory-structure-on-server -preserve_hierarchy: true \ No newline at end of file +preserve_hierarchy: true diff --git a/data/ui.yml b/data/ui.yml index 137d1f392d..222f23fedb 100644 --- a/data/ui.yml +++ b/data/ui.yml @@ -18,6 +18,8 @@ header: product_in_progress: 👋 Hello, explorer! This page is under active development. For the most up-to-date and accurate information, please visit our developer documentation. + early_access: + 👋 This page contains content about an early access feature. Please do not share this URL publicly. search: need_help: Need help? placeholder: Search topics, products... diff --git a/includes/header.html b/includes/header.html index 6a70c04248..403797475a 100644 --- a/includes/header.html +++ b/includes/header.html @@ -15,6 +15,10 @@ {% elsif currentLanguage == 'en' and site.data.ui.header.notices.flags.product_in_progress == true %} {% assign header_notification_type = "product_in_progress" %} {% assign header_notification = site.data.ui.header.notices.product_in_progress %} + + {% elsif page.hidden %} + {% assign header_notification_type = "early_access" %} + {% assign header_notification = site.data.ui.header.notices.early_access %} {% endif %} {% if header_notification %} diff --git a/lib/algolia/find-indexable-pages.js b/lib/algolia/find-indexable-pages.js index d3fb24408f..19822c3d52 100644 --- a/lib/algolia/find-indexable-pages.js +++ b/lib/algolia/find-indexable-pages.js @@ -5,6 +5,8 @@ module.exports = async function findIndexablePages () { const indexablePages = allPages // exclude pages that are part of WIP products .filter(page => !page.parentProduct || !page.parentProduct.wip) + // exclude hidden pages + .filter(page => !page.hidden) // exclude index homepages .filter(page => !page.relativePath.endsWith('index.md')) diff --git a/lib/frontmatter.js b/lib/frontmatter.js index f3e79a5325..fbe98324c9 100644 --- a/lib/frontmatter.js +++ b/lib/frontmatter.js @@ -38,11 +38,9 @@ const schema = { mapTopic: { type: 'boolean' }, - // The `hidden` frontmatter property is no longer used, but leaving it here - // with an enum of `[false]` will help us catch any possible regressions. + // allow hidden articles under `early-access` hidden: { - type: 'boolean', - enum: [false] + type: 'boolean' }, layout: { type: ['string', 'boolean'], From 02308fd474e585d35eed83f3c577db432e420462 Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Tue, 20 Oct 2020 17:19:33 -0400 Subject: [PATCH 002/105] add early-access-test to products.yml --- data/products.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/data/products.yml b/data/products.yml index 96e85ce373..c9f60cd348 100644 --- a/data/products.yml +++ b/data/products.yml @@ -10,4 +10,5 @@ productsInOrder: - rest - graphql - insights - - desktop \ No newline at end of file + - desktop + - early-access-test From 247fa66c09f55874094b06d3945334fe9eab84dc Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Tue, 20 Oct 2020 17:20:44 -0400 Subject: [PATCH 003/105] hide contribution button on hidden docs --- includes/article.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/article.html b/includes/article.html index 8719e347a9..5badb77056 100644 --- a/includes/article.html +++ b/includes/article.html @@ -58,7 +58,7 @@
{% assign helpId = 'xl' %} {% include helpfulness %} - {% include contribution %} + {% unless page.hidden %}{% include contribution %}{% endunless %}
@@ -73,6 +73,6 @@
{% assign helpId = 'sm' %} {% include helpfulness %} - {% include contribution %} + {% unless page.hidden %}{% include contribution %}{% endunless %}
From a45c8549d617625ebad083781f0f97f876aa25e3 Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Tue, 20 Oct 2020 17:21:29 -0400 Subject: [PATCH 004/105] activeProducts are neither wip nor hidden --- middleware/context.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/middleware/context.js b/middleware/context.js index ec9915f421..55f0f055e5 100644 --- a/middleware/context.js +++ b/middleware/context.js @@ -2,7 +2,7 @@ const languages = require('../lib/languages') const enterpriseServerReleases = require('../lib/enterprise-server-releases') const allVersions = require('../lib/all-versions') const allProducts = require('../lib/all-products') -const activeProducts = Object.values(allProducts).filter(product => !product.wip) +const activeProducts = Object.values(allProducts).filter(product => !product.wip && !product.hidden) const { getVersionStringFromPath, getProductStringFromPath, getPathWithoutLanguage } = require('../lib/path-utils') const productNames = require('../lib/product-names') const warmServer = require('../lib/warm-server') From f40f2419be88b316693925ffa8ac0d787de881a7 Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Tue, 20 Oct 2020 17:22:08 -0400 Subject: [PATCH 005/105] support hidden products, which will just be early-access in practice --- lib/all-products.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/all-products.js b/lib/all-products.js index 09e9d2070a..d8d0f87cf0 100644 --- a/lib/all-products.js +++ b/lib/all-products.js @@ -36,7 +36,8 @@ sortedProductIds.forEach(productId => { href, dir, toc, - wip: data.wip || false + wip: data.wip || false, + hidden: data.hidden || false } internalProducts[productId].versions = applicableVersions From beaf73378d56032e5b1b77f4c25aae9cb402fcd5 Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Wed, 21 Oct 2020 09:43:23 -0400 Subject: [PATCH 006/105] hide early-access from public list of products --- data/products.yml | 1 - lib/all-products.js | 11 +++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/data/products.yml b/data/products.yml index c9f60cd348..1ec0286139 100644 --- a/data/products.yml +++ b/data/products.yml @@ -11,4 +11,3 @@ productsInOrder: - graphql - insights - desktop - - early-access-test diff --git a/lib/all-products.js b/lib/all-products.js index d8d0f87cf0..c56b85e3a1 100644 --- a/lib/all-products.js +++ b/lib/all-products.js @@ -17,11 +17,18 @@ const contentProductIds = fs.readdirSync(contentDir, { withFileTypes: true }) .filter(entry => entry.isDirectory()) .map(entry => entry.name) -assert(difference(sortedProductIds, contentProductIds).length === 0) -assert(difference(contentProductIds, sortedProductIds).length === 0) +// require the content/ list to match the list in data/products.yml, +// with the exception of content/early-access, which lives in a separate private repo +const publicContentProductIds = contentProductIds.filter(id => !id.startsWith('early-access')) +assert(difference(sortedProductIds, publicContentProductIds).length === 0) +assert(difference(publicContentProductIds, sortedProductIds).length === 0) const internalProducts = {} +// add optional early access content dir to sorted products list if present +const earlyAccessId = contentProductIds.find(id => id.startsWith('early-access')) +if (earlyAccessId) sortedProductIds.push(earlyAccessId) + sortedProductIds.forEach(productId => { const relPath = productId const dir = slash(path.join('content', relPath)) From 3170d8c98cc5dbd337fbf1ad45ba5bc9449ae7d0 Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Wed, 21 Oct 2020 15:41:02 -0400 Subject: [PATCH 007/105] try heroku prebuild script --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 4e48c71c6c..280c3f5308 100644 --- a/package.json +++ b/package.json @@ -139,7 +139,8 @@ "check-deps": "node script/check-deps.js", "prevent-pushes-to-main": "node script/prevent-pushes-to-main.js", "pa11y-ci": "pa11y-ci", - "pa11y-test": "start-server-and-test browser-test-server 4001 pa11y-ci" + "pa11y-test": "start-server-and-test browser-test-server 4001 pa11y-ci", + "heroku-prebuild": "rm -rf content/early-access && git clone https://${TEMP_KEY_ACCESS_KEY}@github.com/docs/early-access-test content/early-access && rm -rf content/early-access/.git" }, "engines": { "node": "12 - 14" From f355a2ee961dc9f8431ebf8ac18f0ce98c41066d Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Wed, 21 Oct 2020 15:47:20 -0400 Subject: [PATCH 008/105] use GITHUB_TOKEN for clone --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 280c3f5308..879a40ce84 100644 --- a/package.json +++ b/package.json @@ -140,7 +140,7 @@ "prevent-pushes-to-main": "node script/prevent-pushes-to-main.js", "pa11y-ci": "pa11y-ci", "pa11y-test": "start-server-and-test browser-test-server 4001 pa11y-ci", - "heroku-prebuild": "rm -rf content/early-access && git clone https://${TEMP_KEY_ACCESS_KEY}@github.com/docs/early-access-test content/early-access && rm -rf content/early-access/.git" + "heroku-prebuild": "rm -rf content/early-access && git clone https://${GITHUB_TOKEN}@github.com/docs/early-access-test content/early-access && rm -rf content/early-access/.git" }, "engines": { "node": "12 - 14" From 71bfc0d45ea47c66b2246680cbdcc0d13974c056 Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Wed, 21 Oct 2020 16:24:15 -0400 Subject: [PATCH 009/105] go back to old access key for testing --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 879a40ce84..280c3f5308 100644 --- a/package.json +++ b/package.json @@ -140,7 +140,7 @@ "prevent-pushes-to-main": "node script/prevent-pushes-to-main.js", "pa11y-ci": "pa11y-ci", "pa11y-test": "start-server-and-test browser-test-server 4001 pa11y-ci", - "heroku-prebuild": "rm -rf content/early-access && git clone https://${GITHUB_TOKEN}@github.com/docs/early-access-test content/early-access && rm -rf content/early-access/.git" + "heroku-prebuild": "rm -rf content/early-access && git clone https://${TEMP_KEY_ACCESS_KEY}@github.com/docs/early-access-test content/early-access && rm -rf content/early-access/.git" }, "engines": { "node": "12 - 14" From 1a0c41bfca2d678e5915eb239e890815f388f3b8 Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Wed, 21 Oct 2020 16:29:04 -0400 Subject: [PATCH 010/105] testing testing --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 280c3f5308..4e48c71c6c 100644 --- a/package.json +++ b/package.json @@ -139,8 +139,7 @@ "check-deps": "node script/check-deps.js", "prevent-pushes-to-main": "node script/prevent-pushes-to-main.js", "pa11y-ci": "pa11y-ci", - "pa11y-test": "start-server-and-test browser-test-server 4001 pa11y-ci", - "heroku-prebuild": "rm -rf content/early-access && git clone https://${TEMP_KEY_ACCESS_KEY}@github.com/docs/early-access-test content/early-access && rm -rf content/early-access/.git" + "pa11y-test": "start-server-and-test browser-test-server 4001 pa11y-ci" }, "engines": { "node": "12 - 14" From 0304ff3846db0d4792fb24fc9622c0f814266f70 Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Wed, 21 Oct 2020 16:32:06 -0400 Subject: [PATCH 011/105] testing testing --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 4e48c71c6c..280c3f5308 100644 --- a/package.json +++ b/package.json @@ -139,7 +139,8 @@ "check-deps": "node script/check-deps.js", "prevent-pushes-to-main": "node script/prevent-pushes-to-main.js", "pa11y-ci": "pa11y-ci", - "pa11y-test": "start-server-and-test browser-test-server 4001 pa11y-ci" + "pa11y-test": "start-server-and-test browser-test-server 4001 pa11y-ci", + "heroku-prebuild": "rm -rf content/early-access && git clone https://${TEMP_KEY_ACCESS_KEY}@github.com/docs/early-access-test content/early-access && rm -rf content/early-access/.git" }, "engines": { "node": "12 - 14" From 07d090ec19759ac6e1b43b8a1015bb508086525c Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Wed, 21 Oct 2020 16:41:30 -0400 Subject: [PATCH 012/105] use early-access-test --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 280c3f5308..574c8cd7f0 100644 --- a/package.json +++ b/package.json @@ -140,7 +140,7 @@ "prevent-pushes-to-main": "node script/prevent-pushes-to-main.js", "pa11y-ci": "pa11y-ci", "pa11y-test": "start-server-and-test browser-test-server 4001 pa11y-ci", - "heroku-prebuild": "rm -rf content/early-access && git clone https://${TEMP_KEY_ACCESS_KEY}@github.com/docs/early-access-test content/early-access && rm -rf content/early-access/.git" + "heroku-prebuild": "rm -rf content/early-access-test && git clone https://${TEMP_KEY_ACCESS_KEY}@github.com/docs/early-access-test content/early-access-test && rm -rf content/early-access-test/.git" }, "engines": { "node": "12 - 14" From d290d40ffcee5963bccd4104c91caf966670e5c2 Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Thu, 22 Oct 2020 10:07:20 -0400 Subject: [PATCH 013/105] update middleware and tests around robots.txt exclusions of hidden/EA content --- middleware/robots.js | 8 ++-- tests/rendering/robots-txt.js | 70 +++++++++++++++++++++++------------ 2 files changed, 52 insertions(+), 26 deletions(-) diff --git a/middleware/robots.js b/middleware/robots.js index b50d8afe8e..f57d6d3739 100644 --- a/middleware/robots.js +++ b/middleware/robots.js @@ -27,11 +27,13 @@ module.exports = function (req, res, next) { defaultResponse = defaultResponse.concat(`\nDisallow: /${language.code}\nDisallow: /${language.code}/*\n`) }) - // Disallow crawling of WIP products + // Disallow crawling of WIP or early access products Object.values(products) - .filter(product => product.wip) + .filter(product => product.wip || product.hidden) .forEach(product => { - defaultResponse = defaultResponse.concat(`\nDisallow: /*${product.href}\nDisallow: /*/enterprise/*/user${product.href}`) + product.versions.forEach(version => { + defaultResponse = defaultResponse.concat(`\nDisallow: /*/${version}/${product.id}\nDisallow: /*/${version}/${product.id}/*\n`) + }) }) return res.send(defaultResponse) diff --git a/tests/rendering/robots-txt.js b/tests/rendering/robots-txt.js index f070f0e0a8..5514dd983f 100644 --- a/tests/rendering/robots-txt.js +++ b/tests/rendering/robots-txt.js @@ -12,22 +12,22 @@ describe('robots.txt', () => { let res, robots beforeAll(async (done) => { res = await get('/robots.txt') - robots = robotsParser('https://help.github.com/robots.txt', res.text) + robots = robotsParser('https://docs.github.com/robots.txt', res.text) done() }) it('allows indexing of the homepage and English content', async () => { - expect(robots.isAllowed('https://help.github.com/')).toBe(true) - expect(robots.isAllowed('https://help.github.com/en')).toBe(true) - expect(robots.isAllowed('https://help.github.com/en/articles/verifying-your-email-address')).toBe(true) + expect(robots.isAllowed('https://docs.github.com/')).toBe(true) + expect(robots.isAllowed('https://docs.github.com/en')).toBe(true) + expect(robots.isAllowed('https://docs.github.com/en/articles/verifying-your-email-address')).toBe(true) }) it('allows indexing of generally available localized content', async () => { Object.values(languages) .filter(language => !language.wip) .forEach(language => { - expect(robots.isAllowed(`https://help.github.com/${language.code}`)).toBe(true) - expect(robots.isAllowed(`https://help.github.com/${language.code}/articles/verifying-your-email-address`)).toBe(true) + expect(robots.isAllowed(`https://docs.github.com/${language.code}`)).toBe(true) + expect(robots.isAllowed(`https://docs.github.com/${language.code}/articles/verifying-your-email-address`)).toBe(true) }) }) @@ -35,8 +35,8 @@ describe('robots.txt', () => { Object.values(languages) .filter(language => language.wip) .forEach(language => { - expect(robots.isAllowed(`https://help.github.com/${language.code}`)).toBe(false) - expect(robots.isAllowed(`https://help.github.com/${language.code}/articles/verifying-your-email-address`)).toBe(false) + expect(robots.isAllowed(`https://docs.github.com/${language.code}`)).toBe(false) + expect(robots.isAllowed(`https://docs.github.com/${language.code}/articles/verifying-your-email-address`)).toBe(false) }) }) @@ -61,18 +61,18 @@ describe('robots.txt', () => { const { href } = products[id] const blockedPaths = [ // English - `https://help.github.com/en${href}`, - `https://help.github.com/en${href}/overview`, - `https://help.github.com/en${href}/overview/intro`, - `https://help.github.com/en/enterprise/${enterpriseServerReleases.latest}/user${href}`, - `https://help.github.com/en/enterprise/${enterpriseServerReleases.oldestSupported}/user${href}`, + `https://docs.github.com/en${href}`, + `https://docs.github.com/en${href}/overview`, + `https://docs.github.com/en${href}/overview/intro`, + `https://docs.github.com/en/enterprise/${enterpriseServerReleases.latest}/user${href}`, + `https://docs.github.com/en/enterprise/${enterpriseServerReleases.oldestSupported}/user${href}`, // Japanese - `https://help.github.com/ja${href}`, - `https://help.github.com/ja${href}/overview`, - `https://help.github.com/ja${href}/overview/intro`, - `https://help.github.com/ja/enterprise/${enterpriseServerReleases.latest}/user${href}`, - `https://help.github.com/ja/enterprise/${enterpriseServerReleases.oldestSupported}/user${href}` + `https://docs.github.com/ja${href}`, + `https://docs.github.com/ja${href}/overview`, + `https://docs.github.com/ja${href}/overview/intro`, + `https://docs.github.com/ja/enterprise/${enterpriseServerReleases.latest}/user${href}`, + `https://docs.github.com/ja/enterprise/${enterpriseServerReleases.oldestSupported}/user${href}` ] blockedPaths.forEach(path => { @@ -81,12 +81,36 @@ describe('robots.txt', () => { }) }) + it('disallows indexing of early access "hidden" products', async () => { + const hiddenProductIds = Object.values(products) + .filter(product => product.hidden) + .map(product => product.id) + + hiddenProductIds.forEach(id => { + const { versions } = products[id] + const blockedPaths = versions.map(version => { + return [ + // English + `https://docs.github.com/en/${version}/${id}`, + `https://docs.github.com/en/${version}/${id}/some-early-access-article`, + // Japanese + `https://docs.github.com/ja/${version}/${id}`, + `https://docs.github.com/ja/${version}/${id}/some-early-access-article` + ] + }).flat() + + blockedPaths.forEach(path => { + expect(robots.isAllowed(path)).toBe(false) + }) + }) + }) + it('allows indexing of non-WIP products', async () => { expect('actions' in products).toBe(true) - expect(robots.isAllowed('https://help.github.com/en/actions')).toBe(true) - expect(robots.isAllowed('https://help.github.com/en/actions/overview')).toBe(true) - expect(robots.isAllowed('https://help.github.com/en/actions/overview/intro')).toBe(true) - expect(robots.isAllowed(`https://help.github.com/en/enterprise/${enterpriseServerReleases.latest}/user/actions`)).toBe(true) - expect(robots.isAllowed(`https://help.github.com/en/enterprise/${enterpriseServerReleases.oldestSupported}/user/actions`)).toBe(true) + expect(robots.isAllowed('https://docs.github.com/en/actions')).toBe(true) + expect(robots.isAllowed('https://docs.github.com/en/actions/overview')).toBe(true) + expect(robots.isAllowed('https://docs.github.com/en/actions/overview/intro')).toBe(true) + expect(robots.isAllowed(`https://docs.github.com/en/enterprise/${enterpriseServerReleases.latest}/user/actions`)).toBe(true) + expect(robots.isAllowed(`https://docs.github.com/en/enterprise/${enterpriseServerReleases.oldestSupported}/user/actions`)).toBe(true) }) }) From f4e05b189c88a0416ec9f559ead6983a99ce907e Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Thu, 22 Oct 2020 10:07:41 -0400 Subject: [PATCH 014/105] exclude hidden products in addition to hidden pages from Algolia --- lib/algolia/find-indexable-pages.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/algolia/find-indexable-pages.js b/lib/algolia/find-indexable-pages.js index 19822c3d52..d3d6be80dd 100644 --- a/lib/algolia/find-indexable-pages.js +++ b/lib/algolia/find-indexable-pages.js @@ -3,8 +3,8 @@ const loadPages = require('../pages') module.exports = async function findIndexablePages () { const allPages = await loadPages() const indexablePages = allPages - // exclude pages that are part of WIP products - .filter(page => !page.parentProduct || !page.parentProduct.wip) + // exclude pages that are part of WIP or hidden products + .filter(page => !page.parentProduct || !page.parentProduct.wip || page.parentProduct.hidden) // exclude hidden pages .filter(page => !page.hidden) // exclude index homepages From 129641c359254c4cbaafbab6db1e235b66d619a3 Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Thu, 22 Oct 2020 11:20:09 -0400 Subject: [PATCH 015/105] add separate clone script to be run as heroku-prebuild script or locally by docs writers --- package.json | 2 +- script/clone-early-access.js | 57 ++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100755 script/clone-early-access.js diff --git a/package.json b/package.json index 4c5fa461ad..1655b1c681 100644 --- a/package.json +++ b/package.json @@ -140,7 +140,7 @@ "prevent-pushes-to-main": "node script/prevent-pushes-to-main.js", "pa11y-ci": "pa11y-ci", "pa11y-test": "start-server-and-test browser-test-server 4001 pa11y-ci", - "heroku-prebuild": "rm -rf content/early-access-test && git clone https://${TEMP_KEY_ACCESS_KEY}@github.com/docs/early-access-test content/early-access-test && rm -rf content/early-access-test/.git" + "heroku-prebuild": "node script/clone-early-access.js" }, "engines": { "node": "12 - 14" diff --git a/script/clone-early-access.js b/script/clone-early-access.js new file mode 100755 index 0000000000..75378606fd --- /dev/null +++ b/script/clone-early-access.js @@ -0,0 +1,57 @@ +#!/usr/bin/env node + +// [start-readme] +// +// This script is run as a prebuild script during staging and deployments on Heroku. +// If you have access to the PAT, it can also be run locally to clone https://github.com/docs/early-access. +// The script clones a branch in the early-access repo that matches the current branch in the docs repo; +// if one can't be found, it clones the `main` branch. +// +// [end-readme] + +require('dotenv').config() +const { GITHUB_DOCUBOT_REPO_PAT } = process.env + +// TODO... +// // Exit if early access is not enabled +// if (!process.env.EARLY_ACCESS_ENABLED) { +// console.log('Skipping early access, not enable') +// process.exit(0) +// } + +// Exit if PAT is not found +if (!process.env.GITHUB_DOCUBOT_REPO_PAT) { + console.log('Skipping early access, not authorized') + process.exit(0) +} + +const { execSync } = require('child_process') +const rimraf = require('rimraf').sync +const fs = require('fs') +const path = require('path') + +// Early Access details +const earlyAccessDir = 'early-access-test' +const earlyAccessRepo = `https://${GITHUB_DOCUBOT_REPO_PAT}@github.com/docs/${earlyAccessDir}` +const earlyAccessContentDir = path.join(process.cwd(), 'content', earlyAccessDir) + +// Look for a branch in early-access that matches the current docs branch; +// otherwise fall back to `main` +const docsBranch = execSync('git branch --show-current').toString().trim() + +const earlyAccessBranch = execSync(`git ls-remote --heads ${earlyAccessRepo} ${docsBranch}`).toString() + ? docsBranch + : 'main' + +// Remove any dir that may pre-exist +rimraf(earlyAccessContentDir) + +// Clone the repo +execSync(`git clone --single-branch --branch ${earlyAccessBranch} ${earlyAccessRepo} ${earlyAccessContentDir}`) +console.log(`Branch: ${earlyAccessBranch}`) + +// Remove the .git dir +rimraf(`${earlyAccessContentDir}/.git`) + +// Confirm the directory exists +fs.existsSync(earlyAccessContentDir) From 896db3d78852c4e31a13ed871ca2c4e7fecd048a Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Fri, 23 Oct 2020 10:29:20 -0400 Subject: [PATCH 016/105] comment out branch check for now --- script/clone-early-access.js | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/script/clone-early-access.js b/script/clone-early-access.js index 75378606fd..ace465bae5 100755 --- a/script/clone-early-access.js +++ b/script/clone-early-access.js @@ -10,12 +10,12 @@ // [end-readme] require('dotenv').config() -const { GITHUB_DOCUBOT_REPO_PAT } = process.env +const { GITHUB_DOCUBOT_REPO_PAT, EARLY_ACCESS_ENABLED } = process.env // TODO... // // Exit if early access is not enabled -// if (!process.env.EARLY_ACCESS_ENABLED) { -// console.log('Skipping early access, not enable') +// if (!EARLY_ACCESS_ENABLED) { +// console.log('Skipping early access, not enabled') // process.exit(0) // } @@ -35,13 +35,15 @@ const earlyAccessDir = 'early-access-test' const earlyAccessRepo = `https://${GITHUB_DOCUBOT_REPO_PAT}@github.com/docs/${earlyAccessDir}` const earlyAccessContentDir = path.join(process.cwd(), 'content', earlyAccessDir) -// Look for a branch in early-access that matches the current docs branch; -// otherwise fall back to `main` -const docsBranch = execSync('git branch --show-current').toString().trim() - -const earlyAccessBranch = execSync(`git ls-remote --heads ${earlyAccessRepo} ${docsBranch}`).toString() - ? docsBranch - : 'main' +// TODO... +// // Look for a branch in early-access that matches the current docs branch; +// // otherwise fall back to `main` +// const docsBranch = execSync('git branch --show-current').toString().trim() +// +// const earlyAccessBranch = execSync(`git ls-remote --heads ${earlyAccessRepo} ${docsBranch}`).toString() +// ? docsBranch +// : 'main' +const earlyAccessBranch = 'main' // Remove any dir that may pre-exist rimraf(earlyAccessContentDir) From 822649f72aabe4c6d87fc109fb4b323eca3f317d Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Fri, 23 Oct 2020 12:23:32 -0400 Subject: [PATCH 017/105] add yml config file for setting the EA branch --- ea-config.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ea-config.yml diff --git a/ea-config.yml b/ea-config.yml new file mode 100644 index 0000000000..a57f0096f1 --- /dev/null +++ b/ea-config.yml @@ -0,0 +1,6 @@ +# DO NOT CHANGE +EA_PRODUCTION_BRANCH: main + +# Set to the early-access branch you want to stage +# This MUST match your docs branch name +EA_STAGING_BRANCH: main From 16f9fc7b73d30767751f699329e9246d39156f5f Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Fri, 23 Oct 2020 12:23:47 -0400 Subject: [PATCH 018/105] use the new yml config file for setting the EA branch --- script/clone-early-access.js | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/script/clone-early-access.js b/script/clone-early-access.js index ace465bae5..eb96be484f 100755 --- a/script/clone-early-access.js +++ b/script/clone-early-access.js @@ -10,7 +10,8 @@ // [end-readme] require('dotenv').config() -const { GITHUB_DOCUBOT_REPO_PAT, EARLY_ACCESS_ENABLED } = process.env +// const { GITHUB_DOCUBOT_REPO_PAT, EARLY_ACCESS_ENABLED, HEROKU_PRODUCTION_APP } = process.env +const { GITHUB_DOCUBOT_REPO_PAT, HEROKU_PRODUCTION_APP } = process.env // TODO... // // Exit if early access is not enabled @@ -29,28 +30,32 @@ const { execSync } = require('child_process') const rimraf = require('rimraf').sync const fs = require('fs') const path = require('path') +const yaml = require('js-yaml') +const eaConfig = yaml.load(fs.readFileSync(path.join(process.cwd(), 'ea-config.yml'), 'utf8')) // Early Access details +const earlyAccessOwner = 'docs' const earlyAccessDir = 'early-access-test' -const earlyAccessRepo = `https://${GITHUB_DOCUBOT_REPO_PAT}@github.com/docs/${earlyAccessDir}` +const earlyAccessRepo = `https://${GITHUB_DOCUBOT_REPO_PAT}@github.com/${earlyAccessOwner}/${earlyAccessDir}` const earlyAccessContentDir = path.join(process.cwd(), 'content', earlyAccessDir) -// TODO... -// // Look for a branch in early-access that matches the current docs branch; -// // otherwise fall back to `main` -// const docsBranch = execSync('git branch --show-current').toString().trim() -// -// const earlyAccessBranch = execSync(`git ls-remote --heads ${earlyAccessRepo} ${docsBranch}`).toString() -// ? docsBranch -// : 'main' -const earlyAccessBranch = 'main' +// production vs. staging environment +const environment = HEROKU_PRODUCTION_APP ? 'production' : 'staging' +const earlyAccessBranch = HEROKU_PRODUCTION_APP ? eaConfig.EA_PRODUCTION_BRANCH : eaConfig.EA_STAGING_BRANCH + +// confirm that the branch exists in the remote +const doesBranchExist = execSync(`git ls-remote --heads ${earlyAccessRepo} ${earlyAccessBranch}`).toString() +if (!doesBranchExist) { + console.log(`The branch '${earlyAccessBranch}' was not found in ${earlyAccessOwner}/${earlyAccessDir}. Exiting!`) + process.exit(0) +} // Remove any dir that may pre-exist rimraf(earlyAccessContentDir) // Clone the repo execSync(`git clone --single-branch --branch ${earlyAccessBranch} ${earlyAccessRepo} ${earlyAccessContentDir}`) -console.log(`Branch: ${earlyAccessBranch}`) +console.log(`Using early-access ${environment} branch: '${earlyAccessBranch}'`) // Remove the .git dir rimraf(`${earlyAccessContentDir}/.git`) From ea9b161d3f7af2a4a173d7ac705fdfcec5e28a03 Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Fri, 23 Oct 2020 14:00:02 -0400 Subject: [PATCH 019/105] change GITHUB_DOCUBOT_REPO_PAT to DOCUBOT_REPO_PAT --- script/clone-early-access.js | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/script/clone-early-access.js b/script/clone-early-access.js index eb96be484f..99f795439e 100755 --- a/script/clone-early-access.js +++ b/script/clone-early-access.js @@ -10,18 +10,10 @@ // [end-readme] require('dotenv').config() -// const { GITHUB_DOCUBOT_REPO_PAT, EARLY_ACCESS_ENABLED, HEROKU_PRODUCTION_APP } = process.env -const { GITHUB_DOCUBOT_REPO_PAT, HEROKU_PRODUCTION_APP } = process.env - -// TODO... -// // Exit if early access is not enabled -// if (!EARLY_ACCESS_ENABLED) { -// console.log('Skipping early access, not enabled') -// process.exit(0) -// } +const { DOCUBOT_REPO_PAT, HEROKU_PRODUCTION_APP } = process.env // Exit if PAT is not found -if (!process.env.GITHUB_DOCUBOT_REPO_PAT) { +if (!DOCUBOT_REPO_PAT) { console.log('Skipping early access, not authorized') process.exit(0) } @@ -36,7 +28,7 @@ const eaConfig = yaml.load(fs.readFileSync(path.join(process.cwd(), 'ea-config.y // Early Access details const earlyAccessOwner = 'docs' const earlyAccessDir = 'early-access-test' -const earlyAccessRepo = `https://${GITHUB_DOCUBOT_REPO_PAT}@github.com/${earlyAccessOwner}/${earlyAccessDir}` +const earlyAccessRepo = `https://${DOCUBOT_REPO_PAT}@github.com/${earlyAccessOwner}/${earlyAccessDir}` const earlyAccessContentDir = path.join(process.cwd(), 'content', earlyAccessDir) // production vs. staging environment @@ -44,8 +36,8 @@ const environment = HEROKU_PRODUCTION_APP ? 'production' : 'staging' const earlyAccessBranch = HEROKU_PRODUCTION_APP ? eaConfig.EA_PRODUCTION_BRANCH : eaConfig.EA_STAGING_BRANCH // confirm that the branch exists in the remote -const doesBranchExist = execSync(`git ls-remote --heads ${earlyAccessRepo} ${earlyAccessBranch}`).toString() -if (!doesBranchExist) { +const branchExists = execSync(`git ls-remote --heads ${earlyAccessRepo} ${earlyAccessBranch}`).toString() +if (!branchExists) { console.log(`The branch '${earlyAccessBranch}' was not found in ${earlyAccessOwner}/${earlyAccessDir}. Exiting!`) process.exit(0) } From a36c112240320facc11c9b891b0fe93964b717c0 Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Fri, 23 Oct 2020 14:00:25 -0400 Subject: [PATCH 020/105] test for presence of content/early-access dir --- tests/unit/early-access.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 tests/unit/early-access.js diff --git a/tests/unit/early-access.js b/tests/unit/early-access.js new file mode 100644 index 0000000000..8c2c9c4c1a --- /dev/null +++ b/tests/unit/early-access.js @@ -0,0 +1,12 @@ +// TODO test ea-config + +const fs = require('fs') +const path = require('path') + +// TODO this should not fail if run locally without cloning early-access +describe('cloning content/early-access', () => { + test('the content directory exists', async () => { + const eaContentDir = path.join(process.cwd(), 'content/early-access-test') + expect(fs.existsSync(eaContentDir)).toBe(true) + }) +}) From e94e4b77809ec4540bea324b6c8173dff5c271bf Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Fri, 23 Oct 2020 14:00:42 -0400 Subject: [PATCH 021/105] clone early-access repo during testing --- .github/workflows/test.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3b094d49e8..439803ec45 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -63,6 +63,12 @@ jobs: name: Install dependencies run: npm ci + - if: ${{ needs.see_if_should_skip.outputs.should_skip != 'true' }} + name: Clone early access + run: npm run heroku-prebuild + env: + DOCUBOT_REPO_PAT: ${{ secrets.DOCUBOT_REPO_PAT }} + - if: ${{ needs.see_if_should_skip.outputs.should_skip != 'true' }} name: Run linter run: npx standard @@ -109,6 +115,12 @@ jobs: name: Install dependencies run: npm ci + - if: ${{ needs.see_if_should_skip.outputs.should_skip != 'true' }} + name: Clone early access + run: npm run heroku-prebuild + env: + DOCUBOT_REPO_PAT: ${{ secrets.DOCUBOT_REPO_PAT }} + - if: ${{ needs.see_if_should_skip.outputs.should_skip != 'true' }} name: Run build script run: npm run build From b020382e821c092d090ebd1f596e18384c9b7883 Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Fri, 23 Oct 2020 14:10:59 -0400 Subject: [PATCH 022/105] empty commit From cad55f5fcfcfb5a364e170cb4943a115eaeac757 Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Mon, 26 Oct 2020 09:51:54 -0400 Subject: [PATCH 023/105] set staging branch back to main --- ea-config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ea-config.yml b/ea-config.yml index a57f0096f1..4f342b6a20 100644 --- a/ea-config.yml +++ b/ea-config.yml @@ -2,5 +2,5 @@ EA_PRODUCTION_BRANCH: main # Set to the early-access branch you want to stage -# This MUST match your docs branch name +# This MUST match your docs-internal branch name EA_STAGING_BRANCH: main From 1e9a3196479f2f327290129ad06cb0cb25289f5d Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Mon, 26 Oct 2020 09:52:16 -0400 Subject: [PATCH 024/105] conditional test of early access in actions only --- tests/unit/early-access.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/unit/early-access.js b/tests/unit/early-access.js index 8c2c9c4c1a..84732a31b8 100644 --- a/tests/unit/early-access.js +++ b/tests/unit/early-access.js @@ -1,11 +1,13 @@ -// TODO test ea-config - const fs = require('fs') const path = require('path') -// TODO this should not fail if run locally without cloning early-access +const testViaActionsOnly = process.env.GITHUB_ACTIONS ? test : test.skip + +// TODO test ea-config + +// TODO this should only run locally describe('cloning content/early-access', () => { - test('the content directory exists', async () => { + testViaActionsOnly('the content directory exists', async () => { const eaContentDir = path.join(process.cwd(), 'content/early-access-test') expect(fs.existsSync(eaContentDir)).toBe(true) }) From 24a281a6738539b81b038770aaffff4f61d2d6f3 Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Mon, 26 Oct 2020 09:52:35 -0400 Subject: [PATCH 025/105] run clone script from docs-internal only --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 439803ec45..4b9d365ce9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -115,7 +115,7 @@ jobs: name: Install dependencies run: npm ci - - if: ${{ needs.see_if_should_skip.outputs.should_skip != 'true' }} + - if: ${{ needs.see_if_should_skip.outputs.should_skip != 'true' && GITHUB_REPOSITORY == 'github/docs-internal' }} name: Clone early access run: npm run heroku-prebuild env: From df545e8de8206d2ddc6d8e48ab13fcdb1c6b4236 Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Mon, 26 Oct 2020 09:52:51 -0400 Subject: [PATCH 026/105] update var refs in script --- script/clone-early-access.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/script/clone-early-access.js b/script/clone-early-access.js index 99f795439e..5c7c82a46c 100755 --- a/script/clone-early-access.js +++ b/script/clone-early-access.js @@ -28,15 +28,18 @@ const eaConfig = yaml.load(fs.readFileSync(path.join(process.cwd(), 'ea-config.y // Early Access details const earlyAccessOwner = 'docs' const earlyAccessDir = 'early-access-test' -const earlyAccessRepo = `https://${DOCUBOT_REPO_PAT}@github.com/${earlyAccessOwner}/${earlyAccessDir}` +const earlyAccessFullRepo = `https://${DOCUBOT_REPO_PAT}@github.com/${earlyAccessOwner}/${earlyAccessDir}` const earlyAccessContentDir = path.join(process.cwd(), 'content', earlyAccessDir) -// production vs. staging environment +// Production vs. staging environment +// TODO test that this works as expected const environment = HEROKU_PRODUCTION_APP ? 'production' : 'staging' + +// Early access branch to clone const earlyAccessBranch = HEROKU_PRODUCTION_APP ? eaConfig.EA_PRODUCTION_BRANCH : eaConfig.EA_STAGING_BRANCH -// confirm that the branch exists in the remote -const branchExists = execSync(`git ls-remote --heads ${earlyAccessRepo} ${earlyAccessBranch}`).toString() +// Confirm that the branch exists in the remote +const branchExists = execSync(`git ls-remote --heads ${earlyAccessFullRepo} ${earlyAccessBranch}`).toString() if (!branchExists) { console.log(`The branch '${earlyAccessBranch}' was not found in ${earlyAccessOwner}/${earlyAccessDir}. Exiting!`) process.exit(0) @@ -46,7 +49,7 @@ if (!branchExists) { rimraf(earlyAccessContentDir) // Clone the repo -execSync(`git clone --single-branch --branch ${earlyAccessBranch} ${earlyAccessRepo} ${earlyAccessContentDir}`) +execSync(`git clone --single-branch --branch ${earlyAccessBranch} ${earlyAccessFullRepo} ${earlyAccessContentDir}`) console.log(`Using early-access ${environment} branch: '${earlyAccessBranch}'`) // Remove the .git dir From 67466e8a43661685b50f4e7e140e22a773e14d4c Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Mon, 2 Nov 2020 14:23:46 -0500 Subject: [PATCH 027/105] duplicate change between lint and test steps --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4b9d365ce9..a0005c0477 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -63,7 +63,7 @@ jobs: name: Install dependencies run: npm ci - - if: ${{ needs.see_if_should_skip.outputs.should_skip != 'true' }} + - if: ${{ needs.see_if_should_skip.outputs.should_skip != 'true' && GITHUB_REPOSITORY == 'github/docs-internal' }} name: Clone early access run: npm run heroku-prebuild env: From 04e96560f434b9576eff71df651c9f191510f2f9 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Mon, 2 Nov 2020 19:03:28 -0600 Subject: [PATCH 028/105] Use Heroku postbuild so the dependencies are installed before cloning early access --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 010dbdb090..bb785bdf74 100644 --- a/package.json +++ b/package.json @@ -140,7 +140,7 @@ "prevent-pushes-to-main": "node script/prevent-pushes-to-main.js", "pa11y-ci": "pa11y-ci", "pa11y-test": "start-server-and-test browser-test-server 4001 pa11y-ci", - "heroku-prebuild": "node script/clone-early-access.js" + "heroku-postbuild": "node script/clone-early-access.js" }, "engines": { "node": "12 - 14" From 9720574a1b0c7c970d87ad8a96fbd6e6efb87166 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Tue, 3 Nov 2020 10:08:14 -0600 Subject: [PATCH 029/105] Add a username to the Git repo string for more completeness --- script/clone-early-access.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/clone-early-access.js b/script/clone-early-access.js index 5c7c82a46c..6e60e45ab8 100755 --- a/script/clone-early-access.js +++ b/script/clone-early-access.js @@ -28,7 +28,7 @@ const eaConfig = yaml.load(fs.readFileSync(path.join(process.cwd(), 'ea-config.y // Early Access details const earlyAccessOwner = 'docs' const earlyAccessDir = 'early-access-test' -const earlyAccessFullRepo = `https://${DOCUBOT_REPO_PAT}@github.com/${earlyAccessOwner}/${earlyAccessDir}` +const earlyAccessFullRepo = `https://x-access-token:${DOCUBOT_REPO_PAT}@github.com/${earlyAccessOwner}/${earlyAccessDir}` const earlyAccessContentDir = path.join(process.cwd(), 'content', earlyAccessDir) // Production vs. staging environment From 850c11aca75c933de650e01c9882bdde7f16e1f2 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Tue, 3 Nov 2020 10:19:42 -0600 Subject: [PATCH 030/105] Fix workflow syntax error --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a0005c0477..a6857c2b31 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -63,7 +63,7 @@ jobs: name: Install dependencies run: npm ci - - if: ${{ needs.see_if_should_skip.outputs.should_skip != 'true' && GITHUB_REPOSITORY == 'github/docs-internal' }} + - if: ${{ needs.see_if_should_skip.outputs.should_skip != 'true' && github.repository == 'github/docs-internal' }} name: Clone early access run: npm run heroku-prebuild env: From c6980ab666cc2e8e709f824d4cb911da3bb4f25a Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Tue, 3 Nov 2020 10:24:08 -0600 Subject: [PATCH 031/105] Fix the other workflow syntax error I missed --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a6857c2b31..f80b99a661 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -27,7 +27,7 @@ jobs: with: cancel_others: 'false' github_token: ${{ github.token }} - paths: '[".github/workflows/test.yml",".node-version", ".npmrc", "app.json", "content/**", "data/**","lib/**", "Dockerfile", "feature-flags.json", "Gemfile", "Gemfile.lock", "middleware/**", "node_modules/**","package.json", "package-lock.json", "server.js", "tests/**", "translations/**", "Procfile", "webpack.config.js"]' + paths: '[".github/workflows/test.yml", ".node-version", ".npmrc", "app.json", "content/**", "data/**","lib/**", "Dockerfile", "feature-flags.json", "Gemfile", "Gemfile.lock", "middleware/**", "node_modules/**","package.json", "package-lock.json", "server.js", "tests/**", "translations/**", "Procfile", "webpack.config.js"]' lint: needs: see_if_should_skip runs-on: ubuntu-latest @@ -115,7 +115,7 @@ jobs: name: Install dependencies run: npm ci - - if: ${{ needs.see_if_should_skip.outputs.should_skip != 'true' && GITHUB_REPOSITORY == 'github/docs-internal' }} + - if: ${{ needs.see_if_should_skip.outputs.should_skip != 'true' && github.repository == 'github/docs-internal' }} name: Clone early access run: npm run heroku-prebuild env: From 3008ad1b70cca0828d1b436b5e7ab7aaf4574c77 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Tue, 3 Nov 2020 10:30:11 -0600 Subject: [PATCH 032/105] Update all heroku-prebuild refs to heroku-postbuild --- .github/workflows/test.yml | 4 ++-- script/clone-early-access.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f80b99a661..99890df5c0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -65,7 +65,7 @@ jobs: - if: ${{ needs.see_if_should_skip.outputs.should_skip != 'true' && github.repository == 'github/docs-internal' }} name: Clone early access - run: npm run heroku-prebuild + run: npm run heroku-postbuild env: DOCUBOT_REPO_PAT: ${{ secrets.DOCUBOT_REPO_PAT }} @@ -117,7 +117,7 @@ jobs: - if: ${{ needs.see_if_should_skip.outputs.should_skip != 'true' && github.repository == 'github/docs-internal' }} name: Clone early access - run: npm run heroku-prebuild + run: npm run heroku-postbuild env: DOCUBOT_REPO_PAT: ${{ secrets.DOCUBOT_REPO_PAT }} diff --git a/script/clone-early-access.js b/script/clone-early-access.js index 6e60e45ab8..f88ebb9284 100755 --- a/script/clone-early-access.js +++ b/script/clone-early-access.js @@ -2,7 +2,7 @@ // [start-readme] // -// This script is run as a prebuild script during staging and deployments on Heroku. +// This script is run as a postbuild script during staging and deployments on Heroku. // If you have access to the PAT, it can also be run locally to clone https://github.com/docs/early-access. // The script clones a branch in the early-access repo that matches the current branch in the docs repo; // if one can't be found, it clones the `main` branch. From 3e097158a9218d1aa8eaabcc0cfa3de0f8fe53ee Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Tue, 3 Nov 2020 10:49:18 -0600 Subject: [PATCH 033/105] Add debugging log to cloning script --- script/clone-early-access.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/script/clone-early-access.js b/script/clone-early-access.js index f88ebb9284..1be67d8ee3 100755 --- a/script/clone-early-access.js +++ b/script/clone-early-access.js @@ -31,6 +31,8 @@ const earlyAccessDir = 'early-access-test' const earlyAccessFullRepo = `https://x-access-token:${DOCUBOT_REPO_PAT}@github.com/${earlyAccessOwner}/${earlyAccessDir}` const earlyAccessContentDir = path.join(process.cwd(), 'content', earlyAccessDir) +console.log(`Full repo URL: ${earlyAccessFullRepo}`) + // Production vs. staging environment // TODO test that this works as expected const environment = HEROKU_PRODUCTION_APP ? 'production' : 'staging' From 1ea1396b62f541c176886084d77ede484d681657 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Tue, 3 Nov 2020 10:54:30 -0600 Subject: [PATCH 034/105] Split debugging statement --- script/clone-early-access.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/script/clone-early-access.js b/script/clone-early-access.js index 1be67d8ee3..07974f7e6f 100755 --- a/script/clone-early-access.js +++ b/script/clone-early-access.js @@ -31,7 +31,10 @@ const earlyAccessDir = 'early-access-test' const earlyAccessFullRepo = `https://x-access-token:${DOCUBOT_REPO_PAT}@github.com/${earlyAccessOwner}/${earlyAccessDir}` const earlyAccessContentDir = path.join(process.cwd(), 'content', earlyAccessDir) -console.log(`Full repo URL: ${earlyAccessFullRepo}`) +console.log(`PAT 0-8: ${DOCUBOT_REPO_PAT.slice(0, 8)}`) +console.log(`PAT 8-16: ${DOCUBOT_REPO_PAT.slice(8, 16)}`) +console.log(`PAT 16-24: ${DOCUBOT_REPO_PAT.slice(16, 24)}`) +console.log(`PAT 24+: ${DOCUBOT_REPO_PAT.slice(24)}`) // Production vs. staging environment // TODO test that this works as expected From 7514a54785995d7ae7ea2af98fed1523df370bab Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Tue, 3 Nov 2020 10:56:46 -0600 Subject: [PATCH 035/105] Remove EA cloning from lint -- no JS files in there to lint --- .github/workflows/test.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 99890df5c0..78d2d6aad6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -63,12 +63,6 @@ jobs: name: Install dependencies run: npm ci - - if: ${{ needs.see_if_should_skip.outputs.should_skip != 'true' && github.repository == 'github/docs-internal' }} - name: Clone early access - run: npm run heroku-postbuild - env: - DOCUBOT_REPO_PAT: ${{ secrets.DOCUBOT_REPO_PAT }} - - if: ${{ needs.see_if_should_skip.outputs.should_skip != 'true' }} name: Run linter run: npx standard From d00f43758cae80d924c043a04829e2c39c905db2 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Tue, 3 Nov 2020 10:59:08 -0600 Subject: [PATCH 036/105] Remove debugging statements --- script/clone-early-access.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/script/clone-early-access.js b/script/clone-early-access.js index 07974f7e6f..f88ebb9284 100755 --- a/script/clone-early-access.js +++ b/script/clone-early-access.js @@ -31,11 +31,6 @@ const earlyAccessDir = 'early-access-test' const earlyAccessFullRepo = `https://x-access-token:${DOCUBOT_REPO_PAT}@github.com/${earlyAccessOwner}/${earlyAccessDir}` const earlyAccessContentDir = path.join(process.cwd(), 'content', earlyAccessDir) -console.log(`PAT 0-8: ${DOCUBOT_REPO_PAT.slice(0, 8)}`) -console.log(`PAT 8-16: ${DOCUBOT_REPO_PAT.slice(8, 16)}`) -console.log(`PAT 16-24: ${DOCUBOT_REPO_PAT.slice(16, 24)}`) -console.log(`PAT 24+: ${DOCUBOT_REPO_PAT.slice(24)}`) - // Production vs. staging environment // TODO test that this works as expected const environment = HEROKU_PRODUCTION_APP ? 'production' : 'staging' From 105bf42f870ae815fa2e2a9dd0b17903d3d0136d Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Tue, 3 Nov 2020 11:27:20 -0600 Subject: [PATCH 037/105] Try removing the x-access-token username from the Git URL --- script/clone-early-access.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/clone-early-access.js b/script/clone-early-access.js index f88ebb9284..c96be99c35 100755 --- a/script/clone-early-access.js +++ b/script/clone-early-access.js @@ -28,7 +28,7 @@ const eaConfig = yaml.load(fs.readFileSync(path.join(process.cwd(), 'ea-config.y // Early Access details const earlyAccessOwner = 'docs' const earlyAccessDir = 'early-access-test' -const earlyAccessFullRepo = `https://x-access-token:${DOCUBOT_REPO_PAT}@github.com/${earlyAccessOwner}/${earlyAccessDir}` +const earlyAccessFullRepo = `https://${DOCUBOT_REPO_PAT}@github.com/${earlyAccessOwner}/${earlyAccessDir}` const earlyAccessContentDir = path.join(process.cwd(), 'content', earlyAccessDir) // Production vs. staging environment From da7afd4e55222fd67879548f61f46d9622183282 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Tue, 3 Nov 2020 12:02:37 -0600 Subject: [PATCH 038/105] Manually override GitHub Actions extraHeader --- .github/workflows/test.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 78d2d6aad6..cec3b0ec07 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -109,6 +109,18 @@ jobs: name: Install dependencies run: npm ci + - if: ${{ needs.see_if_should_skip.outputs.should_skip != 'true' && github.repository == 'github/docs-internal' }} + name: Override Actions git authorization extraHeader config + env: + DOCUBOT_REPO_PAT: ${{ secrets.DOCUBOT_REPO_PAT }} + run: | + # Reset the header + git config --local "http.https://github.com/.extraheader" "" + + # Override the header + GIT_AUTH_HEADER='AUTHORIZATION: basic '$(printf "x-access-token:$DOCUBOT_REPO_PAT" | base64) + git config --local "http.https://github.com/docs/early-access-test/.extraheader" "${GIT_AUTH_HEADER}" + - if: ${{ needs.see_if_should_skip.outputs.should_skip != 'true' && github.repository == 'github/docs-internal' }} name: Clone early access run: npm run heroku-postbuild From e913c26006a11394e816aabc92098f463cde9274 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Tue, 3 Nov 2020 12:11:23 -0600 Subject: [PATCH 039/105] Try ONLY clearing out the extraHeader value rather than overwriting it --- .github/workflows/test.yml | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cec3b0ec07..4cc4f91f08 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -110,16 +110,8 @@ jobs: run: npm ci - if: ${{ needs.see_if_should_skip.outputs.should_skip != 'true' && github.repository == 'github/docs-internal' }} - name: Override Actions git authorization extraHeader config - env: - DOCUBOT_REPO_PAT: ${{ secrets.DOCUBOT_REPO_PAT }} - run: | - # Reset the header - git config --local "http.https://github.com/.extraheader" "" - - # Override the header - GIT_AUTH_HEADER='AUTHORIZATION: basic '$(printf "x-access-token:$DOCUBOT_REPO_PAT" | base64) - git config --local "http.https://github.com/docs/early-access-test/.extraheader" "${GIT_AUTH_HEADER}" + name: Clear out the Actions git authorization extraHeader config + run: git config --local "http.https://github.com/.extraheader" "" - if: ${{ needs.see_if_should_skip.outputs.should_skip != 'true' && github.repository == 'github/docs-internal' }} name: Clone early access From c4b50183a2f74b3b057be5e844ce5ec5f52b0bf9 Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Tue, 3 Nov 2020 17:17:43 -0500 Subject: [PATCH 040/105] update docs path in newly added robots test --- tests/rendering/robots-txt.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/rendering/robots-txt.js b/tests/rendering/robots-txt.js index b3bf73e327..c9bca76e09 100644 --- a/tests/rendering/robots-txt.js +++ b/tests/rendering/robots-txt.js @@ -118,15 +118,15 @@ describe('robots.txt', () => { enterpriseServerReleases.deprecated.forEach(version => { const blockedPaths = [ // English - `https://help.github.com/en/enterprise-server@${version}/actions`, - `https://help.github.com/en/enterprise/${version}/actions`, - `https://help.github.com/en/enterprise-server@${version}/actions/overview`, - `https://help.github.com/en/enterprise/${version}/actions/overview`, + `https://docs.github.com/en/enterprise-server@${version}/actions`, + `https://docs.github.com/en/enterprise/${version}/actions`, + `https://docs.github.com/en/enterprise-server@${version}/actions/overview`, + `https://docs.github.com/en/enterprise/${version}/actions/overview`, // Japanese - `https://help.github.com/ja/enterprise-server@${version}/actions`, - `https://help.github.com/ja/enterprise/${version}/actions`, - `https://help.github.com/ja/enterprise-server@${version}/actions/overview`, - `https://help.github.com/ja/enterprise/${version}/actions/overview` + `https://docs.github.com/ja/enterprise-server@${version}/actions`, + `https://docs.github.com/ja/enterprise/${version}/actions`, + `https://docs.github.com/ja/enterprise-server@${version}/actions/overview`, + `https://docs.github.com/ja/enterprise/${version}/actions/overview` ] blockedPaths.forEach(path => { From 2be96edf736d3f97fd8d515e9c427c130bc64d3e Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Wed, 4 Nov 2020 11:19:46 -0600 Subject: [PATCH 041/105] Try using actions/checkout's persist-credentials input instead of manually changing the local git config --- .github/workflows/test.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1230afb9aa..566734f9eb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -37,6 +37,9 @@ jobs: - if: ${{ needs.see_if_should_skip.outputs.should_skip != 'true' }} name: Check out repo uses: actions/checkout@a81bbbf8298c0fa03ea29cdc473d45769f953675 + with: + # Enables cloning the Early Access repo later with the relevant PAT + persist-credentials: 'false' - if: ${{ needs.see_if_should_skip.outputs.should_skip != 'true' }} name: Setup node @@ -109,10 +112,6 @@ jobs: name: Install dependencies run: npm ci - - if: ${{ needs.see_if_should_skip.outputs.should_skip != 'true' && github.repository == 'github/docs-internal' }} - name: Clear out the Actions git authorization extraHeader config - run: git config --local "http.https://github.com/.extraheader" "" - - if: ${{ needs.see_if_should_skip.outputs.should_skip != 'true' && github.repository == 'github/docs-internal' }} name: Clone early access run: npm run heroku-postbuild From 4868f97aca5b53124ad911ba4608f4cadb69ef36 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Wed, 4 Nov 2020 11:24:11 -0600 Subject: [PATCH 042/105] Explicitly add 'npm run build' to the 'heroku-postbuild' script as the Heroku build pack will only run one or the other by default --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a6eba5923d..f45ba73e65 100644 --- a/package.json +++ b/package.json @@ -140,7 +140,7 @@ "prevent-pushes-to-main": "node script/prevent-pushes-to-main.js", "pa11y-ci": "pa11y-ci", "pa11y-test": "start-server-and-test browser-test-server 4001 pa11y-ci", - "heroku-postbuild": "node script/clone-early-access.js" + "heroku-postbuild": "node script/clone-early-access.js && npm run build" }, "engines": { "node": "12 - 14" From ed3baeb5dc2376951f1fb5ff6d99a05cee57917f Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Thu, 5 Nov 2020 11:56:27 -0600 Subject: [PATCH 043/105] Update to use new github/docs-early-access repo --- script/clone-early-access.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/script/clone-early-access.js b/script/clone-early-access.js index c96be99c35..50abb85b77 100755 --- a/script/clone-early-access.js +++ b/script/clone-early-access.js @@ -26,9 +26,10 @@ const yaml = require('js-yaml') const eaConfig = yaml.load(fs.readFileSync(path.join(process.cwd(), 'ea-config.yml'), 'utf8')) // Early Access details -const earlyAccessOwner = 'docs' -const earlyAccessDir = 'early-access-test' -const earlyAccessFullRepo = `https://${DOCUBOT_REPO_PAT}@github.com/${earlyAccessOwner}/${earlyAccessDir}` +const earlyAccessOwner = 'github' +const earlyAccessRepo = 'docs-early-access' +const earlyAccessDir = 'early-access' +const earlyAccessFullRepo = `https://${DOCUBOT_REPO_PAT}@github.com/${earlyAccessOwner}/${earlyAccessRepo}` const earlyAccessContentDir = path.join(process.cwd(), 'content', earlyAccessDir) // Production vs. staging environment @@ -41,7 +42,7 @@ const earlyAccessBranch = HEROKU_PRODUCTION_APP ? eaConfig.EA_PRODUCTION_BRANCH // Confirm that the branch exists in the remote const branchExists = execSync(`git ls-remote --heads ${earlyAccessFullRepo} ${earlyAccessBranch}`).toString() if (!branchExists) { - console.log(`The branch '${earlyAccessBranch}' was not found in ${earlyAccessOwner}/${earlyAccessDir}. Exiting!`) + console.log(`The branch '${earlyAccessBranch}' was not found in ${earlyAccessOwner}/${earlyAccessRepo}. Exiting!`) process.exit(0) } From f410fd175c04ccbf17beb3d93579296574bb68af Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Mon, 9 Nov 2020 13:07:04 -0600 Subject: [PATCH 044/105] Mostly reverting github/docs-internal#15313 --- .env.example | 3 - .github/workflows/test.yml | 7 --- .gitignore | 11 ++-- crowdin.yml | 3 +- lib/fetch-early-access-paths.js | 33 ----------- lib/warm-server.js | 10 ++-- middleware/context.js | 3 +- middleware/early-access-paths.js | 33 ----------- middleware/early-access-proxy.js | 25 --------- middleware/index.js | 3 +- middleware/list-hidden-pages.js | 21 +++++++ middleware/render-page.js | 3 +- package-lock.json | 29 ++++------ package.json | 1 - tests/content/crowdin-config.js | 37 +++++++++++++ tests/rendering/early-access-paths.js | 64 --------------------- tests/rendering/early-access-proxy.js | 80 --------------------------- tests/rendering/server.js | 39 +++++++++++++ tests/unit/early-access.js | 14 ++++- 19 files changed, 136 insertions(+), 283 deletions(-) delete mode 100644 lib/fetch-early-access-paths.js delete mode 100644 middleware/early-access-paths.js delete mode 100644 middleware/early-access-proxy.js create mode 100644 middleware/list-hidden-pages.js delete mode 100644 tests/rendering/early-access-paths.js delete mode 100644 tests/rendering/early-access-proxy.js diff --git a/.env.example b/.env.example index 20c941977c..e8896feab8 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,3 @@ ALGOLIA_API_KEY= ALGOLIA_APPLICATION_ID= ALLOW_TRANSLATION_COMMITS= -EARLY_ACCESS_HOSTNAME= -EARLY_ACCESS_SHARED_SECRET= -GITHUB_TOKEN= \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 566734f9eb..a0597d5a0b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -127,10 +127,3 @@ jobs: run: npx jest tests/${{ matrix.test-group }}/ env: NODE_OPTIONS: "--max_old_space_size=4096" - - - name: Send Slack notification if workflow fails - uses: rtCamp/action-slack-notify@e17352feaf9aee300bf0ebc1dfbf467d80438815 - if: failure() && github.ref == 'early-access' - env: - SLACK_WEBHOOK: ${{ secrets.DOCS_ALERTS_SLACK_WEBHOOK }} - SLACK_MESSAGE: "Tests are failing on the `early-access` branch. https://github.com/github/docs-internal/tree/early-access" diff --git a/.gitignore b/.gitignore index 4c960428f8..9c06d3b323 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,14 @@ .algolia-cache .DS_Store .env -node_modules +/node_modules/ npm-debug.log -coverage -content/early-access -content/early-access-test +coverage/ +/assets/early-access/ +/content/early-access/ +/data/early-access/ # blc: broken link checker blc_output.log blc_output_internal.log -dist +/dist/ diff --git a/crowdin.yml b/crowdin.yml index 5f0cf63ee4..726820c67c 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -14,9 +14,10 @@ files: "data/reusables/README.md", "data/variables/product.yml", "data/variables/README.md", + "data/early-access", "data/graphql", "data/products.yml" - ] + ] # These end up as env vars used by the GitHub Actions workflow project_id_env: CROWDIN_PROJECT_ID diff --git a/lib/fetch-early-access-paths.js b/lib/fetch-early-access-paths.js deleted file mode 100644 index 6cc6b86e02..0000000000 --- a/lib/fetch-early-access-paths.js +++ /dev/null @@ -1,33 +0,0 @@ -// This module loads an array of Early Access page paths from EARLY_ACCESS_HOSTNAME -// -// See also middleware/early-acces-proxy.js which fetches Early Access docs from the obscured remote host - -require('dotenv').config() - -const got = require('got') -const isURL = require('is-url') - -module.exports = async function fetchEarlyAccessPaths () { - let url - if (process.env.NODE_ENV === 'test') return [] - - if (!isURL(process.env.EARLY_ACCESS_HOSTNAME)) { - console.log('EARLY_ACCESS_HOSTNAME is not defined; skipping fetching early access paths') - return [] - } - - try { - url = `${process.env.EARLY_ACCESS_HOSTNAME}/early-access-paths.json` - const { body } = await got(url, { - json: true, - timeout: 3000, - headers: { - 'early-access-shared-secret': process.env.EARLY_ACCESS_SHARED_SECRET - } - }) - return body - } catch (err) { - console.error('Unable to fetch early-access-paths.json from', url, err) - return [] - } -} diff --git a/lib/warm-server.js b/lib/warm-server.js index 355fd7531c..5abfef326e 100644 --- a/lib/warm-server.js +++ b/lib/warm-server.js @@ -1,5 +1,4 @@ -const fetchEarlyAccessPaths = require('./fetch-early-access-paths') -let pages, site, redirects, siteTree, earlyAccessPaths +let pages, site, redirects, siteTree module.exports = async function warmServer () { if (!pages) { @@ -8,10 +7,9 @@ module.exports = async function warmServer () { } // Promise.all is used to load multiple things in parallel - ;[pages, site, earlyAccessPaths] = await Promise.all([ + ;[pages, site] = await Promise.all([ require('./pages')(), - require('./site-data')(), - fetchEarlyAccessPaths() + require('./site-data')() ]) redirects = await require('./redirects/precompile')(pages) @@ -19,6 +17,6 @@ module.exports = async function warmServer () { } return { - pages, site, redirects, siteTree, earlyAccessPaths + pages, site, redirects, siteTree } } diff --git a/middleware/context.js b/middleware/context.js index 55f0f055e5..54fbbdcbe7 100644 --- a/middleware/context.js +++ b/middleware/context.js @@ -12,7 +12,7 @@ const featureFlags = Object.keys(require('../feature-flags')) // Note that additional middleware in middleware/index.js adds to this context object module.exports = async function contextualize (req, res, next) { // Ensure that we load some data only once on first request - const { site, redirects, pages, siteTree, earlyAccessPaths } = await warmServer() + const { site, redirects, pages, siteTree } = await warmServer() req.context = {} // make feature flag environment variables accessible in layouts @@ -33,7 +33,6 @@ module.exports = async function contextualize (req, res, next) { req.context.currentPath = req.path req.context.query = req.query req.context.languages = languages - req.context.earlyAccessPaths = earlyAccessPaths req.context.productNames = productNames req.context.enterpriseServerReleases = enterpriseServerReleases req.context.enterpriseServerVersions = Object.keys(allVersions).filter(version => version.startsWith('enterprise-server@')) diff --git a/middleware/early-access-paths.js b/middleware/early-access-paths.js deleted file mode 100644 index 164780f1ac..0000000000 --- a/middleware/early-access-paths.js +++ /dev/null @@ -1,33 +0,0 @@ -const { chain } = require('lodash') -let paths - -// This middleware finds all pages with `hidden: true` frontmatter -// and responds with a JSON array of all requests paths (and redirects) that lead to those pages. - -// Requesting this path from EARLY_ACCESS_HOSTNAME will respond with an array of Early Access paths. -// Requesting this path from docs.github.com (production) will respond with an empty array (no Early Access paths). - -module.exports = async (req, res, next) => { - if (req.path !== '/early-access-paths.json') return next() - - if ( - !req.headers || - !req.headers['early-access-shared-secret'] || - req.headers['early-access-shared-secret'] !== process.env.EARLY_ACCESS_SHARED_SECRET - ) { - return res.status(401).send({ error: '401 Unauthorized' }) - } - - paths = paths || chain(req.context.pages) - .filter(page => page.hidden && page.languageCode === 'en') - .map(page => { - const permalinks = page.permalinks.map(permalink => permalink.href) - const redirects = Object.keys(page.redirects) - return permalinks.concat(redirects) - }) - .flatten() - .uniq() - .value() - - return res.json(paths) -} diff --git a/middleware/early-access-proxy.js b/middleware/early-access-proxy.js deleted file mode 100644 index 5df20e2228..0000000000 --- a/middleware/early-access-proxy.js +++ /dev/null @@ -1,25 +0,0 @@ -// This module serves requests to Early Access content from a hidden proxy host (EARLY_ACCESS_HOSTNAME). -// Paths to this content are fetched in the warmServer module at startup. - -const got = require('got') -const isURL = require('is-url') - -module.exports = async (req, res, next) => { - if ( - isURL(process.env.EARLY_ACCESS_HOSTNAME) && - req.context && - req.context.earlyAccessPaths && - req.context.earlyAccessPaths.includes(req.path) - ) { - try { - const proxyURL = `${process.env.EARLY_ACCESS_HOSTNAME}${req.path}` - const proxiedRes = await got(proxyURL) - res.set('content-type', proxiedRes.headers['content-type']) - res.send(proxiedRes.body) - } catch (err) { - next() - } - } else { - next() - } -} diff --git a/middleware/index.js b/middleware/index.js index 98106e419c..9a7ea154be 100644 --- a/middleware/index.js +++ b/middleware/index.js @@ -40,8 +40,6 @@ module.exports = function (app) { app.use(require('./detect-language')) app.use(asyncMiddleware(require('./context'))) app.use('/csrf', require('./csrf-route')) - app.use(require('./early-access-paths')) - app.use(require('./early-access-proxy')) app.use(require('./find-page')) app.use(require('./notices')) app.use(require('./archived-enterprise-versions')) @@ -56,6 +54,7 @@ module.exports = function (app) { app.use(require('./contextualizers/webhooks')) app.use(require('./disable-caching-on-safari')) app.get('/_500', asyncMiddleware(require('./trigger-error'))) + app.get('/hidden', require('./list-hidden-pages')) app.use(require('./breadcrumbs')) app.use(require('./featured-links')) app.get('/*', asyncMiddleware(require('./render-page'))) diff --git a/middleware/list-hidden-pages.js b/middleware/list-hidden-pages.js new file mode 100644 index 0000000000..d82a3d7b71 --- /dev/null +++ b/middleware/list-hidden-pages.js @@ -0,0 +1,21 @@ +module.exports = async function listHidden (req, res, next) { + if (process.env.NODE_ENV === 'production') { + return res.status(403).end() + } + + const hiddenPages = req.context.pages.filter(page => page.hidden) + let urls = [] + + hiddenPages.forEach(page => { + const pageUrls = page.permalinks.map(permalink => permalink.href) + urls = urls.concat(pageUrls) + }) + + const output = ` + + ` + + return res.send(output) +} diff --git a/middleware/render-page.js b/middleware/render-page.js index c24b00935d..e7921135db 100644 --- a/middleware/render-page.js +++ b/middleware/render-page.js @@ -1,5 +1,4 @@ const { get } = require('lodash') -const env = require('lil-env-thing') const { liquid } = require('../lib/render-content') const patterns = require('../lib/patterns') const layouts = require('../lib/layouts') @@ -64,7 +63,7 @@ module.exports = async function renderPage (req, res, next) { } // `?json` query param for debugging request context - if ('json' in req.query && !env.production) { + if ('json' in req.query && process.env.NODE_ENV !== 'production') { if (req.query.json.length > 1) { // deep reference: ?json=page.permalinks return res.json(get(context, req.query.json)) diff --git a/package-lock.json b/package-lock.json index 948120da18..18ed555816 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3169,7 +3169,7 @@ }, "agentkeepalive": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-2.2.0.tgz", + "resolved": "http://registry.npmjs.org/agentkeepalive/-/agentkeepalive-2.2.0.tgz", "integrity": "sha1-xdG9SxKQCPEWPyNvhuX66iAm4u8=" }, "aggregate-error": { @@ -3329,7 +3329,7 @@ "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=", "requires": { "sprintf-js": "~1.0.2" } @@ -4114,7 +4114,7 @@ }, "brfs": { "version": "1.6.1", - "resolved": "https://registry.npmjs.org/brfs/-/brfs-1.6.1.tgz", + "resolved": "http://registry.npmjs.org/brfs/-/brfs-1.6.1.tgz", "integrity": "sha512-OfZpABRQQf+Xsmju8XE9bDjs+uU4vLREGolP7bDgcpsI17QREyZ4Bl+2KLxxx1kCgA0fAIhKQBaBYh+PEcCqYQ==", "requires": { "quote-stream": "^1.0.1", @@ -4193,7 +4193,7 @@ }, "browserify-aes": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "requires": { "buffer-xor": "^1.0.3", @@ -4227,7 +4227,7 @@ }, "browserify-rsa": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "requires": { "bn.js": "^4.1.0", @@ -5645,7 +5645,7 @@ }, "create-hash": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "requires": { "cipher-base": "^1.0.1", @@ -5657,7 +5657,7 @@ }, "create-hmac": { "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "requires": { "cipher-base": "^1.0.3", @@ -6384,7 +6384,7 @@ }, "diffie-hellman": { "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "requires": { "bn.js": "^4.1.0", @@ -6824,7 +6824,7 @@ "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "integrity": "sha1-tKxAZIEH/c3PriQvQovqihTU8b8=", "dev": true, "requires": { "is-arrayish": "^0.2.1" @@ -9962,7 +9962,7 @@ "dependencies": { "mkdirp": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", + "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=" }, "nopt": { @@ -14899,11 +14899,6 @@ "type-check": "~0.3.2" } }, - "lil-env-thing": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lil-env-thing/-/lil-env-thing-1.0.0.tgz", - "integrity": "sha1-etQmBiG/M1rR6HE1d5s15vFmxns=" - }, "limited-request-queue": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/limited-request-queue/-/limited-request-queue-2.0.0.tgz", @@ -15143,7 +15138,7 @@ }, "magic-string": { "version": "0.22.5", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz", + "resolved": "http://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz", "integrity": "sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w==", "requires": { "vlq": "^0.2.2" @@ -18704,7 +18699,7 @@ }, "sha.js": { "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "requires": { "inherits": "^2.0.1", diff --git a/package.json b/package.json index f45ba73e65..672b7aaa44 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,6 @@ "is-url": "^1.2.4", "js-cookie": "^2.2.1", "js-yaml": "^3.14.0", - "lil-env-thing": "^1.0.0", "liquid": "^5.1.0", "lodash": "^4.17.19", "mini-css-extract-plugin": "^0.9.0", diff --git a/tests/content/crowdin-config.js b/tests/content/crowdin-config.js index 2bbb509482..b54168c969 100644 --- a/tests/content/crowdin-config.js +++ b/tests/content/crowdin-config.js @@ -1,9 +1,46 @@ const config = require('../../lib/crowdin-config').read() +const loadPages = require('../../lib/pages') +const ignoredPagePaths = config.files[0].ignore +const ignoredDataPaths = config.files[2].ignore describe('crowdin.yml config file', () => { + let pages + beforeAll(async (done) => { + pages = await loadPages() + done() + }) + test('has expected file stucture', async () => { expect(config.files.length).toBe(3) expect(config.files[0].source).toBe('/content/**/*.md') expect(config.files[0].ignore).toContain('/content/README.md') }) + + test('ignores all Early Access paths', async () => { + expect(ignoredPagePaths).toContain('content/early-access') + expect(ignoredDataPaths).toContain('data/early-access') + }) + + test('ignores all hidden pages', async () => { + const hiddenPages = pages + .filter(page => page.hidden && page.languageCode === 'en') + .map(page => `/content/${page.relativePath}`) + const overlooked = hiddenPages.filter(page => !isIgnored(page, ignoredPagePaths)) + const message = `Found some hidden pages that are not yet excluded from localization. + Please copy and paste the lines below into the \`ignore\` section of /crowdin.yml: \n\n"${overlooked.join('",\n"')}"` + + // This may not be true anymore given the separation of Early Access docs + // expect(hiddenPages.length).toBeGreaterThan(0) + expect(ignoredPagePaths.length).toBeGreaterThan(0) + expect(overlooked, message).toHaveLength(0) + }) }) + +// file is ignored if its exact filename in the list, +// or if it's within an ignored directory +function isIgnored (filename, ignoredPagePaths) { + return ignoredPagePaths.some(ignoredPath => { + const isDirectory = !ignoredPath.endsWith('.md') + return ignoredPath === filename || (isDirectory && filename.startsWith(ignoredPath)) + }) +} diff --git a/tests/rendering/early-access-paths.js b/tests/rendering/early-access-paths.js deleted file mode 100644 index d597b54b9d..0000000000 --- a/tests/rendering/early-access-paths.js +++ /dev/null @@ -1,64 +0,0 @@ -const MockExpressResponse = require('mock-express-response') -const middleware = require('../../middleware/early-access-paths') - -describe('GET /early-access-paths.json', () => { - beforeEach(() => { - delete process.env['early-access-shared-secret'] - }) - - test('responds with 401 if shared secret is missing', async () => { - const req = { - path: '/early-access-paths.json', - headers: {} - } - const res = new MockExpressResponse() - const next = jest.fn() - await middleware(req, res, next) - - expect(res._getJSON()).toEqual({ error: '401 Unauthorized' }) - }) - - test('responds with an array of hidden paths', async () => { - process.env.EARLY_ACCESS_SHARED_SECRET = 'bananas' - - const req = { - path: '/early-access-paths.json', - headers: { - 'early-access-shared-secret': 'bananas' - }, - context: { - pages: [ - { - hidden: true, - languageCode: 'en', - permalinks: [ - { href: '/some-hidden-page' } - ], - redirects: { - '/old-hidden-page': '/new-hidden-page' - } - }, - { - hidden: false, - languageCode: 'en' - } - ] - } - } - const res = new MockExpressResponse() - const next = jest.fn() - await middleware(req, res, next) - - expect(res._getJSON()).toEqual(['/some-hidden-page', '/old-hidden-page']) - }) - - test('ignores requests to other paths', async () => { - const req = { - path: '/not-early-access' - } - const res = new MockExpressResponse() - const next = jest.fn() - await middleware(req, res, next) - expect(next).toHaveBeenCalled() - }) -}) diff --git a/tests/rendering/early-access-proxy.js b/tests/rendering/early-access-proxy.js deleted file mode 100644 index e0e60ee287..0000000000 --- a/tests/rendering/early-access-proxy.js +++ /dev/null @@ -1,80 +0,0 @@ - -const middleware = require('../../middleware/early-access-proxy') -const nock = require('nock') -const MockExpressResponse = require('mock-express-response') - -describe('Early Access middleware', () => { - const OLD_EARLY_ACCESS_HOSTNAME = process.env.EARLY_ACCESS_HOSTNAME - - beforeAll(() => { - process.env.EARLY_ACCESS_HOSTNAME = 'https://secret-website.com' - }) - - afterAll(() => { - process.env.EARLY_ACCESS_HOSTNAME = OLD_EARLY_ACCESS_HOSTNAME - }) - - const baseReq = { - context: { - earlyAccessPaths: ['/alpha-product/foo', '/beta-product/bar', '/baz'] - } - } - - test('are proxied from an obscured host', async () => { - const mock = nock('https://secret-website.com') - .get('/alpha-product/foo') - .reply(200, 'yay here is your proxied content', { 'content-type': 'text/html' }) - const req = { ...baseReq, path: '/alpha-product/foo' } - const res = new MockExpressResponse() - const next = jest.fn() - await middleware(req, res, next) - expect(mock.isDone()).toBe(true) - expect(res._getString()).toBe('yay here is your proxied content') - }) - - test('follows redirects', async () => { - const mock = nock('https://secret-website.com') - .get('/alpha-product/foo') - .reply(301, undefined, { Location: 'https://secret-website.com/alpha-product/foo2' }) - .get('/alpha-product/foo2') - .reply(200, 'yay you survived the redirect', { 'content-type': 'text/html' }) - const req = { ...baseReq, path: '/alpha-product/foo' } - const res = new MockExpressResponse() - const next = jest.fn() - await middleware(req, res, next) - expect(mock.isDone()).toBe(true) - expect(res._getString()).toBe('yay you survived the redirect') - }) - - test('calls next() if no redirect is found', async () => { - const req = { ...baseReq, path: '/en' } - const res = new MockExpressResponse() - const next = jest.fn() - await middleware(req, res, next) - expect(next).toHaveBeenCalled() - }) - - test('calls next() if proxy request respond with 404', async () => { - const mock = nock('https://secret-website.com') - .get('/beta-product/bar') - .reply(404, 'no dice', { 'content-type': 'text/html' }) - const req = { ...baseReq, path: '/beta-product/bar' } - const res = new MockExpressResponse() - const next = jest.fn() - await middleware(req, res, next) - expect(mock.isDone()).toBe(true) - expect(next).toHaveBeenCalled() - }) - - test('calls next() if proxy request responds with 500', async () => { - const mock = nock('https://secret-website.com') - .get('/beta-product/bar') - .reply(500, 'no dice', { 'content-type': 'text/html' }) - const req = { ...baseReq, path: '/beta-product/bar' } - const res = new MockExpressResponse() - const next = jest.fn() - await middleware(req, res, next) - expect(mock.isDone()).toBe(true) - expect(next).toHaveBeenCalled() - }) -}) diff --git a/tests/rendering/server.js b/tests/rendering/server.js index 2b588df6b5..27c5b1bc51 100644 --- a/tests/rendering/server.js +++ b/tests/rendering/server.js @@ -3,6 +3,7 @@ const enterpriseServerReleases = require('../../lib/enterprise-server-releases') const { get, getDOM, head } = require('../helpers') const path = require('path') const nonEnterpriseDefaultVersion = require('../../lib/non-enterprise-default-version') +const loadPages = require('../../lib/pages') describe('server', () => { jest.setTimeout(60 * 1000) @@ -375,6 +376,44 @@ describe('server', () => { }) }) + describe('hidden articles', () => { + let hiddenPageHrefs, hiddenPages + + beforeAll(async (done) => { + const $ = await getDOM('/hidden') + hiddenPageHrefs = $('a').map((i, el) => $(el).attr('href')).get() + + const allPages = await loadPages() + hiddenPages = allPages.filter(page => page.languageCode === 'en' && page.hidden) + + done() + }) + + test('are listed at /hidden', async () => { + expect(hiddenPageHrefs.length).toBe(hiddenPages.length) + }) + + test('are not listed at /hidden in production', async () => { + const oldNodeEnv = process.env.NODE_ENV + process.env.NODE_ENV = 'production' + const res = await get('/hidden') + process.env.NODE_ENV = oldNodeEnv + expect(res.statusCode).toBe(403) + }) + + test('have noindex meta tags', async () => { + if (hiddenPageHrefs.length > 0) { + const $ = await getDOM(hiddenPageHrefs[0]) + expect($('meta[content="noindex"]').length).toBe(1) + } + }) + + test('non-hidden articles do not have noindex meta tags', async () => { + const $ = await getDOM('/en/articles/set-up-git') + expect($('meta[content="noindex"]').length).toBe(0) + }) + }) + describe('redirects', () => { test('redirects old articles to their English URL', async () => { const res = await get('/articles/deleting-a-team') diff --git a/tests/unit/early-access.js b/tests/unit/early-access.js index 84732a31b8..f347e018bb 100644 --- a/tests/unit/early-access.js +++ b/tests/unit/early-access.js @@ -6,9 +6,19 @@ const testViaActionsOnly = process.env.GITHUB_ACTIONS ? test : test.skip // TODO test ea-config // TODO this should only run locally -describe('cloning content/early-access', () => { +describe('cloning early-access', () => { + testViaActionsOnly('the assets directory exists', async () => { + const eaContentDir = path.join(process.cwd(), 'assets/early-access') + expect(fs.existsSync(eaContentDir)).toBe(true) + }) + testViaActionsOnly('the content directory exists', async () => { - const eaContentDir = path.join(process.cwd(), 'content/early-access-test') + const eaContentDir = path.join(process.cwd(), 'content/early-access') + expect(fs.existsSync(eaContentDir)).toBe(true) + }) + + testViaActionsOnly('the data directory exists', async () => { + const eaContentDir = path.join(process.cwd(), 'data/early-access') expect(fs.existsSync(eaContentDir)).toBe(true) }) }) From 81fda0e2079a660c231e97ef6dfca9809bfefa41 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Mon, 9 Nov 2020 14:38:41 -0600 Subject: [PATCH 045/105] Cloning script refinements and future planning --- script/clone-early-access.js | 93 +++++++++++++++++++++++++++++++----- 1 file changed, 80 insertions(+), 13 deletions(-) diff --git a/script/clone-early-access.js b/script/clone-early-access.js index 50abb85b77..3592899029 100755 --- a/script/clone-early-access.js +++ b/script/clone-early-access.js @@ -22,15 +22,28 @@ const { execSync } = require('child_process') const rimraf = require('rimraf').sync const fs = require('fs') const path = require('path') +const os = require('os') const yaml = require('js-yaml') const eaConfig = yaml.load(fs.readFileSync(path.join(process.cwd(), 'ea-config.yml'), 'utf8')) // Early Access details const earlyAccessOwner = 'github' -const earlyAccessRepo = 'docs-early-access' -const earlyAccessDir = 'early-access' -const earlyAccessFullRepo = `https://${DOCUBOT_REPO_PAT}@github.com/${earlyAccessOwner}/${earlyAccessRepo}` -const earlyAccessContentDir = path.join(process.cwd(), 'content', earlyAccessDir) +const earlyAccessRepoName = 'docs-early-access' +const earlyAccessDirName = 'early-access' +const earlyAccessFullRepo = `https://${DOCUBOT_REPO_PAT}@github.com/${earlyAccessOwner}/${earlyAccessRepoName}` + +const earlyAccessCloningParentDir = os.tmpdir() +const earlyAccessCloningDir = path.join(earlyAccessCloningParentDir, earlyAccessRepoName) + +const destinationDirNames = ['content', 'data', 'assets'] +const destinationDirsMap = destinationDirNames + .reduce( + (map, dirName) => { + map[dirName] = path.join(process.cwd(), dirName, earlyAccessDirName) + return map + }, + {} + ) // Production vs. staging environment // TODO test that this works as expected @@ -42,19 +55,73 @@ const earlyAccessBranch = HEROKU_PRODUCTION_APP ? eaConfig.EA_PRODUCTION_BRANCH // Confirm that the branch exists in the remote const branchExists = execSync(`git ls-remote --heads ${earlyAccessFullRepo} ${earlyAccessBranch}`).toString() if (!branchExists) { - console.log(`The branch '${earlyAccessBranch}' was not found in ${earlyAccessOwner}/${earlyAccessRepo}. Exiting!`) - process.exit(0) + console.error(`The branch '${earlyAccessBranch}' was not found in ${earlyAccessOwner}/${earlyAccessRepoName}. Exiting!`) + process.exit(1) } -// Remove any dir that may pre-exist -rimraf(earlyAccessContentDir) +// Remove any previously cloned copies of the early access repo +rimraf(earlyAccessCloningDir) // Clone the repo -execSync(`git clone --single-branch --branch ${earlyAccessBranch} ${earlyAccessFullRepo} ${earlyAccessContentDir}`) +console.log(`Setting up: ${earlyAccessCloningDir}`) +execSync( + `git clone --single-branch --branch ${earlyAccessBranch} ${earlyAccessFullRepo} ${earlyAccessRepoName}`, + { + cwd: earlyAccessCloningParentDir + } +) console.log(`Using early-access ${environment} branch: '${earlyAccessBranch}'`) -// Remove the .git dir -rimraf(`${earlyAccessContentDir}/.git`) +// Remove all existing early access directories from this repo +destinationDirNames.forEach(key => rimraf(destinationDirsMap[key])) -// Confirm the directory exists -fs.existsSync(earlyAccessContentDir) +// +// OLD STATE: +// +const dirName = 'content' + +// Remove the .git directory +rimraf(path.join(earlyAccessCloningDir, '.git')) + +// Move the directory from the cloned source to the destination +fs.renameSync( + earlyAccessCloningDir, + destinationDirsMap[dirName] +) + +// Confirm the newly moved directory exist +if (fs.existsSync(destinationDirsMap[dirName])) { + console.log(`Successfully moved early access directory '${dirName}' into this repo`) +} else { + throw new Error(`Failed to move early access directory '${dirName}'!`) +} +// END OLD STATE + +// +// FUTURE STATE: +// +// // Move the latest early access source directories into this repo +// destinationDirNames.forEach((dirName) => { +// const sourceDir = path.join(earlyAccessCloningDir, dirName) +// const destDir = destinationDirsMap[dirName] + +// // If the source directory doesn't exist, skip it +// if (!fs.existsSync(sourceDir)) { +// console.warn(`Early access directory '${dirName}' does not exist. Skipping...`) +// return +// } + +// // Move the directory from the cloned source to the destination +// fs.renameSync(sourceDir, destDir) + +// // Confirm the newly moved directory exist +// if (fs.existsSync(destDir)) { +// console.log(`Successfully moved early access directory '${dirName}' into this repo`) +// } else { +// throw new Error(`Failed to move early access directory '${dirName}'!`) +// } +// }) +// +// // Remove the source content again for good hygiene +// rimraf(earlyAccessCloningDir) +// END FUTURE STATE From 2c030a8967dbe3d48b3cda2982235ab46498722e Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Mon, 9 Nov 2020 19:44:10 -0600 Subject: [PATCH 046/105] Remove early-access from the sorted product ID list??? --- lib/all-products.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/all-products.js b/lib/all-products.js index 7dbe6a7a5e..e4b2dce447 100644 --- a/lib/all-products.js +++ b/lib/all-products.js @@ -19,15 +19,19 @@ const contentProductIds = fs.readdirSync(contentDir, { withFileTypes: true }) // require the content/ list to match the list in data/products.yml, // with the exception of content/early-access, which lives in a separate private repo -const publicContentProductIds = contentProductIds.filter(id => !id.startsWith('early-access')) +const publicContentProductIds = contentProductIds.filter(id => id !== 'early-access') assert(difference(sortedProductIds, publicContentProductIds).length === 0) assert(difference(publicContentProductIds, sortedProductIds).length === 0) const internalProducts = {} +// +// TODO: Should this removed now? Uncommenting it causes errors when trying to +// read the frontmatter since the TOC file doesn't exist +// // add optional early access content dir to sorted products list if present -const earlyAccessId = contentProductIds.find(id => id.startsWith('early-access')) -if (earlyAccessId) sortedProductIds.push(earlyAccessId) +// const earlyAccessId = contentProductIds.find(id => id === 'early-access') +// if (earlyAccessId) sortedProductIds.push(earlyAccessId) sortedProductIds.forEach(productId => { const relPath = productId From 4fac3decec3dcef5413199d8bdf8527685a09827 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Mon, 9 Nov 2020 23:04:24 -0600 Subject: [PATCH 047/105] Comment out premature tests --- tests/unit/early-access.js | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/tests/unit/early-access.js b/tests/unit/early-access.js index f347e018bb..925d6a3664 100644 --- a/tests/unit/early-access.js +++ b/tests/unit/early-access.js @@ -3,22 +3,23 @@ const path = require('path') const testViaActionsOnly = process.env.GITHUB_ACTIONS ? test : test.skip +// // TODO test ea-config +// -// TODO this should only run locally describe('cloning early-access', () => { - testViaActionsOnly('the assets directory exists', async () => { - const eaContentDir = path.join(process.cwd(), 'assets/early-access') - expect(fs.existsSync(eaContentDir)).toBe(true) - }) - testViaActionsOnly('the content directory exists', async () => { const eaContentDir = path.join(process.cwd(), 'content/early-access') expect(fs.existsSync(eaContentDir)).toBe(true) }) - testViaActionsOnly('the data directory exists', async () => { - const eaContentDir = path.join(process.cwd(), 'data/early-access') - expect(fs.existsSync(eaContentDir)).toBe(true) - }) + // testViaActionsOnly('the data directory exists', async () => { + // const eaContentDir = path.join(process.cwd(), 'data/early-access') + // expect(fs.existsSync(eaContentDir)).toBe(true) + // }) + + // testViaActionsOnly('the assets directory exists', async () => { + // const eaContentDir = path.join(process.cwd(), 'assets/early-access') + // expect(fs.existsSync(eaContentDir)).toBe(true) + // }) }) From 3e0d94ec91849b91de5ef3be8e7a7dc5b5a0e9e6 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Mon, 9 Nov 2020 23:05:42 -0600 Subject: [PATCH 048/105] Fix pathing on CrowdIn config tests --- tests/content/crowdin-config.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/content/crowdin-config.js b/tests/content/crowdin-config.js index b54168c969..220f4cfc35 100644 --- a/tests/content/crowdin-config.js +++ b/tests/content/crowdin-config.js @@ -17,8 +17,8 @@ describe('crowdin.yml config file', () => { }) test('ignores all Early Access paths', async () => { - expect(ignoredPagePaths).toContain('content/early-access') - expect(ignoredDataPaths).toContain('data/early-access') + expect(ignoredPagePaths).toContain('/content/early-access') + expect(ignoredDataPaths).toContain('/data/early-access') }) test('ignores all hidden pages', async () => { From bdb859f079b02852332af4bba99a08ebf4972848 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Mon, 9 Nov 2020 23:15:47 -0600 Subject: [PATCH 049/105] Be consistent about paths in CrowdIn config --- crowdin.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crowdin.yml b/crowdin.yml index 726820c67c..1856285323 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -10,13 +10,13 @@ files: - source: /data/**/*.md translation: /translations/%locale%/%original_path%/%original_file_name% ignore: [ - "data/README.md", - "data/reusables/README.md", - "data/variables/product.yml", - "data/variables/README.md", - "data/early-access", - "data/graphql", - "data/products.yml" + "/data/README.md", + "/data/reusables/README.md", + "/data/variables/product.yml", + "/data/variables/README.md", + "/data/early-access", + "/data/graphql", + "/data/products.yml" ] # These end up as env vars used by the GitHub Actions workflow From bc3131b15504ad129b08eb20f7c0fff4c12cd111 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Tue, 10 Nov 2020 14:36:34 -0600 Subject: [PATCH 050/105] Remove old '/early-access' redirect from API Previews page --- content/rest/overview/api-previews.md | 1 - translations/de-DE/content/rest/overview/api-previews.md | 1 - translations/es-XL/content/rest/overview/api-previews.md | 1 - translations/ja-JP/content/rest/overview/api-previews.md | 1 - translations/ko-KR/content/rest/overview/api-previews.md | 1 - translations/pt-BR/content/rest/overview/api-previews.md | 1 - translations/ru-RU/content/rest/overview/api-previews.md | 1 - translations/zh-CN/content/rest/overview/api-previews.md | 1 - 8 files changed, 8 deletions(-) diff --git a/content/rest/overview/api-previews.md b/content/rest/overview/api-previews.md index 9a4df5a0be..7e36f84f8f 100644 --- a/content/rest/overview/api-previews.md +++ b/content/rest/overview/api-previews.md @@ -2,7 +2,6 @@ title: API previews intro: You can use API previews to try out new features and provide feedback before these features become official. redirect_from: - - /early-access/ - /v3/previews versions: free-pro-team: '*' diff --git a/translations/de-DE/content/rest/overview/api-previews.md b/translations/de-DE/content/rest/overview/api-previews.md index 0dc9f428e8..96f3faacd9 100644 --- a/translations/de-DE/content/rest/overview/api-previews.md +++ b/translations/de-DE/content/rest/overview/api-previews.md @@ -2,7 +2,6 @@ title: API previews intro: You can use API previews to try out new features and provide feedback before these features become official. redirect_from: - - /early-access/ - /v3/previews versions: free-pro-team: '*' diff --git a/translations/es-XL/content/rest/overview/api-previews.md b/translations/es-XL/content/rest/overview/api-previews.md index a3658b02f9..2953fef157 100644 --- a/translations/es-XL/content/rest/overview/api-previews.md +++ b/translations/es-XL/content/rest/overview/api-previews.md @@ -2,7 +2,6 @@ title: Vistas previas de la API intro: Puedes utilizar las vistas previas de la API para probar características nuevas y proporcionar retroalimentación antes de que dichas características se hagan oficiales. redirect_from: - - /early-access/ - /v3/previews versions: free-pro-team: '*' diff --git a/translations/ja-JP/content/rest/overview/api-previews.md b/translations/ja-JP/content/rest/overview/api-previews.md index cf72f0f953..93d46d2017 100644 --- a/translations/ja-JP/content/rest/overview/api-previews.md +++ b/translations/ja-JP/content/rest/overview/api-previews.md @@ -2,7 +2,6 @@ title: API プレビュー intro: API プレビューを使用して新機能を試し、これらの機能が正式なものになる前にフィードバックを提供できます。 redirect_from: - - /early-access/ - /v3/previews versions: free-pro-team: '*' diff --git a/translations/ko-KR/content/rest/overview/api-previews.md b/translations/ko-KR/content/rest/overview/api-previews.md index 3030a8bd4c..352b690cbd 100644 --- a/translations/ko-KR/content/rest/overview/api-previews.md +++ b/translations/ko-KR/content/rest/overview/api-previews.md @@ -2,7 +2,6 @@ title: API previews intro: You can use API previews to try out new features and provide feedback before these features become official. redirect_from: - - /early-access/ - /v3/previews versions: free-pro-team: '*' diff --git a/translations/pt-BR/content/rest/overview/api-previews.md b/translations/pt-BR/content/rest/overview/api-previews.md index 56baef5a45..28c7e49920 100644 --- a/translations/pt-BR/content/rest/overview/api-previews.md +++ b/translations/pt-BR/content/rest/overview/api-previews.md @@ -2,7 +2,6 @@ title: Pré-visualizações da API intro: Você pode usar pré-visualizações da API para testar novos recursos e fornecer feedback antes que estes recursos se tornem oficiais. redirect_from: - - /early-access/ - /v3/previews versions: free-pro-team: '*' diff --git a/translations/ru-RU/content/rest/overview/api-previews.md b/translations/ru-RU/content/rest/overview/api-previews.md index 8dfaf3d7ee..062684a945 100644 --- a/translations/ru-RU/content/rest/overview/api-previews.md +++ b/translations/ru-RU/content/rest/overview/api-previews.md @@ -2,7 +2,6 @@ title: API previews intro: You can use API previews to try out new features and provide feedback before these features become official. redirect_from: - - /early-access/ - /v3/previews versions: free-pro-team: '*' diff --git a/translations/zh-CN/content/rest/overview/api-previews.md b/translations/zh-CN/content/rest/overview/api-previews.md index 53f75ff611..cca72dc736 100644 --- a/translations/zh-CN/content/rest/overview/api-previews.md +++ b/translations/zh-CN/content/rest/overview/api-previews.md @@ -2,7 +2,6 @@ title: API 预览 intro: 您可以使用 API 预览来试用新功能并在这些功能正式发布之前提供反馈。 redirect_from: - - /early-access/ - /v3/previews versions: free-pro-team: '*' From 10b9000ef233a8b3310581b756c74b2873ed8920 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Tue, 10 Nov 2020 14:37:03 -0600 Subject: [PATCH 051/105] Add early-access back in as a product --- lib/all-products.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/all-products.js b/lib/all-products.js index e4b2dce447..f6001f2044 100644 --- a/lib/all-products.js +++ b/lib/all-products.js @@ -25,13 +25,9 @@ assert(difference(publicContentProductIds, sortedProductIds).length === 0) const internalProducts = {} -// -// TODO: Should this removed now? Uncommenting it causes errors when trying to -// read the frontmatter since the TOC file doesn't exist -// // add optional early access content dir to sorted products list if present -// const earlyAccessId = contentProductIds.find(id => id === 'early-access') -// if (earlyAccessId) sortedProductIds.push(earlyAccessId) +const earlyAccessId = contentProductIds.find(id => id === 'early-access') +if (earlyAccessId) sortedProductIds.push(earlyAccessId) sortedProductIds.forEach(productId => { const relPath = productId From 6940bc3f6a50dbc2da996c79e8107dba58f0d511 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Tue, 10 Nov 2020 15:12:06 -0600 Subject: [PATCH 052/105] Sort hidden page listing by relativePath --- middleware/list-hidden-pages.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/middleware/list-hidden-pages.js b/middleware/list-hidden-pages.js index d82a3d7b71..abfaeba962 100644 --- a/middleware/list-hidden-pages.js +++ b/middleware/list-hidden-pages.js @@ -3,9 +3,11 @@ module.exports = async function listHidden (req, res, next) { return res.status(403).end() } - const hiddenPages = req.context.pages.filter(page => page.hidden) - let urls = [] + const hiddenPages = req.context.pages + .filter(page => page.hidden) + .sort((a, b) => b.relativePath.localeCompare(a.relativePath)) + let urls = [] hiddenPages.forEach(page => { const pageUrls = page.permalinks.map(permalink => permalink.href) urls = urls.concat(pageUrls) From b75cb104c24c31a377ef3d5446aee8d95f6aa623 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Tue, 10 Nov 2020 15:25:18 -0600 Subject: [PATCH 053/105] Make Early Access tests only run on the docs-internal repo --- tests/unit/early-access.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/unit/early-access.js b/tests/unit/early-access.js index 925d6a3664..82b5bc2804 100644 --- a/tests/unit/early-access.js +++ b/tests/unit/early-access.js @@ -1,7 +1,9 @@ const fs = require('fs') const path = require('path') -const testViaActionsOnly = process.env.GITHUB_ACTIONS ? test : test.skip +const { GITHUB_ACTIONS, GITHUB_REPOSITORY } = process.env +const runningActionsOnInternalRepo = GITHUB_ACTIONS === 'true' && GITHUB_REPOSITORY === 'github/docs-internal' +const testViaActionsOnly = runningActionsOnInternalRepo ? test : test.skip // // TODO test ea-config From dac5bc7867dfc0f334551f4e0e8a3f2d2b1474a8 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Tue, 10 Nov 2020 16:01:18 -0600 Subject: [PATCH 054/105] Don't re-run build for Actions in the internal repo --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3ef1b30ca9..29c4fcfacf 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -73,6 +73,7 @@ jobs: DOCUBOT_REPO_PAT: ${{ secrets.DOCUBOT_REPO_PAT }} - name: Run build script + if: ${{ github.repository != 'github/docs-internal' }} run: npm run build - name: Run tests From 5b6d704db71384a620a73dcaf1dbe260936f2f74 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Tue, 10 Nov 2020 16:01:35 -0600 Subject: [PATCH 055/105] Rename clone-early-access script --- package.json | 2 +- .../clone-for-build.js} | 66 ++++++------------- 2 files changed, 21 insertions(+), 47 deletions(-) rename script/{clone-early-access.js => early-access/clone-for-build.js} (64%) diff --git a/package.json b/package.json index dd636e3cb9..382979d614 100644 --- a/package.json +++ b/package.json @@ -139,7 +139,7 @@ "prevent-pushes-to-main": "node script/prevent-pushes-to-main.js", "pa11y-ci": "pa11y-ci", "pa11y-test": "start-server-and-test browser-test-server 4001 pa11y-ci", - "heroku-postbuild": "node script/clone-early-access.js && npm run build" + "heroku-postbuild": "node script/early-access/clone-for-build.js && npm run build" }, "engines": { "node": "12 - 14" diff --git a/script/clone-early-access.js b/script/early-access/clone-for-build.js similarity index 64% rename from script/clone-early-access.js rename to script/early-access/clone-for-build.js index 3592899029..353c2a3ad1 100755 --- a/script/clone-early-access.js +++ b/script/early-access/clone-for-build.js @@ -75,53 +75,27 @@ console.log(`Using early-access ${environment} branch: '${earlyAccessBranch}'`) // Remove all existing early access directories from this repo destinationDirNames.forEach(key => rimraf(destinationDirsMap[key])) -// -// OLD STATE: -// -const dirName = 'content' +// Move the latest early access source directories into this repo +destinationDirNames.forEach((dirName) => { + const sourceDir = path.join(earlyAccessCloningDir, dirName) + const destDir = destinationDirsMap[dirName] -// Remove the .git directory -rimraf(path.join(earlyAccessCloningDir, '.git')) + // If the source directory doesn't exist, skip it + if (!fs.existsSync(sourceDir)) { + console.warn(`Early access directory '${dirName}' does not exist. Skipping...`) + return + } -// Move the directory from the cloned source to the destination -fs.renameSync( - earlyAccessCloningDir, - destinationDirsMap[dirName] -) + // Move the directory from the cloned source to the destination + fs.renameSync(sourceDir, destDir) -// Confirm the newly moved directory exist -if (fs.existsSync(destinationDirsMap[dirName])) { - console.log(`Successfully moved early access directory '${dirName}' into this repo`) -} else { - throw new Error(`Failed to move early access directory '${dirName}'!`) -} -// END OLD STATE + // Confirm the newly moved directory exist + if (fs.existsSync(destDir)) { + console.log(`Successfully moved early access directory '${dirName}' into this repo`) + } else { + throw new Error(`Failed to move early access directory '${dirName}'!`) + } +}) -// -// FUTURE STATE: -// -// // Move the latest early access source directories into this repo -// destinationDirNames.forEach((dirName) => { -// const sourceDir = path.join(earlyAccessCloningDir, dirName) -// const destDir = destinationDirsMap[dirName] - -// // If the source directory doesn't exist, skip it -// if (!fs.existsSync(sourceDir)) { -// console.warn(`Early access directory '${dirName}' does not exist. Skipping...`) -// return -// } - -// // Move the directory from the cloned source to the destination -// fs.renameSync(sourceDir, destDir) - -// // Confirm the newly moved directory exist -// if (fs.existsSync(destDir)) { -// console.log(`Successfully moved early access directory '${dirName}' into this repo`) -// } else { -// throw new Error(`Failed to move early access directory '${dirName}'!`) -// } -// }) -// -// // Remove the source content again for good hygiene -// rimraf(earlyAccessCloningDir) -// END FUTURE STATE +// Remove the source content again for good hygiene +rimraf(earlyAccessCloningDir) From 050ff6dcbc30237e4143f78e50432460b08b88e3 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Tue, 10 Nov 2020 16:46:10 -0600 Subject: [PATCH 056/105] Update for early-access under assets/images/ --- .gitignore | 2 +- script/early-access/clone-for-build.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 9c06d3b323..a561426c08 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ /node_modules/ npm-debug.log coverage/ -/assets/early-access/ +/assets/images/early-access/ /content/early-access/ /data/early-access/ diff --git a/script/early-access/clone-for-build.js b/script/early-access/clone-for-build.js index 353c2a3ad1..37d5311b8c 100755 --- a/script/early-access/clone-for-build.js +++ b/script/early-access/clone-for-build.js @@ -35,7 +35,7 @@ const earlyAccessFullRepo = `https://${DOCUBOT_REPO_PAT}@github.com/${earlyAcces const earlyAccessCloningParentDir = os.tmpdir() const earlyAccessCloningDir = path.join(earlyAccessCloningParentDir, earlyAccessRepoName) -const destinationDirNames = ['content', 'data', 'assets'] +const destinationDirNames = ['content', 'data', 'assets/images'] const destinationDirsMap = destinationDirNames .reduce( (map, dirName) => { From 1b3af6e8c5954201094d1c526634f3d07bd494c7 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Wed, 11 Nov 2020 00:28:12 -0600 Subject: [PATCH 057/105] Add Early Access context via middleware to show list of pages --- middleware/early-access-context.js | 26 ++++++++++++++++++++++++++ middleware/index.js | 2 +- middleware/list-hidden-pages.js | 23 ----------------------- 3 files changed, 27 insertions(+), 24 deletions(-) create mode 100644 middleware/early-access-context.js delete mode 100644 middleware/list-hidden-pages.js diff --git a/middleware/early-access-context.js b/middleware/early-access-context.js new file mode 100644 index 0000000000..37a5a09e7a --- /dev/null +++ b/middleware/early-access-context.js @@ -0,0 +1,26 @@ +module.exports = function earlyAccessContext (req, res, next) { + if (process.env.NODE_ENV === 'production') { + return res.status(403).end() + } + + const earlyAccessPages = req.context.pages + .filter(page => page.hidden) + .sort((a, b) => b.relativePath.localeCompare(a.relativePath)) + + let urls = [] + earlyAccessPages.forEach(page => { + const pageUrls = page.permalinks.map(permalink => permalink.href) + urls = urls.concat(pageUrls) + }) + + const earlyAccessPageLinks = ` +${urls.map(url => `- [${url}](${url})`).join('\n')} +` + + console.log({ earlyAccessPageLinks }) + + // Add to the rendering context + req.context.earlyAccessPageLinks = earlyAccessPageLinks + + return next() +} diff --git a/middleware/index.js b/middleware/index.js index 3d8fee3e95..fb9160d303 100644 --- a/middleware/index.js +++ b/middleware/index.js @@ -38,6 +38,7 @@ module.exports = function (app) { app.use(require('./categories-for-support-team')) app.use(require('./detect-language')) app.use(asyncMiddleware(require('./context'))) + app.use(/(\/.*)?\/early-access$/, require('./early-access-context')) app.use('/csrf', require('./csrf-route')) app.use(require('./find-page')) app.use(require('./notices')) @@ -53,7 +54,6 @@ module.exports = function (app) { app.use(require('./contextualizers/webhooks')) app.use(require('./disable-caching-on-safari')) app.get('/_500', asyncMiddleware(require('./trigger-error'))) - app.get('/hidden', require('./list-hidden-pages')) app.use(require('./breadcrumbs')) app.use(require('./featured-links')) app.get('/*', asyncMiddleware(require('./render-page'))) diff --git a/middleware/list-hidden-pages.js b/middleware/list-hidden-pages.js deleted file mode 100644 index abfaeba962..0000000000 --- a/middleware/list-hidden-pages.js +++ /dev/null @@ -1,23 +0,0 @@ -module.exports = async function listHidden (req, res, next) { - if (process.env.NODE_ENV === 'production') { - return res.status(403).end() - } - - const hiddenPages = req.context.pages - .filter(page => page.hidden) - .sort((a, b) => b.relativePath.localeCompare(a.relativePath)) - - let urls = [] - hiddenPages.forEach(page => { - const pageUrls = page.permalinks.map(permalink => permalink.href) - urls = urls.concat(pageUrls) - }) - - const output = ` - - ` - - return res.send(output) -} From 287ee2050cdb0285a8d9a9e992335244c5af58da Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Wed, 11 Nov 2020 00:30:40 -0600 Subject: [PATCH 058/105] EA context middleware cleanup --- middleware/early-access-context.js | 2 -- middleware/index.js | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/middleware/early-access-context.js b/middleware/early-access-context.js index 37a5a09e7a..f5f81f7410 100644 --- a/middleware/early-access-context.js +++ b/middleware/early-access-context.js @@ -17,8 +17,6 @@ module.exports = function earlyAccessContext (req, res, next) { ${urls.map(url => `- [${url}](${url})`).join('\n')} ` - console.log({ earlyAccessPageLinks }) - // Add to the rendering context req.context.earlyAccessPageLinks = earlyAccessPageLinks diff --git a/middleware/index.js b/middleware/index.js index fb9160d303..5d12cfd8b0 100644 --- a/middleware/index.js +++ b/middleware/index.js @@ -38,7 +38,6 @@ module.exports = function (app) { app.use(require('./categories-for-support-team')) app.use(require('./detect-language')) app.use(asyncMiddleware(require('./context'))) - app.use(/(\/.*)?\/early-access$/, require('./early-access-context')) app.use('/csrf', require('./csrf-route')) app.use(require('./find-page')) app.use(require('./notices')) @@ -56,6 +55,7 @@ module.exports = function (app) { app.get('/_500', asyncMiddleware(require('./trigger-error'))) app.use(require('./breadcrumbs')) app.use(require('./featured-links')) + app.use(/(\/.*)?\/early-access$/, require('./early-access-context')) app.get('/*', asyncMiddleware(require('./render-page'))) app.use(require('./handle-errors')) } From fc917569b4028a86c44fdcbc7eb1491a8267b99d Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Wed, 11 Nov 2020 00:58:16 -0600 Subject: [PATCH 059/105] Add script to symlink to local early-access repo --- .gitignore | 6 +- .../early-access/symlink-from-local-repo.js | 78 +++++++++++++++++++ 2 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 script/early-access/symlink-from-local-repo.js diff --git a/.gitignore b/.gitignore index a561426c08..b8d20835cc 100644 --- a/.gitignore +++ b/.gitignore @@ -4,9 +4,9 @@ /node_modules/ npm-debug.log coverage/ -/assets/images/early-access/ -/content/early-access/ -/data/early-access/ +/assets/images/early-access +/content/early-access +/data/early-access # blc: broken link checker blc_output.log diff --git a/script/early-access/symlink-from-local-repo.js b/script/early-access/symlink-from-local-repo.js new file mode 100644 index 0000000000..145ea46c34 --- /dev/null +++ b/script/early-access/symlink-from-local-repo.js @@ -0,0 +1,78 @@ +#!/usr/bin/env node + +// [start-readme] +// +// This script is run on a writer's machine while developing Early Access content locally. +// You must pass the script the location of your local copy of +// the `github/docs-early-access` git repo as the first argument. +// +// [end-readme] + +const { execSync } = require('child_process') +const rimraf = require('rimraf').sync +const fs = require('fs') +const path = require('path') + +// Early Access details +const earlyAccessDirName = 'early-access' + +if (!process.argv[2]) { + throw new Error('Must provide the location of your local `docs-early-access` repo directory') +} + +const earlyAccessLocalRepoDir = path.resolve(process.cwd(), process.argv[2]) + +let dirStats +try { + dirStats = fs.statSync(earlyAccessLocalRepoDir) +} catch (err) { + dirStats = null +} + +if (!dirStats) { + throw new Error('The local `docs-early-access` repo directory does not exist:', earlyAccessLocalRepoDir) +} +if (dirStats && !dirStats.isDirectory()) { + throw new Error('A non-directory entry exists at the local `docs-early-access` repo directory location:', earlyAccessLocalRepoDir) +} + +const destinationDirNames = ['content', 'data', 'assets/images'] +const destinationDirsMap = destinationDirNames + .reduce( + (map, dirName) => { + map[dirName] = path.join(process.cwd(), dirName, earlyAccessDirName) + return map + }, + {} + ) + +// Remove all existing early access directories from this repo +destinationDirNames.forEach(key => rimraf(destinationDirsMap[key])) + +// Move the latest early access source directories into this repo +destinationDirNames.forEach((dirName) => { + const sourceDir = path.join(earlyAccessLocalRepoDir, dirName) + const destDir = destinationDirsMap[dirName] + + // If the source directory doesn't exist, skip it + if (!fs.existsSync(sourceDir)) { + console.warn(`Early access directory '${dirName}' does not exist. Skipping...`) + return + } + + // Create a symbolic link to the directory + fs.symlinkSync(sourceDir, destDir, 'junction') + + // Confirm the newly moved directory exist + if (!fs.existsSync(destDir)) { + throw new Error(`Failed to symlink early access directory '${dirName}'!`) + } + if (!fs.lstatSync(destDir).isSymbolicLink()) { + throw new Error(`The early access directory '${dirName}' entry is not a symbolic link!`) + } + if (!fs.statSync(destDir).isDirectory()) { + throw new Error(`The early access directory '${dirName}' entry's symbolic link does not refer to a directory!`) + } + + console.log(`Successfully symlinked early access directory '${dirName}' into this repo`) +}) From af3a7f37bf41a09dda0a6915c043cd21d0e3f8a8 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Fri, 13 Nov 2020 10:30:43 -0600 Subject: [PATCH 060/105] Remove unused require in script --- script/early-access/symlink-from-local-repo.js | 1 - 1 file changed, 1 deletion(-) diff --git a/script/early-access/symlink-from-local-repo.js b/script/early-access/symlink-from-local-repo.js index 145ea46c34..5acc8a44e5 100644 --- a/script/early-access/symlink-from-local-repo.js +++ b/script/early-access/symlink-from-local-repo.js @@ -8,7 +8,6 @@ // // [end-readme] -const { execSync } = require('child_process') const rimraf = require('rimraf').sync const fs = require('fs') const path = require('path') From 272c4722d2e142984a524dec07d3bde985e84b99 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Fri, 13 Nov 2020 13:40:25 -0600 Subject: [PATCH 061/105] Add new content-linting tests for Early Access --- tests/content/lint-files.js | 134 ++++++++++++++++++++++++++++++++++-- 1 file changed, 130 insertions(+), 4 deletions(-) diff --git a/tests/content/lint-files.js b/tests/content/lint-files.js index f7e0a23462..9bc28df59c 100644 --- a/tests/content/lint-files.js +++ b/tests/content/lint-files.js @@ -65,6 +65,18 @@ const languageLinkRegex = new RegExp(`(?=^|[^\\]]\\s*)\\[[^\\]]+\\](?::\\n?[ \\t // - [link text](/github/site-policy/enterprise/2.2/admin/blah) const versionLinkRegEx = /(?=^|[^\]]\s*)\[[^\]]+\](?::\n?[ \t]+|\s*\()(?:(?:https?:\/\/(?:help|docs|developer)\.github\.com)?\/enterprise\/\d+(\.\d+)+(?:\/[^)\s]*)?)(?:\)|\s+|$)/gm +// Things matched by this RegExp: +// - [link text](/early-access/github/blah) +// - [link text] (https://docs.github.com/early-access/github/blah) +// - [link-definition-ref]: http://help.github.com/early-access/github/blah +// - etc. +// +// Things intentionally NOT matched by this RegExp: +// - [Node.js](https://nodejs.org/early-access/) +// - etc. +// +const earlyAccessLinkRegex = /(?=^|[^\]]\s*)\[[^\]]+\](?::\n?[ \t]+|\s*\()(?:(?:https?:\/\/(?:help|docs|developer)\.github\.com)?\/early-access(?:\/[^)\s]*)?)(?:\)|\s+|$)/gm + // - [link text](https://docs.github.com/github/blah) // - [link text] (https://help.github.com/github/blah) // - [link-definition-ref]: http://developer.github.com/v3/ @@ -78,6 +90,33 @@ const versionLinkRegEx = /(?=^|[^\]]\s*)\[[^\]]+\](?::\n?[ \t]+|\s*\()(?:(?:http // const domainLinkRegex = /(?=^|[^\]]\s*)\[[^\]]+\](?::\n?[ \t]+|\s*\()(?:https?:)?\/\/(?:help|docs|developer)\.github\.com(?!\/changes\/)[^)\s]*(?:\)|\s+|$)/gm +// Things matched by this RegExp: +// - ![image text](/assets/images/early-access/github/blah.gif) +// - ![image text] (https://docs.github.com/assets/images/early-access/github/blah.gif) +// - [image-definition-ref]: http://help.github.com/assets/images/early-access/github/blah.gif +// - [link text](/assets/images/early-access/github/blah.gif) +// - etc. +// +// Things intentionally NOT matched by this RegExp: +// - [Node.js](https://nodejs.org/assets/images/early-access/blah.gif) +// - etc. +// +const earlyAccessImageRegex = /(?=^|[^\]]\s*)\[[^\]]+\](?::\n?[ \t]+|\s*\()(?:(?:https?:\/\/(?:help|docs|developer)\.github\.com)?\/assets\/images\/early-access(?:\/[^)\s]*)?)(?:\)|\s+|$)/gm + +// Things matched by this RegExp: +// - ![image text](/assets/early-access/images/github/blah.gif) +// - ![image text] (https://docs.github.com/images/early-access/github/blah.gif) +// - [image-definition-ref]: http://help.github.com/assets/early-access/github/blah.gif +// - [link text](/early-access/assets/images/github/blah.gif) +// - [link text](/early-access/images/github/blah.gif) +// - etc. +// +// Things intentionally NOT matched by this RegExp: +// - [Node.js](https://nodejs.org/assets/early-access/images/blah.gif) +// - etc. +// +const badEarlyAccessImageRegex = /(?=^|[^\]]\s*)\[[^\]]+\](?::\n?[ \t]+|\s*\()(?:(?:https?:\/\/(?:help|docs|developer)\.github\.com)?\/(?:(?:assets|images)\/early-access|early-access\/(?:assets|images))(?:\/[^)\s]*)?)(?:\)|\s+|$)/gm + // {{ site.data.example.pizza }} const oldVariableRegex = /{{\s?site\.data\..*}}/g @@ -97,6 +136,9 @@ const relativeArticleLinkErrorText = 'Found unexpected relative article links:' const languageLinkErrorText = 'Found article links with hard-coded language codes:' const versionLinkErrorText = 'Found article links with hard-coded version numbers:' const domainLinkErrorText = 'Found article links with hard-coded domain names:' +const earlyAccessLinkErrorText = 'Found article links leaking Early Access docs:' +const earlyAccessImageErrorText = 'Found article images/links leaking Early Access images:' +const badEarlyAccessImageErrorText = 'Found article images/links leaking incorrect Early Access images:' const oldVariableErrorText = 'Found article uses old {{ site.data... }} syntax. Use {% data example.data.string %} instead!' const oldOcticonErrorText = 'Found octicon variables with the old {{ octicon-name }} syntax. Use {% octicon "name" %} instead!' const oldExtendedMarkdownErrorText = 'Found extended markdown tags with the old {{#note}} syntax. Use {% note %}/{% endnote %} instead!' @@ -120,13 +162,19 @@ describe('lint-files', () => { describe.each([...contentMarkdownTuples, ...reusableMarkdownTuples])( 'in "%s"', (markdownRelPath, markdownAbsPath) => { - let content + let content, isHidden, isEarlyAccess beforeAll(async () => { const fileContents = await fs.promises.readFile(markdownAbsPath, 'utf8') - const { content: bodyContent } = matter(fileContents) + const { data, content: bodyContent } = matter(fileContents) content = bodyContent + isHidden = data.hidden === true + isEarlyAccess = markdownRelPath.split('/').includes('early-access') + }) + + test('hidden docs must be Early Access', async () => { + expect(isHidden).toBe(isEarlyAccess) }) test('relative URLs must start with "/"', async () => { @@ -208,6 +256,32 @@ describe('lint-files', () => { expect(matches.length, errorMessage).toBe(0) }) + test('must not leak Early Access doc URLs', async () => { + // Only execute for docs that are NOT Early Access + if (!isEarlyAccess) { + const matches = (content.match(earlyAccessLinkRegex) || []) + const errorMessage = formatLinkError(earlyAccessLinkErrorText, matches) + expect(matches.length, errorMessage).toBe(0) + } + }) + + test('must not leak Early Access image URLs', async () => { + // Only execute for docs that are NOT Early Access + if (!isEarlyAccess) { + const matches = (content.match(earlyAccessImageRegex) || []) + const errorMessage = formatLinkError(earlyAccessImageErrorText, matches) + expect(matches.length, errorMessage).toBe(0) + } + }) + + test('must have correctly formatted Early Access image URLs', async () => { + // Execute for ALL docs (not just Early Access) to ensure non-EA docs + // are not leaking incorrectly formatted EA image URLs + const matches = (content.match(badEarlyAccessImageRegex) || []) + const errorMessage = formatLinkError(badEarlyAccessImageErrorText, matches) + expect(matches.length, errorMessage).toBe(0) + }) + test('does not use old site.data variable syntax', async () => { const matches = (content.match(oldVariableRegex) || []) const matchesWithExample = matches.map(match => { @@ -250,17 +324,19 @@ describe('lint-files', () => { } const variableYamlAbsPaths = walk(variablesDir, yamlWalkOptions).sort() - const variableYamlRelPaths = variableYamlAbsPaths.map(p => path.relative(rootDir, p)) + const variableYamlRelPaths = variableYamlAbsPaths.map(p => slash(path.relative(rootDir, p))) const variableYamlTuples = zip(variableYamlRelPaths, variableYamlAbsPaths) describe.each(variableYamlTuples)( 'in "%s"', (yamlRelPath, yamlAbsPath) => { - let dictionary + let dictionary, isEarlyAccess beforeAll(async () => { const fileContents = await fs.promises.readFile(yamlAbsPath, 'utf8') dictionary = yaml.safeLoad(fileContents, { filename: yamlRelPath }) + + isEarlyAccess = yamlRelPath.split('/').includes('early-access') }) test('relative URLs must start with "/"', async () => { @@ -319,6 +395,56 @@ describe('lint-files', () => { expect(matches.length, errorMessage).toBe(0) }) + test('must not leak Early Access doc URLs', async () => { + // Only execute for docs that are NOT Early Access + if (!isEarlyAccess) { + const matches = [] + + for (const [key, content] of Object.entries(dictionary)) { + const valMatches = (content.match(earlyAccessLinkRegex) || []) + if (valMatches.length > 0) { + matches.push(...valMatches.map((match) => `Key "${key}": ${match}`)) + } + } + + const errorMessage = formatLinkError(earlyAccessLinkErrorText, matches) + expect(matches.length, errorMessage).toBe(0) + } + }) + + test('must not leak Early Access image URLs', async () => { + // Only execute for docs that are NOT Early Access + if (!isEarlyAccess) { + const matches = [] + + for (const [key, content] of Object.entries(dictionary)) { + const valMatches = (content.match(earlyAccessImageRegex) || []) + if (valMatches.length > 0) { + matches.push(...valMatches.map((match) => `Key "${key}": ${match}`)) + } + } + + const errorMessage = formatLinkError(earlyAccessImageErrorText, matches) + expect(matches.length, errorMessage).toBe(0) + } + }) + + test('must have correctly formatted Early Access image URLs', async () => { + // Execute for ALL docs (not just Early Access) to ensure non-EA docs + // are not leaking incorrectly formatted EA image URLs + const matches = [] + + for (const [key, content] of Object.entries(dictionary)) { + const valMatches = (content.match(badEarlyAccessImageRegex) || []) + if (valMatches.length > 0) { + matches.push(...valMatches.map((match) => `Key "${key}": ${match}`)) + } + } + + const errorMessage = formatLinkError(badEarlyAccessImageErrorText, matches) + expect(matches.length, errorMessage).toBe(0) + }) + test('does not use old site.data variable syntax', async () => { const matches = [] From dc36bd88d148f664773048892850141faae0351b Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Fri, 13 Nov 2020 13:40:58 -0600 Subject: [PATCH 062/105] Change one old API Previews link to use a different redirect URL without 'early-access' in it --- content/rest/overview/api-previews.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/rest/overview/api-previews.md b/content/rest/overview/api-previews.md index 7e36f84f8f..2023f237fd 100644 --- a/content/rest/overview/api-previews.md +++ b/content/rest/overview/api-previews.md @@ -59,7 +59,7 @@ Create, list, update, and delete environments for pre-receive hooks. {% if enterpriseServerVersions contains currentVersion and currentVersion ver_lt "enterprise-server@2.22" %} ### Integrations -Manage [integrations](/early-access/integrations/) through the API. +Manage [integrations](/v3/integrations) through the API. **Custom media type:** `machine-man-preview` **Announced:** [2016-09-14](https://developer.github.com/changes/2016-09-14-Integrations-Early-Access/) From 53910e2b9dc64a07037ad3295b1406e08e0a05e7 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Fri, 13 Nov 2020 15:27:27 -0600 Subject: [PATCH 063/105] Changes to handle a symlink Early Access environment --- lib/algolia/find-indexable-pages.js | 4 ++-- lib/all-products.js | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/algolia/find-indexable-pages.js b/lib/algolia/find-indexable-pages.js index d3d6be80dd..4d897e0100 100644 --- a/lib/algolia/find-indexable-pages.js +++ b/lib/algolia/find-indexable-pages.js @@ -3,10 +3,10 @@ const loadPages = require('../pages') module.exports = async function findIndexablePages () { const allPages = await loadPages() const indexablePages = allPages - // exclude pages that are part of WIP or hidden products - .filter(page => !page.parentProduct || !page.parentProduct.wip || page.parentProduct.hidden) // exclude hidden pages .filter(page => !page.hidden) + // exclude pages that are part of WIP or hidden products + .filter(page => !page.parentProduct || !page.parentProduct.wip || page.parentProduct.hidden) // exclude index homepages .filter(page => !page.relativePath.endsWith('index.md')) diff --git a/lib/all-products.js b/lib/all-products.js index f6001f2044..f65a8a11be 100644 --- a/lib/all-products.js +++ b/lib/all-products.js @@ -14,6 +14,17 @@ const productsYml = yaml.load(fs.readFileSync(productsFile, 'utf8')) const sortedProductIds = productsYml.productsInOrder const contentProductIds = fs.readdirSync(contentDir, { withFileTypes: true }) + .map(entry => { + // `fs.readdir` provides file entries based on `fs.lstat`, which doesn't + // resolve symbolic links to their target file/directory. We need to take + // an extra step here to resolve the Early Access symlinked directory. + const { name } = entry + if (entry.isSymbolicLink()) { + entry = fs.statSync(path.join(contentDir, entry.name)) + entry.name = name + } + return entry + }) .filter(entry => entry.isDirectory()) .map(entry => entry.name) From 1a732eae5e19a4ded730cf43b2ab6fb971dbebef Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Fri, 13 Nov 2020 15:29:20 -0600 Subject: [PATCH 064/105] Let's 404 the /early-access page outside of local rather than 403-ing --- middleware/early-access-context.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/middleware/early-access-context.js b/middleware/early-access-context.js index f5f81f7410..fff0f76012 100644 --- a/middleware/early-access-context.js +++ b/middleware/early-access-context.js @@ -1,6 +1,6 @@ module.exports = function earlyAccessContext (req, res, next) { if (process.env.NODE_ENV === 'production') { - return res.status(403).end() + return res.status(404).end() } const earlyAccessPages = req.context.pages From 510a0e38717d47b300b4f36a2e4efec4452716ef Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Fri, 13 Nov 2020 15:48:10 -0600 Subject: [PATCH 065/105] Add test to verify Early Access does not show up as a product in the sidebar --- tests/rendering/sidebar.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/rendering/sidebar.js b/tests/rendering/sidebar.js index 34733ae803..809678a4e9 100644 --- a/tests/rendering/sidebar.js +++ b/tests/rendering/sidebar.js @@ -36,4 +36,9 @@ describe('sidebar', () => { expect($('.sidebar .is-current-page').length).toBe(1) expect($('.sidebar .is-current-page a').attr('href')).toContain(url) }) + + test('does not display Early Access as a product', async () => { + expect($homePage('.sidebar li.sidebar-product[title*="Early"]').length).toBe(0) + expect($homePage('.sidebar li.sidebar-product[title*="early"]').length).toBe(0) + }) }) From bfb4a0def210a9c66708dc3775ccccdcde4a4028 Mon Sep 17 00:00:00 2001 From: Kevin Heis Date: Mon, 16 Nov 2020 10:10:05 -0800 Subject: [PATCH 066/105] Update package-lock.json --- package-lock.json | 1136 +++++++++++++++------------------------------ 1 file changed, 384 insertions(+), 752 deletions(-) diff --git a/package-lock.json b/package-lock.json index a38f5bbc66..e5efb067a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", - "dev": true, "requires": { "@babel/highlight": "^7.0.0" } @@ -333,7 +332,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", - "dev": true, "requires": { "chalk": "^2.0.0", "esutils": "^2.0.2", @@ -344,7 +342,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -353,7 +350,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -364,7 +360,6 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -1156,9 +1151,9 @@ } }, "@github/rest-api-operations": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/@github/rest-api-operations/-/rest-api-operations-3.6.2.tgz", - "integrity": "sha512-SgJY+Or4AFwfcrkzrgIY+HB1Q9jZQZFtlE+ThzY21EoX3/wACOjYIogWwB2qqB2lggr1+UOv1Mm/BaXBLeoF0w==" + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@github/rest-api-operations/-/rest-api-operations-3.7.1.tgz", + "integrity": "sha512-ppNyeJAOgCpMqmkBBbSwj2JPfzfpN9ST4xe1G3sLl9OsYSasHTb39s1ZiHAIdIaVAtyeRnGL/3wp8dUqrX6G4A==" }, "@hapi/address": { "version": "2.1.4", @@ -2718,8 +2713,7 @@ "@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", - "dev": true + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" }, "@types/events": { "version": "3.0.0", @@ -2816,6 +2810,11 @@ "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==" }, + "@types/minimist": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz", + "integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==" + }, "@types/node": { "version": "14.10.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.10.0.tgz", @@ -2824,8 +2823,7 @@ "@types/normalize-package-data": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", - "dev": true + "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==" }, "@types/parse-json": { "version": "4.0.0", @@ -3057,6 +3055,14 @@ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha1-+PLIh60Qv2f2NPAFtph/7TF5qsg=" }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, "accepts": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", @@ -3169,7 +3175,7 @@ }, "agentkeepalive": { "version": "2.2.0", - "resolved": "http://registry.npmjs.org/agentkeepalive/-/agentkeepalive-2.2.0.tgz", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-2.2.0.tgz", "integrity": "sha1-xdG9SxKQCPEWPyNvhuX66iAm4u8=" }, "aggregate-error": { @@ -3243,7 +3249,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", - "dev": true, "requires": { "string-width": "^3.0.0" }, @@ -3251,26 +3256,22 @@ "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" }, "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" }, "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, "requires": { "emoji-regex": "^7.0.1", "is-fullwidth-code-point": "^2.0.0", @@ -3281,7 +3282,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, "requires": { "ansi-regex": "^4.1.0" } @@ -3329,7 +3329,7 @@ "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "requires": { "sprintf-js": "~1.0.2" } @@ -3398,6 +3398,11 @@ "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=" + }, "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", @@ -3891,42 +3896,6 @@ "tryer": "^1.0.0" } }, - "bhttp": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/bhttp/-/bhttp-1.2.6.tgz", - "integrity": "sha512-c0ar+LpFYTQMU3VHfdG5+mkslAZ0zHWL8qEzq90Ko/N4e2UQ9Mw55uGbO+TmphkEX6pgyx0mjFVoBv8g8eDS2g==", - "dev": true, - "requires": { - "bluebird": "^2.8.2", - "concat-stream": "^1.4.7", - "debug": "^2.1.1", - "dev-null": "^0.1.1", - "errors": "^0.2.0", - "extend": "^2.0.0", - "form-data2": "^1.0.0", - "form-fix-array": "^1.0.0", - "lodash.clonedeep": "^4.5.0", - "lodash.merge": "^4.6.2", - "stream-length": "^1.0.2", - "through2-sink": "^1.0.0", - "through2-spy": "^1.2.0", - "tough-cookie": "^2.3.1" - }, - "dependencies": { - "bluebird": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", - "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=", - "dev": true - }, - "extend": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-2.0.2.tgz", - "integrity": "sha512-AgFD4VU+lVLP6vjnlNfF7OeInLTyeyckCNPEsuxz1vi786UuK/nk6ynPuhn/h+Ju9++TQyr5EpLRI14fc1QtTQ==", - "dev": true - } - } - }, "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -4006,7 +3975,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", - "dev": true, "requires": { "ansi-align": "^3.0.0", "camelcase": "^5.3.1", @@ -4022,7 +3990,6 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, "requires": { "@types/color-name": "^1.1.1", "color-convert": "^2.0.1" @@ -4032,7 +3999,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -4042,7 +4008,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "requires": { "color-name": "~1.1.4" } @@ -4050,20 +4015,17 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "supports-color": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -4071,8 +4033,7 @@ "type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" } } }, @@ -4114,7 +4075,7 @@ }, "brfs": { "version": "1.6.1", - "resolved": "http://registry.npmjs.org/brfs/-/brfs-1.6.1.tgz", + "resolved": "https://registry.npmjs.org/brfs/-/brfs-1.6.1.tgz", "integrity": "sha512-OfZpABRQQf+Xsmju8XE9bDjs+uU4vLREGolP7bDgcpsI17QREyZ4Bl+2KLxxx1kCgA0fAIhKQBaBYh+PEcCqYQ==", "requires": { "quote-stream": "^1.0.1", @@ -4123,51 +4084,6 @@ "through2": "^2.0.0" } }, - "broken-link-checker": { - "version": "0.7.8", - "resolved": "https://registry.npmjs.org/broken-link-checker/-/broken-link-checker-0.7.8.tgz", - "integrity": "sha512-/zH4/nLMNKDeDH5nVuf/R6WYd0Yjnar1NpcdAO2+VlwjGKzJa6y42C03UO+imBSHwe6BefSkVi82fImE2Rb7yg==", - "dev": true, - "requires": { - "bhttp": "^1.2.1", - "calmcard": "~0.1.1", - "chalk": "^1.1.3", - "char-spinner": "^1.0.1", - "condense-whitespace": "^1.0.0", - "default-user-agent": "^1.0.0", - "errno": "~0.1.4", - "extend": "^3.0.0", - "http-equiv-refresh": "^1.0.0", - "humanize-duration": "^3.9.1", - "is-stream": "^1.0.1", - "is-string": "^1.0.4", - "limited-request-queue": "^2.0.0", - "link-types": "^1.1.0", - "maybe-callback": "^2.1.0", - "nopter": "~0.3.0", - "parse5": "^3.0.2", - "robot-directives": "~0.3.0", - "robots-txt-guard": "~0.1.0", - "robots-txt-parse": "~0.0.4", - "urlcache": "~0.7.0", - "urlobj": "0.0.11" - }, - "dependencies": { - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - } - } - }, "brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", @@ -4193,7 +4109,7 @@ }, "browserify-aes": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "requires": { "buffer-xor": "^1.0.3", @@ -4227,7 +4143,7 @@ }, "browserify-rsa": { "version": "4.0.1", - "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "requires": { "bn.js": "^4.1.0", @@ -4513,35 +4429,12 @@ } } }, - "caller-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", - "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", - "dev": true, - "requires": { - "callsites": "^0.2.0" - }, - "dependencies": { - "callsites": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", - "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", - "dev": true - } - } - }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, - "calmcard": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/calmcard/-/calmcard-0.1.1.tgz", - "integrity": "sha1-NawrZkkrDtOa0GqJOg/25hEk5Ek=", - "dev": true - }, "camel-case": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", @@ -4556,6 +4449,16 @@ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" }, + "camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "requires": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + } + }, "camelize": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", @@ -4590,7 +4493,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -4600,7 +4502,6 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, "requires": { "@types/color-name": "^1.1.1", "color-convert": "^2.0.1" @@ -4610,7 +4511,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "requires": { "color-name": "~1.1.4" } @@ -4618,20 +4518,17 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "supports-color": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -4669,12 +4566,6 @@ "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true }, - "char-spinner": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/char-spinner/-/char-spinner-1.0.1.tgz", - "integrity": "sha1-5upnvSR+EHESmDt6sEee02KAAIE=", - "dev": true - }, "character-entities": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", @@ -4847,8 +4738,7 @@ "ci-info": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" }, "cipher-base": { "version": "1.0.4", @@ -4893,8 +4783,7 @@ "cli-boxes": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.0.tgz", - "integrity": "sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w==", - "dev": true + "integrity": "sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w==" }, "cli-cursor": { "version": "2.1.0", @@ -4905,15 +4794,6 @@ "restore-cursor": "^2.0.0" } }, - "cli-table": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.1.tgz", - "integrity": "sha1-9TsFJmqLGguTSz0IIebi3FkUriM=", - "dev": true, - "requires": { - "colors": "1.0.3" - } - }, "cli-width": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", @@ -5029,12 +4909,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, - "colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", - "dev": true - }, "combined-stream": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", @@ -5044,25 +4918,6 @@ "delayed-stream": "~1.0.0" } }, - "combined-stream2": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/combined-stream2/-/combined-stream2-1.1.2.tgz", - "integrity": "sha1-9uFLegFWZvjHsKH6xQYkAWSsNXA=", - "dev": true, - "requires": { - "bluebird": "^2.8.1", - "debug": "^2.1.1", - "stream-length": "^1.0.1" - }, - "dependencies": { - "bluebird": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", - "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=", - "dev": true - } - } - }, "comma-separated-tokens": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", @@ -5172,17 +5027,10 @@ } } }, - "condense-whitespace": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/condense-whitespace/-/condense-whitespace-1.0.0.tgz", - "integrity": "sha1-g3bZjvAo5sss0kaOKM5CxcZasak=", - "dev": true - }, "configstore": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", - "dev": true, "requires": { "dot-prop": "^5.2.0", "graceful-fs": "^4.1.2", @@ -5645,7 +5493,7 @@ }, "create-hash": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "requires": { "cipher-base": "^1.0.1", @@ -5657,7 +5505,7 @@ }, "create-hmac": { "version": "1.1.7", - "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "requires": { "cipher-base": "^1.0.3", @@ -5735,8 +5583,7 @@ "crypto-random-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "dev": true + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" }, "csp-parse": { "version": "0.0.2", @@ -6027,6 +5874,22 @@ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" }, + "decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "requires": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=" + } + } + }, "decimal.js": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.0.tgz", @@ -6055,8 +5918,7 @@ "deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" }, "deep-is": { "version": "0.1.3", @@ -6069,27 +5931,6 @@ "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", "dev": true }, - "default-user-agent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/default-user-agent/-/default-user-agent-1.0.0.tgz", - "integrity": "sha1-FsRu/cq6PtxF8k8r1IaLAbfCrcY=", - "dev": true, - "requires": { - "os-name": "~1.0.3" - }, - "dependencies": { - "os-name": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/os-name/-/os-name-1.0.3.tgz", - "integrity": "sha1-GzefZINa98Wn9JizV8uVIVwVnt8=", - "dev": true, - "requires": { - "osx-release": "^1.0.0", - "win-release": "^1.0.0" - } - } - } - }, "defer-to-connect": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", @@ -6370,12 +6211,6 @@ "minimist": "^1.1.1" } }, - "dev-null": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/dev-null/-/dev-null-0.1.1.tgz", - "integrity": "sha1-WiBc48Ky73e2I41roXnrdMag6Bg=", - "dev": true - }, "diff-sequences": { "version": "26.0.0", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.0.0.tgz", @@ -6384,7 +6219,7 @@ }, "diffie-hellman": { "version": "5.0.3", - "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "requires": { "bn.js": "^4.1.0", @@ -6652,7 +6487,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", - "dev": true, "requires": { "is-obj": "^2.0.0" } @@ -6807,12 +6641,6 @@ "through": "~2.3.4" } }, - "eol": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eol/-/eol-0.2.0.tgz", - "integrity": "sha1-L22whqJDpG4+Xb0OE0Ncfr6/Cd0=", - "dev": true - }, "errno": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", @@ -6824,18 +6652,11 @@ "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha1-tKxAZIEH/c3PriQvQovqihTU8b8=", - "dev": true, + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "requires": { "is-arrayish": "^0.2.1" } }, - "errors": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/errors/-/errors-0.2.0.tgz", - "integrity": "sha1-D1Hoidqj4RsZ5xhtEfEEqmbrJAM=", - "dev": true - }, "es-abstract": { "version": "1.17.6", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", @@ -6920,8 +6741,7 @@ "escape-goat": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", - "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", - "dev": true + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==" }, "escape-html": { "version": "1.0.3", @@ -7605,6 +7425,11 @@ } } }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, "event-to-promise": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/event-to-promise/-/event-to-promise-0.8.0.tgz", @@ -7614,8 +7439,7 @@ "eventemitter3": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz", - "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==", - "dev": true + "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==" }, "events": { "version": "1.1.1", @@ -8644,45 +8468,6 @@ "mime-types": "^2.1.12" } }, - "form-data2": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/form-data2/-/form-data2-1.0.4.tgz", - "integrity": "sha512-buz4C3F6/7Vpdxt7dNU/tSfjQO/5z9Pyjfb43VhlVvSos5zVhCcMuW9sF1yJ2FdxZRCD2sWQ9WhqUiYLt+AUVQ==", - "dev": true, - "requires": { - "bluebird": "^2.8.2", - "combined-stream2": "^1.0.2", - "debug": "^2.1.1", - "mime": "^1.3.4", - "uuid": "^2.0.1" - }, - "dependencies": { - "bluebird": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", - "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=", - "dev": true - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true - }, - "uuid": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", - "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=", - "dev": true - } - } - }, - "form-fix-array": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/form-fix-array/-/form-fix-array-1.0.0.tgz", - "integrity": "sha1-oTR6R+UxF6t7zb8+Lz7JHGZ2m8g=", - "dev": true - }, "format": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", @@ -9343,6 +9128,55 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "gaxios": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.0.1.tgz", + "integrity": "sha512-jOin8xRZ/UytQeBpSXFqIzqU7Fi5TqgPNLlUsSB8kjJ76+FiGBfImF8KJu++c6J4jOldfJUtt0YmkRj2ZpSHTQ==", + "requires": { + "abort-controller": "^3.0.0", + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.3.0" + }, + "dependencies": { + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + } + }, + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "requires": { + "ms": "2.1.2" + } + }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "gemoji": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/gemoji/-/gemoji-4.2.1.tgz", @@ -9432,7 +9266,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.0.1.tgz", "integrity": "sha512-5HqUqdhkEovj2Of/ms3IeS/EekcO54ytHRLV4PEY2rhRwrHXLQjeVEES0Lhka0xwNDtGYn58wyC4s5+MHsOO6A==", - "dev": true, "requires": { "ini": "^1.3.5" } @@ -9567,6 +9400,11 @@ "har-schema": "^2.0.0" } }, + "hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==" + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -9644,8 +9482,7 @@ "has-yarn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", - "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", - "dev": true + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==" }, "hash-base": { "version": "3.1.0", @@ -9962,7 +9799,7 @@ "dependencies": { "mkdirp": { "version": "0.3.0", - "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=" }, "nopt": { @@ -9992,8 +9829,15 @@ "hosted-git-info": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha1-l/I2l3vW4SVAiTD/bePuxigewEc=", - "dev": true + "integrity": "sha1-l/I2l3vW4SVAiTD/bePuxigewEc=" + }, + "hot-shots": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/hot-shots/-/hot-shots-8.2.0.tgz", + "integrity": "sha512-k0J7QAMGWAawZr8b+Bgornns9IlBaV1p0X6Ju/OTye2QLf+u1KuD9NEbSU4IShviyr1tSOUBUxrEm2QSlx2Nfw==", + "requires": { + "unix-dgram": "2.0.x" + } }, "hpkp": { "version": "2.0.0", @@ -10089,12 +9933,6 @@ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.0.3.tgz", "integrity": "sha512-TcIMG3qeVLgDr1TEd2XvHaTnMPwYQUQMIBLy+5pLSDKYFc7UIqj39w8EGzZkaxoLv/l2K8HaI0t5AVA+YYgUew==" }, - "http-equiv-refresh": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/http-equiv-refresh/-/http-equiv-refresh-1.0.0.tgz", - "integrity": "sha1-jsU4hmBCvl8/evpzfRmNlL6xsHs=", - "dev": true - }, "http-errors": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", @@ -10202,12 +10040,6 @@ "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", "dev": true }, - "humanize-duration": { - "version": "3.20.0", - "resolved": "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.20.0.tgz", - "integrity": "sha512-4w6Y+EqikmF3KDdOTOIi8xl1kCJcnB9mu304aB224ZwCxDm/qeqzh/aYHEfpLFxo0HMVur5ibNlel/VPdN+2Jw==", - "dev": true - }, "husky": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/husky/-/husky-4.2.1.tgz", @@ -10346,8 +10178,7 @@ "import-lazy": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", - "dev": true + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=" }, "import-local": { "version": "3.0.2", @@ -10585,8 +10416,7 @@ "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" }, "is-binary-path": { "version": "1.0.1", @@ -10597,12 +10427,6 @@ "binary-extensions": "^1.0.0" } }, - "is-browser": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-browser/-/is-browser-2.1.0.tgz", - "integrity": "sha512-F5rTJxDQ2sW81fcfOR1GnCXT6sVJC104fCyfj+mjpwNEwaPYSn5fte5jiHmBg3DHsIoL/l8Kvw5VN5SsTRcRFQ==", - "dev": true - }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", @@ -10627,11 +10451,18 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, "requires": { "ci-info": "^2.0.0" } }, + "is-core-module": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.1.0.tgz", + "integrity": "sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA==", + "requires": { + "has": "^1.0.3" + } + }, "is-data-descriptor": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", @@ -10698,8 +10529,7 @@ "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, "is-generator-fn": { "version": "2.1.0", @@ -10725,7 +10555,6 @@ "version": "0.3.2", "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", - "dev": true, "requires": { "global-dirs": "^2.0.1", "is-path-inside": "^3.0.1" @@ -10753,14 +10582,7 @@ "is-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true - }, - "is-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", - "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=", - "dev": true + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" }, "is-path-cwd": { "version": "2.2.0", @@ -10791,8 +10613,7 @@ "is-path-inside": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", - "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", - "dev": true + "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==" }, "is-plain-obj": { "version": "2.1.0", @@ -10848,12 +10669,6 @@ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, - "is-string": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.4.tgz", - "integrity": "sha1-zDqbaYV9Yh6WNyWiTK7shzuCbmQ=", - "dev": true - }, "is-symbol": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", @@ -10866,8 +10681,7 @@ "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, "is-unc-path": { "version": "1.0.0", @@ -10919,8 +10733,7 @@ "is-yarn-global": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", - "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", - "dev": true + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==" }, "is2": { "version": "2.0.4", @@ -10944,12 +10757,6 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" }, - "isbot": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/isbot/-/isbot-2.3.1.tgz", - "integrity": "sha512-SrdO3SoZCcymFt+JKmCrUD/Ln0vTUfBa/jBXekFI51NlVzWxYdKAw+ZQk2svcgje3BrL5Zwh3haECP0T7EBRVg==", - "dev": true - }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -14783,6 +14590,11 @@ "minimist": "^1.2.0" } }, + "jsonexport": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/jsonexport/-/jsonexport-3.0.1.tgz", + "integrity": "sha512-lxDoAZxmWDt1wa4S75CUYe/ZASdmOYyhV7iYbF4npTWxrDv19ofZpJMGbt20W5Orx0hYuid65zGHpt6rRW0Z3A==" + }, "jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", @@ -14860,7 +14672,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", - "dev": true, "requires": { "package-json": "^6.3.0" } @@ -14899,27 +14710,68 @@ "type-check": "~0.3.2" } }, - "limited-request-queue": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/limited-request-queue/-/limited-request-queue-2.0.0.tgz", - "integrity": "sha1-FMfBILE4BgsZoqEDCrr2aTVyZQ0=", - "dev": true, - "requires": { - "is-browser": "^2.0.1", - "parse-domain": "~0.2.0" - } - }, "lines-and-columns": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", - "dev": true + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=" }, - "link-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/link-types/-/link-types-1.1.0.tgz", - "integrity": "sha1-r2XlnbUucMH/sYrEw8sFa/55aDA=", - "dev": true + "linkinator": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/linkinator/-/linkinator-2.2.2.tgz", + "integrity": "sha512-mcMMbo8n8p6qDuCQ5zZaBbxKK9jq79oWsoduBIGc0rRzukDmfLr7t6Ok1jJA7aHHFX251NmtbX2LdQvD54UHow==", + "requires": { + "chalk": "^4.0.0", + "cheerio": "^1.0.0-rc.2", + "finalhandler": "^1.1.2", + "gaxios": "^4.0.0", + "jsonexport": "^3.0.0", + "meow": "^8.0.0", + "p-queue": "^6.2.1", + "serve-static": "^1.14.1", + "server-destroy": "^1.0.1", + "update-notifier": "^5.0.0" + }, + "dependencies": { + "is-npm": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", + "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==" + }, + "pupa": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", + "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", + "requires": { + "escape-goat": "^2.0.0" + } + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" + }, + "update-notifier": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.0.1.tgz", + "integrity": "sha512-BuVpRdlwxeIOvmc32AGYvO1KVdPlsmqSh8KDDBxS6kDE5VR7R8OMP1d8MdhaVBvxl4H3551k9akXr0Y1iIB2Wg==", + "requires": { + "boxen": "^4.2.0", + "chalk": "^4.1.0", + "configstore": "^5.0.1", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.3.2", + "is-npm": "^5.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.1.0", + "pupa": "^2.1.1", + "semver": "^7.3.2", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" + } + } + } }, "liquid": { "version": "5.1.0", @@ -14999,12 +14851,6 @@ "integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU=", "dev": true }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", - "dev": true - }, "lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", @@ -15121,16 +14967,6 @@ "highlight.js": "~10.3.0" } }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, "macos-release": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.3.0.tgz", @@ -15138,7 +14974,7 @@ }, "magic-string": { "version": "0.22.5", - "resolved": "http://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz", "integrity": "sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w==", "requires": { "vlq": "^0.2.2" @@ -15179,6 +15015,11 @@ "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" }, + "map-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.1.0.tgz", + "integrity": "sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g==" + }, "map-stream": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", @@ -15216,12 +15057,6 @@ } } }, - "maybe-callback": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/maybe-callback/-/maybe-callback-2.1.0.tgz", - "integrity": "sha1-ivoLp7aRp6sSPn8S9l4yu10fgkM=", - "dev": true - }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -15277,6 +15112,87 @@ "readable-stream": "^2.0.1" } }, + "meow": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.0.0.tgz", + "integrity": "sha512-nbsTRz2fwniJBFgUkcdISq8y/q9n9VbiHYbfwklFh5V4V2uAcxtKQkDc0yCLPM/kP0d+inZBewn3zJqewHE7kg==", + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "dependencies": { + "hosted-git-info": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.7.tgz", + "integrity": "sha512-fWqc0IcuXs+BmE9orLDyVykAG9GJtGLGuZAAqgcckPgv5xad4AcXGIv8galtQvlwutxSlaMcdw7BUtq2EIvqCQ==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "normalize-package-data": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.0.tgz", + "integrity": "sha512-6lUjEI0d3v6kFrtgA/lOx4zHCWULXsFNIjHolnZCKCTLA6m/G625cdn3O7eNmT0iD3jfo6HZ9cdImGZwf21prw==", + "requires": { + "hosted-git-info": "^3.0.6", + "resolve": "^1.17.0", + "semver": "^7.3.2", + "validate-npm-package-license": "^3.0.1" + } + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "requires": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + } + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" + }, + "type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==" + } + } + }, "merge-deep": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/merge-deep/-/merge-deep-3.0.2.tgz", @@ -15402,6 +15318,11 @@ "dom-walk": "^0.1.0" } }, + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==" + }, "mini-css-extract-plugin": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz", @@ -15464,6 +15385,23 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, + "minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "dependencies": { + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" + } + } + }, "minipass": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", @@ -16010,94 +15948,6 @@ } } }, - "nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", - "dev": true, - "requires": { - "abbrev": "1" - } - }, - "nopter": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/nopter/-/nopter-0.3.0.tgz", - "integrity": "sha1-uWkOb6uPJWs35OfM0j4rOEUMxx8=", - "dev": true, - "requires": { - "caller-path": "~0.1.0", - "camelcase": "^1.0.2", - "chalk": "~0.5.1", - "cli-table": "~0.3.1", - "eol": "~0.2.0", - "nopt": "^3.0.1", - "object-assign": "^2.0.0", - "splitargs": "~0.0.3" - }, - "dependencies": { - "ansi-regex": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", - "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=", - "dev": true - }, - "ansi-styles": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz", - "integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94=", - "dev": true - }, - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", - "dev": true - }, - "chalk": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", - "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=", - "dev": true, - "requires": { - "ansi-styles": "^1.1.0", - "escape-string-regexp": "^1.0.0", - "has-ansi": "^0.1.0", - "strip-ansi": "^0.3.0", - "supports-color": "^0.2.0" - } - }, - "has-ansi": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", - "integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=", - "dev": true, - "requires": { - "ansi-regex": "^0.2.0" - } - }, - "object-assign": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz", - "integrity": "sha1-Q8NuXVaf+OSBbE76i+AtJpZ8GKo=", - "dev": true - }, - "strip-ansi": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", - "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=", - "dev": true, - "requires": { - "ansi-regex": "^0.2.1" - } - }, - "supports-color": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz", - "integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo=", - "dev": true - } - } - }, "normalize-package-data": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", @@ -16359,15 +16209,6 @@ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, - "osx-release": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/osx-release/-/osx-release-1.1.0.tgz", - "integrity": "sha1-8heRGigTaUmvG/kwiyQeJzfTzWw=", - "dev": true, - "requires": { - "minimist": "^1.1.0" - } - }, "p-cancelable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", @@ -16419,7 +16260,6 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.2.1.tgz", "integrity": "sha512-wV8yC/rkuWpgu9LGKJIb48OynYSrE6lVl2Bx6r8WjbyVKrFAzzQ/QevAvwnDjlD+mLt8xy0LTDOU1freOvMTCg==", - "dev": true, "requires": { "eventemitter3": "^4.0.0", "p-timeout": "^3.1.0" @@ -16429,7 +16269,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", - "dev": true, "requires": { "p-finally": "^1.0.0" } @@ -16764,7 +16603,6 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", - "dev": true, "requires": { "got": "^9.6.0", "registry-auth-token": "^4.0.0", @@ -16775,8 +16613,7 @@ "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" } } }, @@ -16825,12 +16662,6 @@ "safe-buffer": "^5.1.1" } }, - "parse-domain": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/parse-domain/-/parse-domain-0.2.2.tgz", - "integrity": "sha1-GImJseLnOYv/PE9P19yhV+tR+sE=", - "dev": true - }, "parse-entities": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.2.2.tgz", @@ -17368,12 +17199,6 @@ "event-stream": "=3.3.4" } }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, "psl": { "version": "1.1.31", "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", @@ -17542,6 +17367,11 @@ "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" }, + "quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==" + }, "quote-stream": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/quote-stream/-/quote-stream-1.0.2.tgz", @@ -17601,7 +17431,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, "requires": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -17639,7 +17468,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, "requires": { "@types/normalize-package-data": "^2.4.0", "normalize-package-data": "^2.5.0", @@ -17651,7 +17479,6 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, "requires": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", @@ -17663,7 +17490,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", - "dev": true, "requires": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -17674,14 +17500,12 @@ "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" }, "resolve": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "dev": true, "requires": { "path-parse": "^1.0.6" } @@ -17689,8 +17513,7 @@ "type-fest": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==" } } }, @@ -17698,7 +17521,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, "requires": { "find-up": "^4.1.0", "read-pkg": "^5.2.0", @@ -17708,8 +17530,7 @@ "type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" } } }, @@ -17750,6 +17571,15 @@ "resolved": "https://registry.npmjs.org/readline-sync/-/readline-sync-1.4.10.tgz", "integrity": "sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==" }, + "redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "requires": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + } + }, "reduce": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/reduce/-/reduce-1.0.2.tgz", @@ -17826,7 +17656,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.0.tgz", "integrity": "sha512-P+lWzPrsgfN+UEpDS3U8AQKg/UjZX6mQSJueZj3EK+vNESoqBSpBUD3gmu4sF9lOsjXWjF11dQKUqemf3veq1w==", - "dev": true, "requires": { "rc": "^1.2.8" } @@ -17835,7 +17664,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", - "dev": true, "requires": { "rc": "^1.2.8" } @@ -18327,48 +18155,12 @@ "resolved": "https://registry.npmjs.org/rndm/-/rndm-1.2.0.tgz", "integrity": "sha1-8z/pz7Urv9UgqhgyO8ZdsRCht2w=" }, - "robot-directives": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/robot-directives/-/robot-directives-0.3.0.tgz", - "integrity": "sha1-F0+x/8KpuXh3MB6HyJs5X0KdH2U=", - "dev": true, - "requires": { - "isbot": "^2.0.0", - "useragent": "^2.1.8" - } - }, "robots-parser": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/robots-parser/-/robots-parser-2.1.1.tgz", "integrity": "sha512-6yWEYSdhK3bAEcYY0In3wgSBK70BiQoJArzdjZKCP/35b3gKIYu5Lc0qQqsoxjoLVebVoJiKK4VWGc5+oxvWBQ==", "dev": true }, - "robots-txt-guard": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/robots-txt-guard/-/robots-txt-guard-0.1.1.tgz", - "integrity": "sha512-6+nGkE6c2dI9/dmhmNcoMKVwJxlA6sgN/XNo0rm6LLdA0hnj4YkpgrZdhMPl58gJkAqeiHlf4+8tJcLM1tv1Ew==", - "dev": true - }, - "robots-txt-parse": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/robots-txt-parse/-/robots-txt-parse-0.0.4.tgz", - "integrity": "sha1-99HzI/eZIdfpxsS70lBI9umBDXE=", - "dev": true, - "requires": { - "bluebird": "^2.3.5", - "split": "^0.3.0", - "stream-combiner": "^0.2.1", - "through": "^2.3.4" - }, - "dependencies": { - "bluebird": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", - "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=", - "dev": true - } - } - }, "rsvp": { "version": "4.8.5", "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", @@ -18582,7 +18374,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", - "dev": true, "requires": { "semver": "^6.3.0" }, @@ -18590,8 +18381,7 @@ "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" } } }, @@ -18661,6 +18451,11 @@ "send": "0.17.1" } }, + "server-destroy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz", + "integrity": "sha1-8Tv5KOQrnD55OD5hzDmYtdFObN0=" + }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -18699,7 +18494,7 @@ }, "sha.js": { "version": "2.4.11", - "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "requires": { "inherits": "^2.0.1", @@ -18988,7 +18783,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", "integrity": "sha1-BaW01xU6GVvJLDxCW2nzsqlSTII=", - "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" @@ -18997,14 +18791,12 @@ "spdx-exceptions": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", - "integrity": "sha1-LHrmEFbHFKW5ubKyr30xHvXHj+k=", - "dev": true + "integrity": "sha1-LHrmEFbHFKW5ubKyr30xHvXHj+k=" }, "spdx-expression-parse": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", "integrity": "sha1-meEZt6XaAOBUkcn6M4t5BII7QdA=", - "dev": true, "requires": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" @@ -19013,8 +18805,7 @@ "spdx-license-ids": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", - "integrity": "sha1-enzShHDMbToc/m1miG9rxDDTrIc=", - "dev": true + "integrity": "sha1-enzShHDMbToc/m1miG9rxDDTrIc=" }, "split": { "version": "0.3.3", @@ -19033,12 +18824,6 @@ "extend-shallow": "^3.0.0" } }, - "splitargs": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/splitargs/-/splitargs-0.0.7.tgz", - "integrity": "sha1-/p965lc3GzOxDLgNoUPPgknPazs=", - "dev": true - }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -19342,16 +19127,6 @@ "readable-stream": "^2.0.2" } }, - "stream-combiner": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", - "integrity": "sha1-rsjLrBd7Vrb0+kec7YwZEs7lKFg=", - "dev": true, - "requires": { - "duplexer": "~0.1.1", - "through": "~2.3.4" - } - }, "stream-each": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", @@ -19373,23 +19148,6 @@ "xtend": "^4.0.0" } }, - "stream-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stream-length/-/stream-length-1.0.2.tgz", - "integrity": "sha1-gnfzy+5JpNqrz9tOL0qbXp8snwA=", - "dev": true, - "requires": { - "bluebird": "^2.6.2" - }, - "dependencies": { - "bluebird": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", - "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=", - "dev": true - } - } - }, "stream-shift": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", @@ -19436,7 +19194,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -19446,20 +19203,17 @@ "ansi-regex": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "strip-ansi": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, "requires": { "ansi-regex": "^5.0.0" } @@ -19650,11 +19404,18 @@ "resolved": "https://registry.npmjs.org/strip-html-comments/-/strip-html-comments-1.0.0.tgz", "integrity": "sha1-Cuff8DAKYHWkwpP7YRG0yx0Mt7c=" }, + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "requires": { + "min-indent": "^1.0.0" + } + }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" }, "style-loader": { "version": "1.2.1", @@ -19882,8 +19643,7 @@ "term-size": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.0.tgz", - "integrity": "sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw==", - "dev": true + "integrity": "sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw==" }, "terminal-link": { "version": "2.1.1", @@ -20054,98 +19814,6 @@ } } }, - "through2-sink": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/through2-sink/-/through2-sink-1.0.0.tgz", - "integrity": "sha1-XxBruh1zMNrTy6XAqxhjkjJWw5k=", - "dev": true, - "requires": { - "through2": "~0.5.1", - "xtend": "~3.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - }, - "through2": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.5.1.tgz", - "integrity": "sha1-390BLrnHAOIyP9M084rGIqs3Lac=", - "dev": true, - "requires": { - "readable-stream": "~1.0.17", - "xtend": "~3.0.0" - } - }, - "xtend": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz", - "integrity": "sha1-XM50B7r2Qsunvs2laBEcST9ZZlo=", - "dev": true - } - } - }, - "through2-spy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/through2-spy/-/through2-spy-1.2.0.tgz", - "integrity": "sha1-nIkcqcpA4eHkzzHhrFf5TMnSSMs=", - "dev": true, - "requires": { - "through2": "~0.5.1", - "xtend": "~3.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - }, - "through2": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.5.1.tgz", - "integrity": "sha1-390BLrnHAOIyP9M084rGIqs3Lac=", - "dev": true, - "requires": { - "readable-stream": "~1.0.17", - "xtend": "~3.0.0" - } - }, - "xtend": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz", - "integrity": "sha1-XM50B7r2Qsunvs2laBEcST9ZZlo=", - "dev": true - } - } - }, "timers-browserify": { "version": "2.0.11", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz", @@ -20330,6 +19998,11 @@ "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-1.1.3.tgz", "integrity": "sha512-E0ZosSWYK2mkSu+KEtQ9/KqarVjA9HztOSX+9FDdNacRAq29RRV6ZQNgob3iuW8Htar9vAfEa6yyt5qBAHZDBA==" }, + "trim-newlines": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz", + "integrity": "sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA==" + }, "trim-trailing-lines": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.3.tgz", @@ -20434,7 +20107,6 @@ "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, "requires": { "is-typedarray": "^1.0.0" } @@ -20548,7 +20220,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "dev": true, "requires": { "crypto-random-string": "^2.0.0" } @@ -20630,6 +20301,16 @@ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true }, + "unix-dgram": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/unix-dgram/-/unix-dgram-2.0.4.tgz", + "integrity": "sha512-7tpK6x7ls7J7pDrrAU63h93R0dVhRbPwiRRCawR10cl+2e1VOvF3bHlVJc6WI1dl/8qk5He673QU+Ogv7bPNaw==", + "optional": true, + "requires": { + "bindings": "^1.3.0", + "nan": "^2.13.2" + } + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -20812,26 +20493,6 @@ "prepend-http": "^2.0.0" } }, - "urlcache": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/urlcache/-/urlcache-0.7.0.tgz", - "integrity": "sha512-xOW4t6wJDT07+VunsHwePemyXXRidCSOZ/1RIILJi2XnB+81FA5H0MRvS63/7joTWjGLajcJJGvR5odpbkV6hw==", - "dev": true, - "requires": { - "urlobj": "0.0.11" - } - }, - "urlobj": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/urlobj/-/urlobj-0.0.11.tgz", - "integrity": "sha512-Ncck0WWtuFBbZhSYwKjK1AU2V51V98P/KHUPkaEc+mFy4xkpAHFNyVQT+S5SgtsJAr94e4wiKUucJSfasV2kBw==", - "dev": true, - "requires": { - "is-object": "^1.0.1", - "is-string": "^1.0.4", - "object-assign": "^4.1.1" - } - }, "use": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/use/-/use-3.1.0.tgz", @@ -20840,16 +20501,6 @@ "kind-of": "^6.0.2" } }, - "useragent": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz", - "integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==", - "dev": true, - "requires": { - "lru-cache": "4.1.x", - "tmp": "0.0.x" - } - }, "util": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", @@ -20909,7 +20560,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz", "integrity": "sha1-gWQ7y+8b3+zUYjeT3EZIlIupgzg=", - "dev": true, "requires": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" @@ -21752,20 +21402,10 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "dev": true, "requires": { "string-width": "^4.0.0" } }, - "win-release": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/win-release/-/win-release-1.1.1.tgz", - "integrity": "sha1-X6VeAr58qTTt/BJmVjLoSbcuUgk=", - "dev": true, - "requires": { - "semver": "^5.0.1" - } - }, "windows-release": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.2.0.tgz", @@ -21870,7 +21510,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, "requires": { "imurmurhash": "^0.1.4", "is-typedarray": "^1.0.0", @@ -21892,8 +21531,7 @@ "xdg-basedir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", - "dev": true + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==" }, "xml-name-validator": { "version": "3.0.0", @@ -21933,12 +21571,6 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - }, "yaml": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.7.2.tgz", From 367685b166949049f1b9dd95a13370461bf4adff Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Mon, 16 Nov 2020 19:59:15 -0600 Subject: [PATCH 067/105] Update symlinking script to support unlinking --- .../early-access/symlink-from-local-repo.js | 61 ++++++++++++++----- 1 file changed, 45 insertions(+), 16 deletions(-) diff --git a/script/early-access/symlink-from-local-repo.js b/script/early-access/symlink-from-local-repo.js index 5acc8a44e5..1e62274267 100644 --- a/script/early-access/symlink-from-local-repo.js +++ b/script/early-access/symlink-from-local-repo.js @@ -11,28 +11,44 @@ const rimraf = require('rimraf').sync const fs = require('fs') const path = require('path') +const program = require('commander') // Early Access details +const earlyAccessRepo = 'docs-early-access' const earlyAccessDirName = 'early-access' +const earlyAccessRepoUrl = `https://github.com/github/${earlyAccessRepo}` -if (!process.argv[2]) { - throw new Error('Must provide the location of your local `docs-early-access` repo directory') +program + .description(`Create or destroy symlinks to your local "${earlyAccessRepo}" repository.`) + .option('-p, --path-to-early-access-repo ', `path to a local checkout of ${earlyAccessRepoUrl}`) + .option('-u, --unlink', 'remove the symlinks') + .parse(process.argv) + +const { pathToEarlyAccessRepo, unlink } = program + +if (!pathToEarlyAccessRepo && !unlink) { + throw new Error('Must provide either `--path-to-early-access-repo ` or `--unlink`') } -const earlyAccessLocalRepoDir = path.resolve(process.cwd(), process.argv[2]) +let earlyAccessLocalRepoDir -let dirStats -try { - dirStats = fs.statSync(earlyAccessLocalRepoDir) -} catch (err) { - dirStats = null -} +// If creating symlinks, run some extra validation +if (!unlink && pathToEarlyAccessRepo) { + earlyAccessLocalRepoDir = path.resolve(process.cwd(), pathToEarlyAccessRepo) -if (!dirStats) { - throw new Error('The local `docs-early-access` repo directory does not exist:', earlyAccessLocalRepoDir) -} -if (dirStats && !dirStats.isDirectory()) { - throw new Error('A non-directory entry exists at the local `docs-early-access` repo directory location:', earlyAccessLocalRepoDir) + let dirStats + try { + dirStats = fs.statSync(earlyAccessLocalRepoDir) + } catch (err) { + dirStats = null + } + + if (!dirStats) { + throw new Error(`The local "${earlyAccessRepo}" repo directory does not exist:`, earlyAccessLocalRepoDir) + } + if (dirStats && !dirStats.isDirectory()) { + throw new Error(`A non-directory entry exists at the local "${earlyAccessRepo}" repo directory location:`, earlyAccessLocalRepoDir) + } } const destinationDirNames = ['content', 'data', 'assets/images'] @@ -46,7 +62,20 @@ const destinationDirsMap = destinationDirNames ) // Remove all existing early access directories from this repo -destinationDirNames.forEach(key => rimraf(destinationDirsMap[key])) +destinationDirNames.forEach((dirName) => { + const destDir = destinationDirsMap[dirName] + rimraf(destDir) + console.log(`- Removed symlink for early access directory '${dirName}' from this repo`) +}) + +// If removing symlinks, just stop here! +if (unlink) { + process.exit(0) +} + +// +// Otherwise, keep going... +// // Move the latest early access source directories into this repo destinationDirNames.forEach((dirName) => { @@ -73,5 +102,5 @@ destinationDirNames.forEach((dirName) => { throw new Error(`The early access directory '${dirName}' entry's symbolic link does not refer to a directory!`) } - console.log(`Successfully symlinked early access directory '${dirName}' into this repo`) + console.log(`+ Added symlink for early access directory '${dirName}' into this repo`) }) From 5c6f5bb3512a5f23c976996e049920fc1a571fce Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Tue, 17 Nov 2020 09:55:34 -0600 Subject: [PATCH 068/105] Fix site-data-references tests by using uniqWith instead of uniqBy --- tests/content/site-data-references.js | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/tests/content/site-data-references.js b/tests/content/site-data-references.js index c35d6044f1..bafe99c0c1 100644 --- a/tests/content/site-data-references.js +++ b/tests/content/site-data-references.js @@ -1,4 +1,4 @@ -const { isEqual, get, uniqBy } = require('lodash') +const { isEqual, get, uniqWith } = require('lodash') const loadSiteData = require('../../lib/site-data') const loadPages = require('../../lib/pages') const getDataReferences = require('../../lib/get-liquid-data-references') @@ -6,8 +6,8 @@ const fs = require('fs') const path = require('path') describe('data references', () => { - let data - let pages + let data, pages + beforeAll(async (done) => { data = await loadSiteData() pages = await loadPages() @@ -20,15 +20,15 @@ describe('data references', () => { expect(pages.length).toBeGreaterThan(0) pages.forEach(page => { + const file = path.join('content', page.relativePath) const pageRefs = getDataReferences(page.markdown) pageRefs.forEach(key => { const value = get(data.en, key) - const file = path.join('content', page.relativePath) if (typeof value !== 'string') errors.push({ key, value, file }) }) }) - errors = uniqBy(errors, isEqual) // remove duplicates + errors = uniqWith(errors, isEqual) // remove duplicates expect(errors.length, JSON.stringify(errors, null, 2)).toBe(0) }) @@ -39,17 +39,18 @@ describe('data references', () => { expect(reusables.length).toBeGreaterThan(0) reusables.forEach(reusablesPerFile => { + let reusableFile = path.join(__dirname, '../../data/reusables/', getFilenameByValue(allReusables, reusablesPerFile)) + reusableFile = getFilepath(reusableFile) + const reusableRefs = getDataReferences(JSON.stringify(reusablesPerFile)) reusableRefs.forEach(key => { const value = get(data.en, key) - let reusableFile = path.join(__dirname, '../../data/reusables/', getFilenameByValue(allReusables, reusablesPerFile)) - reusableFile = getFilepath(reusableFile) if (typeof value !== 'string') errors.push({ key, value, reusableFile }) }) }) - errors = uniqBy(errors, isEqual) // remove duplicates + errors = uniqWith(errors, isEqual) // remove duplicates expect(errors.length, JSON.stringify(errors, null, 2)).toBe(0) }) @@ -60,17 +61,18 @@ describe('data references', () => { expect(variables.length).toBeGreaterThan(0) variables.forEach(variablesPerFile => { + let variableFile = path.join(__dirname, '../../data/variables/', getFilenameByValue(allVariables, variablesPerFile)) + variableFile = getFilepath(variableFile) + const variableRefs = getDataReferences(JSON.stringify(variablesPerFile)) variableRefs.forEach(key => { const value = get(data.en, key) - let variableFile = path.join(__dirname, '../../data/variables/', getFilenameByValue(allVariables, variablesPerFile)) - variableFile = getFilepath(variableFile) if (typeof value !== 'string') errors.push({ key, value, variableFile }) }) }) - errors = uniqBy(errors, isEqual) // remove duplicates + errors = uniqWith(errors, isEqual) // remove duplicates expect(errors.length, JSON.stringify(errors, null, 2)).toBe(0) }) }) From a53255c8c9184ebf1541e8f9d081f3565ebb3fb9 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Tue, 17 Nov 2020 09:56:21 -0600 Subject: [PATCH 069/105] Add tests for site-data-references within article metadata --- tests/content/site-data-references.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/content/site-data-references.js b/tests/content/site-data-references.js index bafe99c0c1..30f16950ca 100644 --- a/tests/content/site-data-references.js +++ b/tests/content/site-data-references.js @@ -2,6 +2,7 @@ const { isEqual, get, uniqWith } = require('lodash') const loadSiteData = require('../../lib/site-data') const loadPages = require('../../lib/pages') const getDataReferences = require('../../lib/get-liquid-data-references') +const frontmatter = require('@github-docs/frontmatter') const fs = require('fs') const path = require('path') @@ -32,6 +33,25 @@ describe('data references', () => { expect(errors.length, JSON.stringify(errors, null, 2)).toBe(0) }) + test('every data reference found in metadata of English content files is defined and has a value', () => { + let errors = [] + expect(pages.length).toBeGreaterThan(0) + + pages.forEach(page => { + const metadataFile = path.join('content', page.relativePath) + const fileContents = fs.readFileSync(path.join(__dirname, '../..', metadataFile)) + const { data: metadata } = frontmatter(fileContents, { filepath: page.fullPath }) + const metadataRefs = getDataReferences(JSON.stringify(metadata)) + metadataRefs.forEach(key => { + const value = get(data.en, key) + if (typeof value !== 'string') errors.push({ key, value, metadataFile }) + }) + }) + + errors = uniqWith(errors, isEqual) // remove duplicates + expect(errors.length, JSON.stringify(errors, null, 2)).toBe(0) + }) + test('every data reference found in English reusable files is defined and has a value', () => { let errors = [] const allReusables = data.en.site.data.reusables From 88a889a97f24dd73d87b9f259d9cbdaa2eae5f66 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Tue, 17 Nov 2020 09:56:48 -0600 Subject: [PATCH 070/105] Ensure early-access site-data-references are detected by the tests --- lib/patterns.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/patterns.js b/lib/patterns.js index 44a621ea0c..1184486b75 100644 --- a/lib/patterns.js +++ b/lib/patterns.js @@ -25,7 +25,7 @@ module.exports = { searchPath: /\/search(?:\/)?(\?)/, ymd: /^\d{4}-\d{2}-\d{2}$/, hasLiquid: /[{{][{%]/, - dataReference: /{% ?data\s(?:reusables|variables|ui)\..*?%}/gm, + dataReference: /{% ?data\s(?:early-access\.)?(?:reusables|variables|ui)\..*?%}/gm, imagePath: /\/?assets\/images\/.*?\.(png|svg|gif|pdf|ico|jpg|jpeg)/gi, homepagePath: /^\/\w{2}$/, // /en, /ja, /cn multipleSlashes: /^\/{2,}/, From 04dff854c256c0efcb3d3806c3bb26ced507e10f Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Tue, 17 Nov 2020 11:50:18 -0600 Subject: [PATCH 071/105] Include other actions-only EA-cloning tests now --- tests/unit/early-access.js | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/tests/unit/early-access.js b/tests/unit/early-access.js index 82b5bc2804..f8ad0b3b88 100644 --- a/tests/unit/early-access.js +++ b/tests/unit/early-access.js @@ -5,23 +5,19 @@ const { GITHUB_ACTIONS, GITHUB_REPOSITORY } = process.env const runningActionsOnInternalRepo = GITHUB_ACTIONS === 'true' && GITHUB_REPOSITORY === 'github/docs-internal' const testViaActionsOnly = runningActionsOnInternalRepo ? test : test.skip -// -// TODO test ea-config -// - describe('cloning early-access', () => { testViaActionsOnly('the content directory exists', async () => { const eaContentDir = path.join(process.cwd(), 'content/early-access') expect(fs.existsSync(eaContentDir)).toBe(true) }) - // testViaActionsOnly('the data directory exists', async () => { - // const eaContentDir = path.join(process.cwd(), 'data/early-access') - // expect(fs.existsSync(eaContentDir)).toBe(true) - // }) + testViaActionsOnly('the data directory exists', async () => { + const eaContentDir = path.join(process.cwd(), 'data/early-access') + expect(fs.existsSync(eaContentDir)).toBe(true) + }) - // testViaActionsOnly('the assets directory exists', async () => { - // const eaContentDir = path.join(process.cwd(), 'assets/early-access') - // expect(fs.existsSync(eaContentDir)).toBe(true) - // }) + testViaActionsOnly('the assets/images directory exists', async () => { + const eaContentDir = path.join(process.cwd(), 'assets/images/early-access') + expect(fs.existsSync(eaContentDir)).toBe(true) + }) }) From 909db6fcf41c1ecb23e3074b2521bef56188bf4b Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Tue, 17 Nov 2020 13:51:48 -0600 Subject: [PATCH 072/105] Update EA cloning script to rely on the current git branch --- .github/workflows/test.yml | 1 + ea-config.yml | 6 ---- script/early-access/clone-for-build.js | 42 ++++++++++++++++++++++---- 3 files changed, 37 insertions(+), 12 deletions(-) delete mode 100644 ea-config.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e3cf8725f9..900d60498e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -77,6 +77,7 @@ jobs: run: npm run heroku-postbuild env: DOCUBOT_REPO_PAT: ${{ secrets.DOCUBOT_REPO_PAT }} + GIT_BRANCH: ${{ github.ref }} - name: Run build script if: ${{ github.repository != 'github/docs-internal' and needs.see_if_should_skip.outputs.should_skip != 'true'}} diff --git a/ea-config.yml b/ea-config.yml deleted file mode 100644 index 4f342b6a20..0000000000 --- a/ea-config.yml +++ /dev/null @@ -1,6 +0,0 @@ -# DO NOT CHANGE -EA_PRODUCTION_BRANCH: main - -# Set to the early-access branch you want to stage -# This MUST match your docs-internal branch name -EA_STAGING_BRANCH: main diff --git a/script/early-access/clone-for-build.js b/script/early-access/clone-for-build.js index 37d5311b8c..13acd8107c 100755 --- a/script/early-access/clone-for-build.js +++ b/script/early-access/clone-for-build.js @@ -10,7 +10,11 @@ // [end-readme] require('dotenv').config() -const { DOCUBOT_REPO_PAT, HEROKU_PRODUCTION_APP } = process.env +const { + DOCUBOT_REPO_PAT, + HEROKU_PRODUCTION_APP, + GIT_BRANCH +} = process.env // Exit if PAT is not found if (!DOCUBOT_REPO_PAT) { @@ -23,8 +27,22 @@ const rimraf = require('rimraf').sync const fs = require('fs') const path = require('path') const os = require('os') -const yaml = require('js-yaml') -const eaConfig = yaml.load(fs.readFileSync(path.join(process.cwd(), 'ea-config.yml'), 'utf8')) +const EA_PRODUCTION_BRANCH = 'main' + +// If a branch name is not provided in the environment, attempt to get +// the local branch name; or default to 'main' +let currentBranch = (GIT_BRANCH || '').replace(/^refs\/heads\//, '') +if (!currentBranch) { + try { + currentBranch = execSync('git branch --show-current').toString() + } catch (err) { + // Ignore but log + console.warn('Error checking for local branch:', err.message) + } +} +if (!currentBranch) { + currentBranch = EA_PRODUCTION_BRANCH +} // Early Access details const earlyAccessOwner = 'github' @@ -50,12 +68,24 @@ const destinationDirsMap = destinationDirNames const environment = HEROKU_PRODUCTION_APP ? 'production' : 'staging' // Early access branch to clone -const earlyAccessBranch = HEROKU_PRODUCTION_APP ? eaConfig.EA_PRODUCTION_BRANCH : eaConfig.EA_STAGING_BRANCH +let earlyAccessBranch = HEROKU_PRODUCTION_APP ? EA_PRODUCTION_BRANCH : currentBranch // Confirm that the branch exists in the remote -const branchExists = execSync(`git ls-remote --heads ${earlyAccessFullRepo} ${earlyAccessBranch}`).toString() +let branchExists = execSync(`git ls-remote --heads ${earlyAccessFullRepo} ${earlyAccessBranch}`).toString() + +// If the branch did NOT exist, try checking for the default branch instead +if (!branchExists && earlyAccessBranch !== EA_PRODUCTION_BRANCH) { + console.warn(`The branch '${earlyAccessBranch}' was not found in ${earlyAccessOwner}/${earlyAccessRepoName}!`) + console.warn(`Attempting the default branch ${EA_PRODUCTION_BRANCH} instead...`) + + earlyAccessBranch = EA_PRODUCTION_BRANCH + branchExists = execSync(`git ls-remote --heads ${earlyAccessFullRepo} ${earlyAccessBranch}`).toString() +} + +// If no suitable branch was found, bail out now if (!branchExists) { - console.error(`The branch '${earlyAccessBranch}' was not found in ${earlyAccessOwner}/${earlyAccessRepoName}. Exiting!`) + console.error(`The branch '${earlyAccessBranch}' was not found in ${earlyAccessOwner}/${earlyAccessRepoName}!`) + console.error('Exiting!') process.exit(1) } From 4962f91a13fb5ab1498f5d9b13847c140b99e2ac Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Tue, 17 Nov 2020 14:50:43 -0600 Subject: [PATCH 073/105] Fix test workflow file --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 900d60498e..b9b1f158cd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -73,14 +73,14 @@ jobs: run: npm ci - name: Clone early access - if: ${{ github.repository == 'github/docs-internal' }} + if: ${{ needs.see_if_should_skip.outputs.should_skip != 'true' && github.repository == 'github/docs-internal' }} run: npm run heroku-postbuild env: DOCUBOT_REPO_PAT: ${{ secrets.DOCUBOT_REPO_PAT }} GIT_BRANCH: ${{ github.ref }} - name: Run build script - if: ${{ github.repository != 'github/docs-internal' and needs.see_if_should_skip.outputs.should_skip != 'true'}} + if: ${{ needs.see_if_should_skip.outputs.should_skip != 'true' && github.repository != 'github/docs-internal' }} run: npm run build - if: ${{ needs.see_if_should_skip.outputs.should_skip != 'true' }} From 1280f6f06c20cfbf32d3f107ba7f04af57319ddf Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Tue, 17 Nov 2020 16:18:54 -0600 Subject: [PATCH 074/105] Fix rendering tests for /early-access --- tests/rendering/server.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/tests/rendering/server.js b/tests/rendering/server.js index e6cd000bb2..d59706d3b1 100644 --- a/tests/rendering/server.js +++ b/tests/rendering/server.js @@ -358,12 +358,12 @@ describe('server', () => { }) }) - describe('hidden articles', () => { + describe('Early Access articles', () => { let hiddenPageHrefs, hiddenPages beforeAll(async (done) => { - const $ = await getDOM('/hidden') - hiddenPageHrefs = $('a').map((i, el) => $(el).attr('href')).get() + const $ = await getDOM('/early-access') + hiddenPageHrefs = $('#article-contents ul > li > a').map((i, el) => $(el).attr('href')).get() const allPages = await loadPages() hiddenPages = allPages.filter(page => page.languageCode === 'en' && page.hidden) @@ -371,16 +371,17 @@ describe('server', () => { done() }) - test('are listed at /hidden', async () => { - expect(hiddenPageHrefs.length).toBe(hiddenPages.length) + test('are listed at /early-access', async () => { + // hiddenPages count multiplied by product version count per page + expect(hiddenPageHrefs.length).toBeGreaterThanOrEqual(hiddenPages.length) }) - test('are not listed at /hidden in production', async () => { + test('are not listed at /early-access in production', async () => { const oldNodeEnv = process.env.NODE_ENV process.env.NODE_ENV = 'production' - const res = await get('/hidden') + const res = await get('/early-access', { followRedirects: true }) process.env.NODE_ENV = oldNodeEnv - expect(res.statusCode).toBe(403) + expect(res.statusCode).toBe(404) }) test('have noindex meta tags', async () => { @@ -390,7 +391,7 @@ describe('server', () => { } }) - test('non-hidden articles do not have noindex meta tags', async () => { + test('public articles do not have noindex meta tags', async () => { const $ = await getDOM('/en/articles/set-up-git') expect($('meta[content="noindex"]').length).toBe(0) }) From b04f01d8907b4a83c6eef94ddcfdd2ecfec14558 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Tue, 17 Nov 2020 16:22:44 -0600 Subject: [PATCH 075/105] Ignore early-access as a product for the category-pages tests? --- tests/content/category-pages.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/content/category-pages.js b/tests/content/category-pages.js index 7f7d77137a..702c2d4c8c 100644 --- a/tests/content/category-pages.js +++ b/tests/content/category-pages.js @@ -25,7 +25,7 @@ describe('category pages', () => { const walkOptions = { globs: ['*/index.md', 'enterprise/*/index.md'], - ignore: ['{rest,graphql,developers}/**', 'enterprise/index.md', '**/articles/**'], + ignore: ['{rest,graphql,developers}/**', 'enterprise/index.md', '**/articles/**', 'early-access/**'], directories: false, includeBasePath: true } From 72d1265ebcea6251761be353fafbae322d484883 Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Tue, 17 Nov 2020 16:30:08 -0600 Subject: [PATCH 076/105] Fix lint-files tests for Early Access to use newly established pattern --- tests/content/lint-files.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/content/lint-files.js b/tests/content/lint-files.js index 6b8073f72c..5051e921cc 100644 --- a/tests/content/lint-files.js +++ b/tests/content/lint-files.js @@ -405,6 +405,7 @@ describe('lint-files', () => { const matches = [] for (const [key, content] of Object.entries(dictionary)) { + if (typeof content !== 'string') continue const valMatches = (content.match(earlyAccessLinkRegex) || []) if (valMatches.length > 0) { matches.push(...valMatches.map((match) => `Key "${key}": ${match}`)) @@ -422,6 +423,7 @@ describe('lint-files', () => { const matches = [] for (const [key, content] of Object.entries(dictionary)) { + if (typeof content !== 'string') continue const valMatches = (content.match(earlyAccessImageRegex) || []) if (valMatches.length > 0) { matches.push(...valMatches.map((match) => `Key "${key}": ${match}`)) @@ -439,6 +441,7 @@ describe('lint-files', () => { const matches = [] for (const [key, content] of Object.entries(dictionary)) { + if (typeof content !== 'string') continue const valMatches = (content.match(badEarlyAccessImageRegex) || []) if (valMatches.length > 0) { matches.push(...valMatches.map((match) => `Key "${key}": ${match}`)) From d6fcc369ea275fd4315b91f1ec526dc6fe67184a Mon Sep 17 00:00:00 2001 From: "James M. Greene" Date: Tue, 17 Nov 2020 18:29:07 -0600 Subject: [PATCH 077/105] Render the nice 404 page for /early-access in non-local envs --- middleware/early-access-context.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/middleware/early-access-context.js b/middleware/early-access-context.js index fff0f76012..e9acd62c5e 100644 --- a/middleware/early-access-context.js +++ b/middleware/early-access-context.js @@ -1,6 +1,6 @@ module.exports = function earlyAccessContext (req, res, next) { if (process.env.NODE_ENV === 'production') { - return res.status(404).end() + return next(404) } const earlyAccessPages = req.context.pages From 6304c863febc2e3e1f8beb9725d1083cdad4456f Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Tue, 1 Dec 2020 13:07:00 -0500 Subject: [PATCH 078/105] lint --- lib/warm-server.js | 2 +- tests/content/lint-files.js | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/warm-server.js b/lib/warm-server.js index 8acaf885c2..f1dcb4a0b3 100644 --- a/lib/warm-server.js +++ b/lib/warm-server.js @@ -8,7 +8,7 @@ async function warmServer () { } // Promise.all is used to load multiple things in parallel - ;[pages, site] = await Promise.all([ + [pages, site] = await Promise.all([ require('./pages')(), require('./site-data')() ]) diff --git a/tests/content/lint-files.js b/tests/content/lint-files.js index f3957f5be7..1ebe5626d3 100644 --- a/tests/content/lint-files.js +++ b/tests/content/lint-files.js @@ -4,6 +4,7 @@ const fs = require('fs') const walk = require('walk-sync') const { zip } = require('lodash') const yaml = require('js-yaml') +const frontmatter = require('../../lib/frontmatter') const languages = require('../../lib/languages') const { tags } = require('../../lib/liquid-tags/extended-markdown') const ghesReleaseNotesSchema = require('../../lib/release-notes-schema') @@ -167,7 +168,7 @@ describe('lint-files', () => { beforeAll(async () => { const fileContents = await fs.promises.readFile(markdownAbsPath, 'utf8') - const { data, content: bodyContent } = matter(fileContents) + const { data, content: bodyContent } = frontmatter(fileContents) content = bodyContent isHidden = data.hidden === true From 93a9d3b85b799a03e4c54286dc480e684d7b9c59 Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Tue, 1 Dec 2020 13:07:18 -0500 Subject: [PATCH 079/105] add new script --- script/early-access/clone-locally | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100755 script/early-access/clone-locally diff --git a/script/early-access/clone-locally b/script/early-access/clone-locally new file mode 100755 index 0000000000..cd53729502 --- /dev/null +++ b/script/early-access/clone-locally @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +if [ -d "../docs-early-access" ]; then + echo "A '../docs-early-access' directory already exists!" + exit 0 +fi + +# Go up a directory +pushd .. + +# Clone the repo +git clone git@github.com:github/docs-early-access.git + +# Go back to the previous working directory +popd + +# Symlink the local docs-early-access repo into this repo +node script/early-access/symlink-from-local-repo.js -p ../docs-early-access + +echo -e '\nDone!' \ No newline at end of file From 6214ee05174f46dbdead746236eab5718184bd18 Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Tue, 1 Dec 2020 13:12:14 -0500 Subject: [PATCH 080/105] be careful of paths for cross-platform use --- script/early-access/clone-locally | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/script/early-access/clone-locally b/script/early-access/clone-locally index cd53729502..5faa3c974f 100755 --- a/script/early-access/clone-locally +++ b/script/early-access/clone-locally @@ -1,18 +1,19 @@ #!/usr/bin/env bash -if [ -d "../docs-early-access" ]; then - echo "A '../docs-early-access' directory already exists!" +# Go up a directory +pushd .. > /dev/null + +if [ -d "docs-early-access" ]; then + echo "A 'docs-early-access' directory already exists!" + popd > /dev/null exit 0 fi -# Go up a directory -pushd .. - # Clone the repo git clone git@github.com:github/docs-early-access.git # Go back to the previous working directory -popd +popd > /dev/null # Symlink the local docs-early-access repo into this repo node script/early-access/symlink-from-local-repo.js -p ../docs-early-access From e49914f0b7efc0d82787ead3f3831fbb3f1aeb47 Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Tue, 1 Dec 2020 13:30:58 -0500 Subject: [PATCH 081/105] render early access links for the current version on the landing page --- middleware/early-access-context.js | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/middleware/early-access-context.js b/middleware/early-access-context.js index e9acd62c5e..99a64b144f 100644 --- a/middleware/early-access-context.js +++ b/middleware/early-access-context.js @@ -3,21 +3,22 @@ module.exports = function earlyAccessContext (req, res, next) { return next(404) } - const earlyAccessPages = req.context.pages + // Get a list of all hidden pages per version + const earlyAccessPageLinks = req.context.pages .filter(page => page.hidden) - .sort((a, b) => b.relativePath.localeCompare(a.relativePath)) - - let urls = [] - earlyAccessPages.forEach(page => { - const pageUrls = page.permalinks.map(permalink => permalink.href) - urls = urls.concat(pageUrls) - }) - - const earlyAccessPageLinks = ` -${urls.map(url => `- [${url}](${url})`).join('\n')} -` + // Do not include early access landing page + .filter(page => page.relativePath !== 'early-access/index.md') + // Create Markdown links + .map(page => { + return page.permalinks.map(permalink => `- [${permalink.href}](${permalink.href})`) + }) + .flat() + // Get links for the current version + .filter(link => link.includes(req.context.currentVersion)) + .join('\n') // Add to the rendering context + // This is only used in the separate EA repo on local development req.context.earlyAccessPageLinks = earlyAccessPageLinks return next() From 5248fac240bcf844abcf7c574df2dd5ad9589dba Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Tue, 1 Dec 2020 15:46:02 -0500 Subject: [PATCH 082/105] chmod a+x script/early-access/symlink-from-local-repo.js --- script/early-access/symlink-from-local-repo.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 script/early-access/symlink-from-local-repo.js diff --git a/script/early-access/symlink-from-local-repo.js b/script/early-access/symlink-from-local-repo.js old mode 100644 new mode 100755 From 77576177b104a940597421ae16fd1ecd44eb6d47 Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Tue, 1 Dec 2020 16:20:09 -0500 Subject: [PATCH 083/105] move early-access link middleware to contextualizers --- .../early-access-links.js} | 0 middleware/index.js | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename middleware/{early-access-context.js => contextualizers/early-access-links.js} (100%) diff --git a/middleware/early-access-context.js b/middleware/contextualizers/early-access-links.js similarity index 100% rename from middleware/early-access-context.js rename to middleware/contextualizers/early-access-links.js diff --git a/middleware/index.js b/middleware/index.js index 4df69bf476..1d3958a96f 100644 --- a/middleware/index.js +++ b/middleware/index.js @@ -62,7 +62,7 @@ module.exports = function (app) { app.use('/csrf', require('./csrf-route')) app.use(require('./archived-enterprise-versions')) app.use(require('./robots')) - app.use(/(\/.*)?\/early-access$/, require('./early-access-context')) + app.use(/(\/.*)?\/early-access$/, require('./contextualizers/early-access-links')) app.use(require('./categories-for-support-team')) app.use(require('./loaderio-verification')) app.get('/_500', asyncMiddleware(require('./trigger-error'))) From 6d82e46580aafff64db7ae6a7a8bebc5b7169b65 Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Wed, 2 Dec 2020 12:30:45 -0500 Subject: [PATCH 084/105] lint --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b9b1f158cd..c53e39baac 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -87,4 +87,4 @@ jobs: name: Run tests run: npx jest tests/${{ matrix.test-group }}/ env: - NODE_OPTIONS: "--max_old_space_size=4096" + NODE_OPTIONS: '--max_old_space_size=4096' From d3d2f0884ad40f9a490d597fcd5d69a071cf63d5 Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Wed, 2 Dec 2020 12:41:26 -0500 Subject: [PATCH 085/105] new script to update data and image paths --- .../update-data-and-image-paths.js | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100755 script/early-access/update-data-and-image-paths.js diff --git a/script/early-access/update-data-and-image-paths.js b/script/early-access/update-data-and-image-paths.js new file mode 100755 index 0000000000..2d4a3ed4e1 --- /dev/null +++ b/script/early-access/update-data-and-image-paths.js @@ -0,0 +1,139 @@ +#!/usr/bin/env node + +// [start-readme] +// +// This script is run on a writer's machine while developing Early Access content locally. It +// updates the data and image paths to either include `early-access` or remove it. +// +// [end-readme] + +const fs = require('fs') +const path = require('path') +const program = require('commander') +const walk = require('walk-sync') +const { escapeRegExp, last } = require('lodash') +const yaml = require('js-yaml') +const patterns = require('../../lib/patterns') +const earlyAccessContent = path.posix.join(process.cwd(), 'content/early-access') +const earlyAccessData = path.posix.join(process.cwd(), 'data/early-access') +const earlyAccessImages = path.posix.join(process.cwd(), 'assets/images/early-access') + +program + .description('Update data and image paths.') + .option('-a, --add', 'Add "early-access" to data and image paths.') + .option('-r, --remove', 'Remove "early-access" from data and image paths.') + .parse(process.argv) + +if (!(program.add || program.remove)) { + console.error('Error! Must specify either `--add` or `--remove`.') + process.exit(1) +} + +// Gather the EA content and data files +const earlyAccessContentAndDataFiles = walk(earlyAccessContent, { includeBasePath: true, directories: false }) + .concat(walk(earlyAccessData, { includeBasePath: true, directories: false })) + +// Update the EA content and data files +earlyAccessContentAndDataFiles + .forEach(file => { + const oldContents = fs.readFileSync(file, 'utf8') + + // Get all the data references in each file that exist in data/early-access + const dataRefs = (oldContents.match(patterns.dataReference) || []) + .filter(dataRef => dataRef.includes('variables') ? checkVariable(dataRef) : checkReusable(dataRef)) + + // Get all the image references in each file that exist in assets/images/early-access + const imageRefs = (oldContents.match(patterns.imagePath) || []) + .filter(imageRef => checkImage(imageRef)) + + const replacements = {} + + if (program.add) { + dataRefs + // Since we're adding early-access to the path, filter for those that do not already include it + .filter(dataRef => !dataRef.includes('data early-access.')) + // Add to the { oldRef: newRef } replacements object + .forEach(dataRef => { + replacements[dataRef] = dataRef.replace(/({% data )(.*)/, '$1early-access.$2') + }) + + imageRefs + // Since we're adding early-access to the path, filter for those that do not already include it + .filter(imageRef => !imageRef.split('/').includes('early-access')) + // Add to the { oldRef: newRef } replacements object + .forEach(imageRef => { + replacements[imageRef] = imageRef.replace('/assets/images/', '/assets/images/early-access/') + }) + } + + if (program.remove) { + dataRefs + // Since we're removing early-access from the path, filter for those that include it + .filter(dataRef => dataRef.includes('{% data early-access.')) + // Add to the { oldRef: newRef } replacements object + .forEach(dataRef => { + replacements[dataRef] = dataRef.replace('early-access.', '') + }) + + imageRefs + // Since we're removing early-access from the path, filter for those that include it + .filter(imageRef => imageRef.split('/').includes('early-access')) + // Add to the { oldRef: newRef } replacements object + .forEach(imageRef => { + replacements[imageRef] = imageRef.replace('/assets/images/early-access/', '/assets/images/') + }) + } + + // Return early if nothing to replace + if (!Object.keys(replacements).length) { + return + } + + // Make the replacement in the content + let newContents = oldContents + Object.entries(replacements).forEach(([oldRef, newRef]) => { + newContents = newContents.replace(new RegExp(escapeRegExp(oldRef), 'g'), newRef) + }) + + // Write the updated content + fs.writeFileSync(file, newContents) + }) + +console.log('Done! Run "git status" in your docs-early-access checkout to see the changes.\n') + +function checkVariable (dataRef) { + // Get the data file path from the data reference + const variablePathArray = dataRef.match(/{% data (.*?) %}/)[1].split('.') + // If early access is part of the path, remove it (since the path below already includes it) + .filter(n => n !== 'early-access') + + const variableKey = last(variablePathArray); variablePathArray.pop() + const variablePath = path.posix.join(earlyAccessData, `${variablePathArray.join('/')}.yml`) + + // If the variable file doesn't exist in data/early-access, exclude it + if (!fs.existsSync(variablePath)) return false + + // If the variable file exists but doesn't have the referenced key, exclude it + const variableFileContent = yaml.safeLoad(fs.readFileSync(variablePath, 'utf8')) + return variableFileContent[variableKey] +} + +function checkReusable (dataRef) { + const reusablePath = dataRef.match(/{% data (.*?) %}/)[1].split('.') + // If early access is part of the path, remove it (since the path below already includes it) + .filter(n => n !== 'early-access') + .join('/') + + // If the reusable file doesn't exist in data/early-access, exclude it + return fs.existsSync(`${path.posix.join(earlyAccessData, reusablePath)}.md`) +} + +function checkImage (imageRef) { + const imagePath = imageRef + .replace('/assets/images/', '') + // If early access is part of the path, remove it (since the path below already includes it) + .replace('early-access', '') + + // If the image file doesn't exist in assets/images/early-access, exclude it + return fs.existsSync(path.posix.join(earlyAccessImages, imagePath)) +} From 5b49fdff588f92da05d3d9192949da049f388033 Mon Sep 17 00:00:00 2001 From: Sarah Schneider Date: Wed, 2 Dec 2020 13:40:24 -0500 Subject: [PATCH 086/105] do not link to early access landing page in sidebar and breadcrumbs --- includes/breadcrumbs.html | 4 ++++ includes/sidebar-specific-product.html | 2 ++ 2 files changed, 6 insertions(+) diff --git a/includes/breadcrumbs.html b/includes/breadcrumbs.html index 4c3afc325b..6d56072949 100644 --- a/includes/breadcrumbs.html +++ b/includes/breadcrumbs.html @@ -1,6 +1,10 @@ diff --git a/includes/sidebar-specific-product.html b/includes/sidebar-specific-product.html index d9d6250704..60ba3316bf 100644 --- a/includes/sidebar-specific-product.html +++ b/includes/sidebar-specific-product.html @@ -9,7 +9,9 @@ {% assign product = siteTree[currentLanguage][currentVersion].products[currentProduct] %} {% include all-products-link %}