Skip to content

Commit c40ac98

Browse files
authored
repo sync
2 parents 484530c + b7739ec commit c40ac98

13 files changed

+190
-13
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
name: Confirm internal staff meant to post in public
2+
3+
on:
4+
issues:
5+
types:
6+
- opened
7+
- reopened
8+
- transferred
9+
pull_request_target:
10+
types:
11+
- opened
12+
- reopened
13+
14+
jobs:
15+
check-team-membership:
16+
runs-on: ubuntu-latest
17+
continue-on-error: true
18+
if: github.repository == 'github/docs'
19+
steps:
20+
- uses: actions/github-script@626af12fe9a53dc2972b48385e7fe7dec79145c9
21+
with:
22+
github-token: ${{ secrets.DOCUBOT_FR_PROJECT_BOARD_WORKFLOWS_REPO_ORG_READ_SCOPES }}
23+
script: |
24+
// Only perform this action with GitHub employees
25+
try {
26+
await github.teams.getMembershipForUserInOrg({
27+
org: 'github',
28+
team_slug: 'employees',
29+
username: context.payload.sender.login,
30+
});
31+
} catch(err) {
32+
// An error will be thrown if the user is not a GitHub employee
33+
// If a user is not a GitHub employee, we should stop here and
34+
// Not send a notification
35+
return
36+
}
37+
38+
// Don't perform this action with Docs team members
39+
try {
40+
await github.teams.getMembershipForUserInOrg({
41+
org: 'github',
42+
team_slug: 'docs',
43+
username: context.payload.sender.login,
44+
});
45+
// If the user is a Docs team member, we should stop here and not send
46+
// a notification
47+
return
48+
} catch(err) {
49+
// An error will be thrown if the user is not a Docs team member
50+
// If a user is not a Docs team member we should continue and send
51+
// the notification
52+
}
53+
54+
const issueNo = context.number || context.issue.number
55+
56+
// Create an issue in our private repo
57+
await github.issues.create({
58+
owner: 'github',
59+
repo: 'docs-internal',
60+
title: `@${context.payload.sender.login} confirm that \#${issueNo} should be in the public github/docs repo`,
61+
body: `@${context.payload.sender.login} opened https://github.com/github/docs/issues/${issueNo} publicly in the github/docs repo, instead of the private github/docs-internal repo.\n\n@${context.payload.sender.login}, please confirm that this belongs in the public repo and that no sensitive information was disclosed by commenting below and closing the issue.\n\nIf this was not intentional and sensitive information was shared, please delete https://github.com/github/docs/issues/${issueNo} and notify us in the \#docs-open-source channel.\n\nThanks! \n\n/cc @github/docs @github/docs-engineering`
62+
});
63+
64+
throw new Error('A Hubber opened an issue on the public github/docs repo');
65+
66+
- name: Send Slack notification if a GitHub employee who isn't on the docs team opens an issue in public
67+
if: ${{ failure() && github.repository == 'github/docs' }}
68+
uses: someimportantcompany/github-actions-slack-message@0b470c14b39da4260ed9e3f9a4f1298a74ccdefd
69+
with:
70+
channel: ${{ secrets.DOCS_OPEN_SOURCE_SLACK_CHANNEL_ID }}
71+
bot-token: ${{ secrets.SLACK_DOCS_BOT_TOKEN }}
72+
text: <@${{github.actor}}> opened https://github.com/github/docs/issues/${{ github.event.number || github.event.issue.number }} publicly on the github/docs repo instead of the private github/docs-internal repo. They have been notified via a new issue in the github/docs-internal repo to confirm this was intentional.

data/ui.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ header:
1717
still in translation. For the most up-to-date and accurate information,
1818
please visit our
1919
<a id="to-english-doc" href="/en">English documentation</a>.
20-
early_access: 👋 This page contains content about an early access feature. Please do not share this URL publicly.
20+
early_access: 📣 Please <b>do not share</b> this URL publicly. This page contains content about an early access feature.
2121
search:
2222
need_help: Need help?
2323
placeholder: Search topics, products...

includes/breadcrumbs.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<nav class="breadcrumbs f5" aria-label="Breadcrumb">
22
{% for breadcrumb in breadcrumbs %}
3-
{% if page.hidden %}
4-
<span class="d-inline-block">{{breadcrumb[1].title}}</span>
3+
{% if breadcrumb[1].href == '' %}
4+
<span>{{breadcrumb[1].title}}</span>
55
{% else %}
66
<a title="{{ breadcrumb[0]}}: {{breadcrumb[1].title}}" href="/{{currentLanguage}}{{breadcrumb[1].href}}" class="d-inline-block {% if breadcrumb[1].href == currentPathWithoutLanguage %}text-gray-light{% endif %}">
77
{{breadcrumb[1].title}}</a>

includes/header-notification.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
{% endif %}
5050

5151
{% if early_access_notification_type %}
52-
<div class="header-notifications text-center f5 bg-blue-1 text-gray-dark py-4 px-6 early_access">
52+
<div class="header-notifications text-center f5 bg-red-1 text-gray-dark py-4 px-6 early_access">
5353
{{ early_access_notification }}
5454
</div>
5555
{% endif %}

middleware/breadcrumbs.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ const path = require('path')
22
const { getPathWithoutLanguage } = require('../lib/path-utils')
33

44
module.exports = async (req, res, next) => {
5+
if (!req.context.page) return next()
6+
if (req.context.page.hidden) return next()
7+
58
req.context.breadcrumbs = {}
69

7-
if (!req.context.page) return next()
10+
// Return an empty object on the landing page
811
if (req.context.page.relativePath === 'index.md') return next()
912

1013
const rawPath = getPathWithoutLanguage(req.path)

middleware/contextualizers/early-access-links.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
1+
const { uniq } = require('lodash')
2+
13
module.exports = function earlyAccessContext (req, res, next) {
24
if (process.env.NODE_ENV === 'production') {
35
return next(404)
46
}
57

68
// Get a list of all hidden pages per version
7-
const earlyAccessPageLinks = req.context.pages
9+
const earlyAccessPageLinks = uniq(Object.values(req.context.pages)
810
.filter(page => page.hidden)
911
// Do not include early access landing page
1012
.filter(page => page.relativePath !== 'early-access/index.md')
1113
// Create Markdown links
1214
.map(page => {
1315
return page.permalinks.map(permalink => `- [${permalink.title}](${permalink.href})`)
1416
})
15-
.flat()
17+
.flat())
1618
// Get links for the current version
1719
.filter(link => link.includes(req.context.currentVersion))
1820
.sort()
+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
const path = require('path')
2+
const { getPathWithoutLanguage } = require('../lib/path-utils')
3+
4+
// Early Access content doesn't conform to the same structure as other products, so we
5+
// can't derive breadcrumbs from the siteTree in the same way. Hence, this separate middleware.
6+
module.exports = async (req, res, next) => {
7+
if (!req.context.page) return next()
8+
if (!req.context.page.hidden) return next()
9+
10+
req.context.breadcrumbs = {}
11+
12+
const earlyAccessProduct = req.context.siteTree[req.language][req.context.currentVersion].products[req.context.currentProduct]
13+
14+
req.context.breadcrumbs.earlyAccessProduct = {
15+
href: '', // no link for the EA product breadcrumb
16+
title: earlyAccessProduct.title
17+
}
18+
19+
const rawPath = getPathWithoutLanguage(req.path)
20+
const pathParts = rawPath.split('/')
21+
22+
// drop first '/'
23+
pathParts.shift()
24+
25+
// drop the version and early-accesss segments so pathParts now starts with /product
26+
pathParts.shift()
27+
pathParts.shift()
28+
29+
// Early Access products do not require an index.md, so look for a matching public product
30+
const product = req.context.siteTree[req.language][req.context.currentVersion].products[pathParts[0]]
31+
32+
if (!product) return next()
33+
34+
req.context.breadcrumbs.product = {
35+
href: '', // no link for the EA product breadcrumbs
36+
title: product.title
37+
}
38+
39+
if (!pathParts[1]) return next()
40+
41+
// get Early Access category path
42+
// e.g., `enforcing-best-practices-with-github-policies` in /free-pro-team@latest/early-access/github/enforcing-best-practices-with-github-policies
43+
const categoryPath = path.posix.join('/', req.context.currentVersion, 'early-access', pathParts[0], pathParts[1])
44+
const category = req.context.pages[path.posix.join('/en', categoryPath)]
45+
46+
if (!category) return next()
47+
48+
req.context.breadcrumbs.category = {
49+
href: categoryPath, // do include a link for EA category breadcrumbs
50+
title: category.shortTitle || category.title
51+
}
52+
53+
if (!pathParts[2]) return next()
54+
55+
// for Early Access purposes, we don't need to differentiate between map topics and articles breadcrumbs
56+
const mapTopicOrArticlePath = path.posix.join(categoryPath, pathParts[2])
57+
const mapTopicOrArticle = req.context.pages[path.posix.join('/en', mapTopicOrArticlePath)]
58+
59+
if (!mapTopicOrArticle) return next()
60+
61+
const mapTopicOrArticleTitle = await mapTopicOrArticle.renderProp('shortTitle', req.context, { textOnly: true, encodeEntities: true })
62+
63+
req.context.breadcrumbs.article = {
64+
href: mapTopicOrArticlePath, // do include a link for EA map topic/article breadcrumbs
65+
title: mapTopicOrArticleTitle
66+
}
67+
68+
return next()
69+
}

middleware/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ module.exports = function (app) {
7373
app.use(require('./contextualizers/rest'))
7474
app.use(require('./contextualizers/webhooks'))
7575
app.use(require('./breadcrumbs'))
76+
app.use(require('./early-access-breadcrumbs'))
7677
app.use(require('./dev-toc'))
7778
app.use(require('./featured-links'))
7879

stylesheets/article.scss

+1-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@
8787
/* Breadcrumbs
8888
------------------------------------------------------------------------------*/
8989

90-
.breadcrumbs a:not(:last-child)::after{
90+
.breadcrumbs a:not(:last-child)::after, .breadcrumbs span:not(:last-child)::after {
9191
content: '/';
9292
color: $gray-400;
9393
padding-right: $spacer-1;

tests/helpers/conditional-runs.js

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const runningActionsOnInternalRepo = process.env.GITHUB_ACTIONS === 'true' && process.env.GITHUB_REPOSITORY === 'github/docs-internal'
2+
3+
const testViaActionsOnly = runningActionsOnInternalRepo ? test : test.skip
4+
const describeViaActionsOnly = runningActionsOnInternalRepo ? describe : describe.skip
5+
6+
module.exports = {
7+
testViaActionsOnly,
8+
describeViaActionsOnly
9+
}

tests/rendering/breadcrumbs.js

+23
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
const { getDOM, getJSON } = require('../helpers/supertest')
22
const nonEnterpriseDefaultVersion = require('../../lib/non-enterprise-default-version')
33

4+
const describeInternalOnly = process.env.GITHUB_REPOSITORY === 'github/docs-internal' ? describe : describe.skip
5+
46
describe('breadcrumbs', () => {
57
jest.setTimeout(300 * 1000)
68

@@ -60,6 +62,27 @@ describe('breadcrumbs', () => {
6062
})
6163
})
6264

65+
describeInternalOnly('early access rendering', () => {
66+
test('top-level product pages have breadcrumbs', async () => {
67+
const $ = await getDOM('/early-access/github/articles/using-gist-playground')
68+
expect($('.breadcrumbs')).toHaveLength(1)
69+
})
70+
71+
test('early access article pages have breadcrumbs with product, category, and article', async () => {
72+
const $ = await getDOM('/early-access/github/enforcing-best-practices-with-github-policies/about-github-policies')
73+
const $breadcrumbSpans = $('.breadcrumbs span')
74+
const $breadcrumbLinks = $('.breadcrumbs a')
75+
76+
expect($breadcrumbSpans).toHaveLength(2)
77+
expect($breadcrumbLinks).toHaveLength(2)
78+
expect($breadcrumbSpans.eq(0).text()).toBe('Early Access documentation')
79+
expect($breadcrumbSpans.eq(1).text()).toBe('GitHub.com')
80+
expect($breadcrumbLinks.eq(0).attr('title')).toBe('category: Enforcing best practices with GitHub Policies')
81+
expect($breadcrumbLinks.eq(1).attr('title')).toBe('article: About GitHub Policies')
82+
expect($breadcrumbLinks.eq(1).hasClass('text-gray-light')).toBe(true)
83+
})
84+
})
85+
6386
describe('context.breadcrumbs object', () => {
6487
test('works on product index pages', async () => {
6588
const breadcrumbs = await getJSON('/en/github?json=breadcrumbs')

tests/rendering/server.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const lodash = require('lodash')
22
const enterpriseServerReleases = require('../../lib/enterprise-server-releases')
33
const { get, getDOM, head } = require('../helpers/supertest')
4+
const { describeViaActionsOnly } = require('../helpers/conditional-runs')
45
const path = require('path')
56
const nonEnterpriseDefaultVersion = require('../../lib/non-enterprise-default-version')
67
const { loadPages } = require('../../lib/pages')
@@ -356,7 +357,7 @@ describe('server', () => {
356357
})
357358
})
358359

359-
describe.skip('Early Access articles', () => {
360+
describeViaActionsOnly('Early Access articles', () => {
360361
let hiddenPageHrefs, hiddenPages
361362

362363
beforeAll(async (done) => {

tests/unit/early-access.js

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
const fs = require('fs').promises
22
const path = require('path')
3-
4-
const { GITHUB_ACTIONS, GITHUB_REPOSITORY } = process.env
5-
const runningActionsOnInternalRepo = GITHUB_ACTIONS === 'true' && GITHUB_REPOSITORY === 'github/docs-internal'
6-
const testViaActionsOnly = runningActionsOnInternalRepo ? test : test.skip
3+
const { testViaActionsOnly } = require('../helpers/conditional-runs')
74

85
describe('cloning early-access', () => {
96
testViaActionsOnly('the content directory exists', async () => {

0 commit comments

Comments
 (0)