Skip to content

Commit c37e810

Browse files
authored
Merge pull request #17310 from github/remove-fpt-helper
Add helper function that removes free-pro-team from URLs
2 parents 325c71d + ba661d6 commit c37e810

32 files changed

+348
-312
lines changed

content/README.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ See the [contributing docs](/CONTRIBUTING.md) for general information about work
2424
- [Escaping single quotes](#escaping-single-quotes)
2525
- [Autogenerated mini TOCs](#autogenerated-mini-tocs)
2626
- [Versioning](#versioning)
27+
- [Free-pro-team vs. GitHub.com versioning](#free-pro-team-vs.-github.com-versioning)
2728
- [Filenames](#filenames)
2829
- [Whitespace control](#whitespace-control)
2930
- [Links and image paths](#links-and-image-paths)
@@ -213,6 +214,12 @@ A content file can have **two** types of versioning:
213214
* Liquid statements in content (**optional**)
214215
* Conditionally render content depending on the current version being viewed. See [contributing/liquid-helpers](../contributing/liquid-helpers.md) for more info. Note Liquid conditionals can also appear in `data` and `include` files.
215216

217+
### Free-pro-team vs. GitHub.com versioning
218+
219+
As of early 2021, the `free-pro-team@latest` version is **only** supported in content files (in both frontmatter and Liquid versioning) and throughout the docs site backend. It is **not** user facing. A helper function called `lib/remove-fpt-from-path.js` removes the version from URLs. Users now select `GitHub.com` in the Article Versions dropdown instead of `Free, Pro, Team`.
220+
221+
The convenience function allows us to continue supporting a consistent versioning structure under-the-hood while not displaying plan information to users that may be potentially confusing.
222+
216223
## Filenames
217224

218225
When adding a new article, make sure the filename is a [kebab-cased](https://en.wikipedia.org/wiki/Letter_case#Special_case_styles) version of the title you use in the article's [`title`](#title) frontmatter. This can get tricky when a title has punctuation (such as "GitHub's Billing Plans"). A test will flag any discrepancies between title and filename. To override this requirement for a given article, you can add [`allowTitleToDifferFromFilename`](#allowtitletodifferfromfilename) frontmatter.
@@ -224,7 +231,7 @@ When using Liquid conditionals in lists or tables, you can use [whitespace contr
224231
Just add a hyphen on either the left, right, or both sides to indicate that there should be no newline on that side. For example, this statement removes a newline on the left side:
225232

226233
```
227-
{%- if page.version == 'dotcom' %}
234+
{%- if currentVersion == 'free-pro-team@latest' %}
228235
```
229236

230237
These characters are especially important in [index pages](#index-pages) comprised of list items.
@@ -238,9 +245,9 @@ For example, if you include the following link in a content file:
238245
```
239246
/github/writing-on-github/creating-a-saved-reply
240247
```
241-
When viewed on GitHub.com docs, the link gets rendered with the language code and version:
248+
When viewed on GitHub.com docs, the link gets rendered with the language code:
242249
```
243-
/en/free-pro-team@latest/github/writing-on-github/creating-a-saved-reply
250+
/en/github/writing-on-github/creating-a-saved-reply
244251
```
245252
and when viewed on GitHub Enterprise Server docs, the version is included as well:
246253
```

contributing/development.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ For more detailed instructions, please see this [VS Code recipe](https://github.
4848

4949
While running the local server, you can visit [localhost:4000/dev-toc](http://localhost:4000/dev-toc) to view a top-level TOC of all the content in the site. This page is not available on https://docs.github.com. It was created for internal GitHub writers' use.
5050

51-
At the `/dev-toc` path, you'll see a list of available versions. Click a version, and a list of products will appear. Note that the TOC content is versioned. If you are viewing `free-pro-team@latest` and you click the `Enterprise Admin` product, it will be empty, because there isn't any Admin content available on that version.
51+
At the `/dev-toc` path, you'll see a list of available versions. Click a version, and a list of products will appear. Note that the TOC content is versioned. If you are viewing the `GitHub.com` version and you click the `Enterprise Admin` product, it will be empty, because there isn't any Admin content available on that version.
5252

5353
## Site structure
5454

contributing/permalinks.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,16 @@ Because the site is dynamic, it does not build HTML files for each different ver
44

55
For example, an article that is available in currently supported versions will have permalink URLs like the following:
66

7-
* `/en/free-pro-team@latest/github/getting-started-with-github/set-up-git`
7+
* `/en/github/getting-started-with-github/set-up-git`
88
* `/en/enterprise-server@2.22/github/getting-started-with-github/set-up-git`
99
* `/en/enterprise-server@2.21/github/getting-started-with-github/set-up-git`
1010
* `/en/enterprise-server@2.20/github/getting-started-with-github/set-up-git`
1111
* `/en/enterprise-server@2.19/github/getting-started-with-github/set-up-git`
1212

1313
An article that is not available in Enterprise will have just one permalink:
1414

15-
* `/en/free-pro-team@latest/github/getting-started-with-github/set-up-git`
15+
* `/en/github/getting-started-with-github/set-up-git`
1616

1717
**If you are a content contributor:** You don't need to worry about supported versions when adding a link to a document. Following the examples above, if you want to reference an article you can just use its relative location:
1818

19-
* `/github/getting-started-with-github/set-up-git`
20-
21-
*(Please note that using a hard-coded link or supported version will result in an error when your submitted PR is automatically tested)*
19+
* `/github/getting-started-with-github/set-up-git`

lib/all-products.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const yaml = require('js-yaml')
77
const contentDir = path.join(process.cwd(), 'content')
88
const frontmatter = require('@github-docs/frontmatter')
99
const getApplicableVersions = require('./get-applicable-versions')
10+
const removeFPTFromPath = require('./remove-fpt-from-path')
1011

1112
// the product order is determined by data/products.yml
1213
const productsFile = path.join(process.cwd(), 'data/products.yml')
@@ -46,7 +47,7 @@ sortedProductIds.forEach(productId => {
4647
const toc = slash(path.join(dir, 'index.md'))
4748
const { data } = frontmatter(fs.readFileSync(toc, 'utf8'))
4849
const applicableVersions = getApplicableVersions(data.versions, toc)
49-
const href = slash(path.join('/', applicableVersions[0], productId))
50+
const href = removeFPTFromPath(slash(path.join('/', applicableVersions[0], productId)))
5051

5152
internalProducts[productId] = {
5253
id: productId,

lib/all-versions.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ const versionDelimiter = '@'
77
const latestNonNumberedRelease = 'latest'
88

99
const plans = [
10-
{
10+
{ // free-pro-team is **not** a user-facing version and is stripped from URLs.
11+
// See lib/remove-fpt-from-path.js for details.
1112
plan: 'free-pro-team',
12-
planTitle: 'Free, Pro, and Team',
13+
planTitle: 'GitHub.com',
1314
releases: [latestNonNumberedRelease],
1415
latestRelease: latestNonNumberedRelease,
1516
nonEnterpriseDefault: true, // permanent way to refer to this plan if the name changes

lib/get-link-data.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const path = require('path')
22
const findPage = require('./find-page')
33
const nonEnterpriseDefaultVersion = require('./non-enterprise-default-version')
4+
const removeFPTFromPath = require('./remove-fpt-from-path')
45

56
// rawLinks is an array of paths: [ '/foo' ]
67
// we need to convert it to an array of localized objects: [ { href: '/en/foo', title: 'Foo', intro: 'Description here' } ]
@@ -12,7 +13,7 @@ module.exports = async (rawLinks, context, additionalProperties = []) => {
1213
for (const link of rawLinks) {
1314
const linkPath = link.href || link
1415
const version = context.currentVersion === 'homepage' ? nonEnterpriseDefaultVersion : context.currentVersion
15-
const href = path.join('/', context.currentLanguage, version, linkPath)
16+
const href = removeFPTFromPath(path.join('/', context.currentLanguage, version, linkPath))
1617

1718
const linkedPage = findPage(href, context.pages, context.redirects)
1819
if (!linkedPage) continue

lib/path-utils.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,13 @@ function getProductStringFromPath (href) {
7676

7777
if (href === '/') return 'homepage'
7878

79-
return allProducts[href.split('/')[2]]
80-
? href.split('/')[2]
81-
: href.split('/')[1]
79+
const pathParts = href.split('/')
80+
81+
if (pathParts.includes('early-access')) return 'early-access'
82+
83+
return allProducts[pathParts[2]]
84+
? pathParts[2]
85+
: pathParts[1]
8286
}
8387

8488
// Return the corresponding object for the product segment in a path

lib/permalink.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ const path = require('path')
33
const patterns = require('./patterns')
44
const getApplicableVersions = require('./get-applicable-versions')
55
const allVersions = require('./all-versions')
6+
const nonEnterpriseDefaultVersion = require('./non-enterprise-default-version')
7+
const removeFPTFromPath = require('./remove-fpt-from-path')
68

79
class Permalink {
810
constructor (languageCode, pageVersion, relativePath, title) {
@@ -13,7 +15,7 @@ class Permalink {
1315

1416
const permalinkSuffix = this.constructor.relativePathToSuffix(relativePath)
1517

16-
this.href = path.join('/', languageCode, pageVersion, permalinkSuffix)
18+
this.href = removeFPTFromPath(path.join('/', languageCode, pageVersion, permalinkSuffix))
1719
.replace(patterns.trailingSlash, '$1')
1820

1921
this.pageVersionTitle = allVersions[pageVersion].versionTitle
@@ -26,9 +28,12 @@ class Permalink {
2628
assert(languageCode, 'languageCode is required')
2729

2830
const applicableVersions = getApplicableVersions(frontmatterVersions, path.join(languageCode, relativePath))
29-
const permalinks = applicableVersions.map(pageVersion => {
30-
return new Permalink(languageCode, pageVersion, relativePath, title)
31-
})
31+
const permalinks = applicableVersions
32+
// skip the Dotcom homepage here because a special homepage permalink is added below
33+
.filter(pageVersion => !(pageVersion === nonEnterpriseDefaultVersion && relativePath === 'index.md'))
34+
.map(pageVersion => {
35+
return new Permalink(languageCode, pageVersion, relativePath, title)
36+
})
3237

3338
// special permalink for homepage
3439
if (relativePath === 'index.md') {

lib/redirects/get-old-paths-from-permalink.js

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
const { latest, lastReleaseWithLegacyFormat } = require('../enterprise-server-releases')
2-
const { getPathWithoutLanguage, getPathWithLanguage } = require('../path-utils')
1+
const { latest, deprecated, lastReleaseWithLegacyFormat } = require('../enterprise-server-releases')
2+
const { getPathWithoutLanguage, getPathWithLanguage, getVersionStringFromPath } = require('../path-utils')
33
const patterns = require('../patterns')
44
const versionSatisfiesRange = require('../version-satisfies-range')
55
const currentlySupportedVersions = Object.keys(require('../all-versions'))
@@ -11,6 +11,15 @@ const nonEnterpriseDefaultVersion = require('../non-enterprise-default-version')
1111
module.exports = function getOldPathsFromPath (currentPath, languageCode, currentVersion) {
1212
const oldPaths = new Set()
1313

14+
const versionFromPath = getVersionStringFromPath(currentPath)
15+
16+
// This only applies to Dotcom paths, so no need to determine whether the version is deprecated
17+
// create old path /free-pro-team@latest/github from new path /github (or from a frontmatter `redirect_from` path like /articles)
18+
if (versionFromPath === 'homepage' || !(currentlySupportedVersions.includes(versionFromPath) || deprecated.includes(versionFromPath)) || (versionFromPath === nonEnterpriseDefaultVersion && !currentPath.includes(nonEnterpriseDefaultVersion))) {
19+
oldPaths.add(currentPath
20+
.replace(`/${languageCode}`, `/${languageCode}/${nonEnterpriseDefaultVersion}`))
21+
}
22+
1423
// ------ BEGIN LEGACY VERSION FORMAT REPLACEMENTS ------//
1524
// These remain relevant to handle legacy-formatted frontmatter redirects
1625
// and archived versions paths.
@@ -57,14 +66,6 @@ module.exports = function getOldPathsFromPath (currentPath, languageCode, curren
5766
// ------ BEGIN MODERN VERSION FORMAT REPLACEMENTS ------//
5867
if (currentlySupportedVersions.includes(currentVersion) || versionSatisfiesRange(currentVersion, `>${lastReleaseWithLegacyFormat}`)) {
5968
(new Set(oldPaths)).forEach(oldPath => {
60-
// create old path /github from new path /free-pro-team@latest/github
61-
oldPaths.add(oldPath
62-
.replace(`/${nonEnterpriseDefaultVersion}`, ''))
63-
64-
// create old path /free-pro-team/github from new path /free-pro-team@latest/github
65-
oldPaths.add(oldPath
66-
.replace('@latest', ''))
67-
6869
// create old path /enterprise/<version> from new path /enterprise-server@<version>
6970
oldPaths.add(oldPath
7071
.replace(/\/enterprise-server@(\d)/, '/enterprise/$1'))

lib/redirects/permalinks.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const supportedVersions = new Set(Object.keys(require('../all-versions')))
44
const getOldPathsFromPermalink = require('./get-old-paths-from-permalink')
55
const { getVersionStringFromPath } = require('../path-utils')
66
const { getNewVersionedPath } = require('../old-versions-utils')
7+
const removeFPTFromPath = require('../remove-fpt-from-path')
78

89
module.exports = function generateRedirectsForPermalinks (permalinks, redirectFrontmatter) {
910
// account for Array or String frontmatter entries
@@ -37,7 +38,7 @@ module.exports = function generateRedirectsForPermalinks (permalinks, redirectFr
3738
// get the old path for the current permalink version
3839
let versionedFrontmatterOldPath = path.join('/', permalink.languageCode, getNewVersionedPath(frontmatterOldPath))
3940
const versionFromPath = getVersionStringFromPath(versionedFrontmatterOldPath)
40-
versionedFrontmatterOldPath = versionedFrontmatterOldPath.replace(versionFromPath, permalink.pageVersion)
41+
versionedFrontmatterOldPath = removeFPTFromPath(versionedFrontmatterOldPath.replace(versionFromPath, permalink.pageVersion))
4142

4243
// add it to the redirects object
4344
redirects[versionedFrontmatterOldPath] = permalink.href

lib/remove-fpt-from-path.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const slash = require('slash')
2+
const nonEnterpriseDefaultVersion = require('./non-enterprise-default-version')
3+
4+
// This is a convenience function to remove free-pro-team@latest from all
5+
// **user-facing** aspects of the site (particularly URLs) while continuing to support
6+
// free-pro-team@latest as a version both in the codebase and in content/data files.
7+
module.exports = function removeFPTFromPath (path) {
8+
return slash(path.replace(`/${nonEnterpriseDefaultVersion}`, ''))
9+
}

lib/rewrite-local-links.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const nonEnterpriseDefaultVersion = require('./non-enterprise-default-version')
99
const allVersions = require('./all-versions')
1010
const supportedVersions = Object.keys(allVersions)
1111
const supportedPlans = Object.values(allVersions).map(v => v.plan)
12+
const removeFPTFromPath = require('./remove-fpt-from-path')
1213

1314
// Content authors write links like `/some/article/path`, but they need to be
1415
// rewritten on the fly to match the current language and page version
@@ -79,7 +80,7 @@ function getNewHref (link, languageCode, version) {
7980
// ------ END ONE-OFF OVERRIDES ------//
8081

8182
// update the version in the link
82-
newHref = newHref.replace(versionFromHref, version)
83+
newHref = removeFPTFromPath(newHref.replace(versionFromHref, version))
8384
}
8485

8586
newHref = newHref.replace(patterns.trailingSlash, '$1')

lib/site-tree.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const allVersions = Object.keys(require('./all-versions'))
66
const { getPathWithoutVersion } = require('./path-utils')
77
const getApplicableVersions = require('./get-applicable-versions')
88
const findPage = require('./find-page')
9+
const removeFPTFromPath = require('./remove-fpt-from-path')
910

1011
// This module builds a localized tree of every page on the site
1112
// It includes single-source pages that have different variants
@@ -46,7 +47,7 @@ module.exports = async function buildSiteTree (pageMap, site, redirects) {
4647
if (!getApplicableVersions(page.versions).includes(version)) return
4748

4849
// item.hrefs have a default version via lib/all-products, so update to the current version
49-
const versionedProductHref = path.join('/', languageCode, version, getPathWithoutVersion(item.href))
50+
const versionedProductHref = removeFPTFromPath(path.join('/', languageCode, version, getPathWithoutVersion(item.href)))
5051

5152
product.categories = buildCategoriesTree(page.tocItems, versionedProductHref, pageMap, redirects, version)
5253

middleware/breadcrumbs.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
const path = require('path')
22
const { getPathWithoutLanguage } = require('../lib/path-utils')
3+
const nonEnterpriseDefaultVersion = require('../lib/non-enterprise-default-version')
4+
const removeFPTFromPath = require('../lib/remove-fpt-from-path')
35

46
module.exports = async (req, res, next) => {
57
if (!req.context.page) return next()
@@ -24,19 +26,22 @@ module.exports = async (req, res, next) => {
2426
}
2527

2628
req.context.breadcrumbs.product = {
27-
href: path.posix.join('/', req.context.currentLanguage, req.context.currentVersion, productPath),
29+
href: removeFPTFromPath(path.posix.join('/', req.context.currentLanguage, req.context.currentVersion, productPath)),
2830
title: product.title
2931
}
3032

31-
// drop the version segment so pathParts now starts with /product
32-
pathParts.shift()
33+
// if this is not FPT, drop the version segment so pathParts now starts with /product
34+
// if this is FPT, there is no version segment so pathParts already starts with /product
35+
if (req.context.currentVersion !== nonEnterpriseDefaultVersion) {
36+
pathParts.shift()
37+
}
3338

3439
if (!pathParts[1]) return next()
3540

3641
// get category path
3742
// e.g., `getting-started-with-github` in /free-pro-team@latest/github/getting-started-with-github
3843
// or /enterprise-server@2.21/github/getting-started-with-github
39-
const categoryPath = path.posix.join('/', req.context.currentLanguage, req.context.currentVersion, productPath, pathParts[1])
44+
const categoryPath = removeFPTFromPath(path.posix.join('/', req.context.currentLanguage, req.context.currentVersion, productPath, pathParts[1]))
4045

4146
const category = product.categories[categoryPath]
4247

middleware/contextualizers/early-access-links.js

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,14 @@ module.exports = function earlyAccessContext (req, res, next) {
77

88
// Get a list of all hidden pages per version
99
const earlyAccessPageLinks = uniq(Object.values(req.context.pages)
10-
.filter(page => page.hidden)
11-
// Do not include early access landing page
12-
.filter(page => page.relativePath !== 'early-access/index.md')
13-
// Create Markdown links
14-
.map(page => {
15-
return page.permalinks.map(permalink => `- [${permalink.title}](${permalink.href})`)
16-
})
10+
.filter(page => page.hidden && page.relativePath.startsWith('early-access') && page.relativePath !== 'early-access/index.md')
11+
.map(page => page.permalinks)
1712
.flat())
1813
// Get links for the current version
19-
.filter(link => link.includes(req.context.currentVersion))
14+
.filter(permalink => req.context.currentVersion === permalink.pageVersion)
2015
.sort()
16+
// Create Markdown links
17+
.map(permalink => `- [${permalink.title}](${permalink.href})`)
2118

2219
// Add to the rendering context
2320
// This is only used in the separate EA repo on local development

middleware/contextualizers/rest.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
const path = require('path')
22
const rest = require('../../lib/rest')
3+
const removeFPTFromPath = require('../../lib/remove-fpt-from-path')
34

45
module.exports = async function (req, res, next) {
56
req.context.rest = rest
67

78
// link to include in `Works with GitHub Apps` notes
89
// e.g. /ja/rest/reference/apps or /en/enterprise/2.20/user/rest/reference/apps
9-
req.context.restGitHubAppsLink = path.join(
10+
req.context.restGitHubAppsLink = removeFPTFromPath(path.join(
1011
'/',
1112
req.context.currentLanguage,
1213
req.context.currentVersion,
1314
'/developers/apps'
14-
)
15+
))
1516

1617
// ignore requests to non-REST reference paths
1718
if (!req.path.includes('rest/reference')) return next()

0 commit comments

Comments
 (0)