From 2343ffca9ded310cd36d3d2302ea677aa6c973d6 Mon Sep 17 00:00:00 2001 From: Rachael Sewell Date: Mon, 28 Sep 2020 11:01:36 -0700 Subject: [PATCH 1/4] Merge main --- .github/workflows/triage-issue-comments.yml | 49 +++++++ .github/workflows/triage-issues.yml | 25 ++++ includes/head.html | 16 +++ javascripts/get-csrf.js | 3 + javascripts/index.js | 6 + javascripts/wrap-code-terms.js | 12 ++ layouts/default.html | 3 + layouts/product-landing.html | 7 + middleware/context.js | 11 ++ middleware/csrf-route.js | 4 + middleware/csrf.js | 5 + stylesheets/tables.scss | 3 + tests/browser/browser.js | 3 + tests/rendering/csrf-route.js | 4 + tests/rendering/events.js | 137 ++++++++++++++++++++ 15 files changed, 288 insertions(+) create mode 100644 .github/workflows/triage-issue-comments.yml create mode 100644 .github/workflows/triage-issues.yml diff --git a/.github/workflows/triage-issue-comments.yml b/.github/workflows/triage-issue-comments.yml new file mode 100644 index 000000000000..9e0a8e409fbf --- /dev/null +++ b/.github/workflows/triage-issue-comments.yml @@ -0,0 +1,49 @@ +name: First responder docs-content +on: + issue_comment: + types: [created] + +jobs: + triage-issue-comments: + runs-on: ubuntu-latest + + steps: + - name: Dump GitHub context + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: echo "$GITHUB_CONTEXT" + - name: Check if the event originated from a team member + uses: actions/github-script@v2.0.0 + id: set-result + with: + github-token: ${{secrets.GITHUB_TOKEN}} + result-encoding: string + script: | + const repo = context.payload.repository.name + const org = context.payload.repository.owner.login + const actor = context.actor + let collaboratorStatus = '' + try { + collaboratorStatus = await github.request('GET /repos/{owner}/{repo}/collaborators/{username}', { + owner: org, + repo: repo, + username: actor + }) + console.log(`This issue was commented on by a Hubber.`) + return 'true' + } catch (error) { + console.log(`This issue was commented on by an external contributor.`) + return 'false' + } + - name: Triage issues commented on by external contributors + uses: andymckay/labeler@v1.0.2 + if: (steps.set-result.outputs.result == 'false') + with: + repo-token: "${{ secrets.GITHUB_TOKEN }}" + add-labels: "triage" + - name: Triage to project board + uses: konradpabjan/actions-add-new-issue-to-column@v1.1 + with: + action-token: ${{ secrets.GITHUB_TOKEN }} + project-url: "https://github.com/github/docs/projects/1" + column-name: "Triage" \ No newline at end of file diff --git a/.github/workflows/triage-issues.yml b/.github/workflows/triage-issues.yml new file mode 100644 index 000000000000..5206347b173b --- /dev/null +++ b/.github/workflows/triage-issues.yml @@ -0,0 +1,25 @@ +name: First responder docs-content +on: + issues: + types: [reopened, opened] + +jobs: + triage_issues: + runs-on: ubuntu-latest + + steps: + - name: Dump GitHub context + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: echo "$GITHUB_CONTEXT" + - name: Label new issues with docs-content-fr + uses: andymckay/labeler@v1.0.2 + with: + repo-token: "${{ secrets.GITHUB_TOKEN }}" + add-labels: "triage" + - name: Triage to project board + uses: konradpabjan/actions-add-new-issue-to-column@v1.1 + with: + action-token: ${{ secrets.GITHUB_TOKEN }} + project-url: "https://github.com/github/docs/projects/1" + column-name: "Triage" diff --git a/includes/head.html b/includes/head.html index 3eba2d16102b..fd747991c381 100644 --- a/includes/head.html +++ b/includes/head.html @@ -1,5 +1,8 @@ +<<<<<<< HEAD +======= +>>>>>>> origin/main {% if error == '404' %}{{ site.data.ui.errors.oops }}{% elsif currentVersion == 'homepage' %}GitHub Documentation{% else %}{{ page.fullTitle }}{% endif %} {% if page.hidden %} {% endif %} @@ -26,4 +29,17 @@ +<<<<<<< HEAD + + {% if page.relativePath contains "managing-your-work-on-github/disabling-project-boards-in-your-organization" %} + + {% if fastlyEnabled %} + + + {% else %} + + {% endif %} + {% endif %} +======= +>>>>>>> origin/main diff --git a/javascripts/get-csrf.js b/javascripts/get-csrf.js index a2ea86db4666..fc469e090fc3 100644 --- a/javascripts/get-csrf.js +++ b/javascripts/get-csrf.js @@ -1,3 +1,5 @@ +<<<<<<< HEAD +======= export async function fillCsrf () { const response = await fetch('/csrf', { method: 'GET', @@ -12,6 +14,7 @@ export async function fillCsrf () { document.querySelector('head').append(meta) } +>>>>>>> origin/main export default function getCsrf () { const csrfEl = document .querySelector('meta[name="csrf-token"]') diff --git a/javascripts/index.js b/javascripts/index.js index 7eda77be376a..9178fef3e81a 100644 --- a/javascripts/index.js +++ b/javascripts/index.js @@ -13,7 +13,10 @@ import print from './print' import localization from './localization' import helpfulness from './helpfulness' import experiment from './experiment' +<<<<<<< HEAD +======= import { fillCsrf } from './get-csrf' +>>>>>>> origin/main document.addEventListener('DOMContentLoaded', () => { displayPlatformSpecificContent() @@ -27,7 +30,10 @@ document.addEventListener('DOMContentLoaded', () => { wrapCodeTerms() print() localization() +<<<<<<< HEAD +======= fillCsrf() +>>>>>>> origin/main helpfulness() experiment() }) diff --git a/javascripts/wrap-code-terms.js b/javascripts/wrap-code-terms.js index e6f30260c6f6..809ac997d445 100644 --- a/javascripts/wrap-code-terms.js +++ b/javascripts/wrap-code-terms.js @@ -1,3 +1,11 @@ +<<<<<<< HEAD +const wordsLongerThan18Chars = /[\w:]{18,}/g +const camelCaseChars = /([a-z])([A-Z])/g +const underscoresAfter12thChar = /([\w:]{12}[^_]*?)_/g + +// insert a tag in code terms that use camelcase or underscore +// inspired by http://heap.ch/blog/2016/01/19/camelwrap/ +======= const wordsLongerThan18Chars = /[\w:/\\]{18,}/g const camelCaseChars = /([a-z])([A-Z])/g const underscoresAfter12thChar = /([\w:]{12}[^_]*?)_/g @@ -6,6 +14,7 @@ const slashChars = /([/\\])/g // This module improves table rendering on reference pages by inserting a // tag in code terms that use camelcase, slashes, or underscores, inspired by // http://heap.ch/blog/2016/01/19/camelwrap/ +>>>>>>> origin/main export default function () { const codeTerms = document.querySelectorAll('#article-contents table code') if (!codeTerms) return @@ -19,8 +28,11 @@ export default function () { // to keep word breaks looking nice, only break on underscores after the 12th char // so `has_organization_projects` will break after `has_organization` instead of after `has_` .replace(underscoresAfter12thChar, '$1_') +<<<<<<< HEAD +======= // Some Actions reference pages have tables with code terms separated by slashes .replace(slashChars, '$1') +>>>>>>> origin/main }) }) } diff --git a/layouts/default.html b/layouts/default.html index cbb745a516a2..b2b769ca5274 100644 --- a/layouts/default.html +++ b/layouts/default.html @@ -1,4 +1,7 @@ +<<<<<<< HEAD +======= +>>>>>>> origin/main {% include head %} diff --git a/layouts/product-landing.html b/layouts/product-landing.html index 5bd1189b1ab1..8020f000c66c 100644 --- a/layouts/product-landing.html +++ b/layouts/product-landing.html @@ -1,10 +1,17 @@ +<<<<<<< HEAD +======= +>>>>>>> origin/main {% include head %} {% include sidebar %} +<<<<<<< HEAD + +======= +>>>>>>> origin/main
{% include header %} diff --git a/middleware/context.js b/middleware/context.js index 9b44ca2c9dc4..c8f6ea2572b5 100644 --- a/middleware/context.js +++ b/middleware/context.js @@ -55,6 +55,17 @@ module.exports = async function contextualize (req, res, next) { req.context.siteTree = siteTree req.context.pages = pages +<<<<<<< HEAD + // To securely accept data from end users, + // we need to validate that they were actually on a docs page first. + // We'll put this token in the and call on it when we send user data + // to the docs server, so we know the request came from someone on the page. + req.context.csrfToken = req.csrfToken() + req.context.fastlyEnabled = process.env.NODE_ENV === 'production' && + req.hostname === 'docs.github.com' + +======= +>>>>>>> origin/main return next() } diff --git a/middleware/csrf-route.js b/middleware/csrf-route.js index c884f294d83e..c2da4f9b52ab 100644 --- a/middleware/csrf-route.js +++ b/middleware/csrf-route.js @@ -8,7 +8,11 @@ router.get('/', (req, res) => { 'surrogate-control': 'private, no-store', 'cache-control': 'private, no-store' }) +<<<<<<< HEAD + res.send(``) +======= res.json({ token: req.csrfToken() }) +>>>>>>> origin/main }) module.exports = router diff --git a/middleware/csrf.js b/middleware/csrf.js index 914546178b5a..d7b33e76bad1 100644 --- a/middleware/csrf.js +++ b/middleware/csrf.js @@ -1,4 +1,9 @@ module.exports = require('csurf')({ cookie: require('../lib/cookie-settings'), +<<<<<<< HEAD + ignoreMethods: ['GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'] + // TODO CSRF edit this to include POST and PUT to require it +======= ignoreMethods: ['GET', 'HEAD', 'OPTIONS'] +>>>>>>> origin/main }) diff --git a/stylesheets/tables.scss b/stylesheets/tables.scss index cc20e3115d89..8e80294feb1d 100644 --- a/stylesheets/tables.scss +++ b/stylesheets/tables.scss @@ -2,10 +2,13 @@ border-collapse: collapse; position: relative; font-size: 90%; +<<<<<<< HEAD +======= /* We want to keep table-layout: auto so that column widths dynamically adjust; otherwise entries get needlessly smushed into narrow columns. As a workaround, we use javascripts/wrap-code-terms.js to prevent some reference table content from expanding beyond the horizontal boundaries of the parent element. */ +>>>>>>> origin/main table-layout: auto; code { diff --git a/tests/browser/browser.js b/tests/browser/browser.js index bc133e09f8cb..c2f39e7c493c 100644 --- a/tests/browser/browser.js +++ b/tests/browser/browser.js @@ -135,6 +135,8 @@ describe('helpfulness', () => { }) }) +<<<<<<< HEAD +======= describe('csrf meta', () => { it('should have a csrf-token meta tag on the page', async () => { await page.goto('http://localhost:4001/en/actions/getting-started-with-github-actions/about-github-actions') @@ -142,6 +144,7 @@ describe('csrf meta', () => { }) }) +>>>>>>> origin/main async function getLocationObject (page) { const location = await page.evaluate(() => { return Promise.resolve(JSON.stringify(window.location, null, 2)) diff --git a/tests/rendering/csrf-route.js b/tests/rendering/csrf-route.js index 62609e7a1dbe..5afcd0518e1a 100644 --- a/tests/rendering/csrf-route.js +++ b/tests/rendering/csrf-route.js @@ -9,7 +9,11 @@ describe('GET /csrf', () => { it('should render a non-cache include for CSRF token', async () => { const res = await request(app).get('/csrf') expect(res.status).toBe(200) +<<<<<<< HEAD + expect(res.text).toMatch(/^$/) +======= expect(res.body).toHaveProperty('token') +>>>>>>> origin/main expect(res.headers['surrogate-control']).toBe('private, no-store') expect(res.headers['cache-control']).toBe('private, no-store') }) diff --git a/tests/rendering/events.js b/tests/rendering/events.js index 0380ca09abe0..2c3f1f130fb3 100644 --- a/tests/rendering/events.js +++ b/tests/rendering/events.js @@ -11,6 +11,11 @@ Airtable.mockImplementation(function () { }) describe('POST /events', () => { +<<<<<<< HEAD + beforeEach(() => { + process.env.AIRTABLE_API_KEY = '$AIRTABLE_API_KEY$' + process.env.AIRTABLE_BASE_KEY = '$AIRTABLE_BASE_KEY$' +======= jest.setTimeout(60 * 1000) let csrfToken = '' @@ -22,12 +27,16 @@ describe('POST /events', () => { agent = request.agent(app) const csrfRes = await agent.get('/csrf') csrfToken = csrfRes.body.token +>>>>>>> origin/main }) afterEach(() => { delete process.env.AIRTABLE_API_KEY delete process.env.AIRTABLE_BASE_KEY +<<<<<<< HEAD +======= csrfToken = '' +>>>>>>> origin/main }) describe('HELPFULNESS', () => { @@ -41,93 +50,163 @@ describe('POST /events', () => { } it('should accept a valid object', () => +<<<<<<< HEAD + request(app) + .post('/events') + .send(example) + .set('Accept', 'application/json') +======= agent .post('/events') .send(example) .set('Accept', 'application/json') .set('csrf-token', csrfToken) +>>>>>>> origin/main .expect(201) ) it('should reject extra properties', () => +<<<<<<< HEAD + request(app) + .post('/events') + .send({ ...example, toothpaste: false }) + .set('Accept', 'application/json') +======= agent .post('/events') .send({ ...example, toothpaste: false }) .set('Accept', 'application/json') .set('csrf-token', csrfToken) +>>>>>>> origin/main .expect(400) ) it('should not accept if type is missing', () => +<<<<<<< HEAD + request(app) + .post('/events') + .send({ ...example, type: undefined }) + .set('Accept', 'application/json') +======= agent .post('/events') .send({ ...example, type: undefined }) .set('Accept', 'application/json') .set('csrf-token', csrfToken) +>>>>>>> origin/main .expect(400) ) it('should not accept if url is missing', () => +<<<<<<< HEAD + request(app) + .post('/events') + .send({ ...example, url: undefined }) + .set('Accept', 'application/json') +======= agent .post('/events') .send({ ...example, url: undefined }) .set('Accept', 'application/json') .set('csrf-token', csrfToken) +>>>>>>> origin/main .expect(400) ) it('should not accept if url is misformatted', () => +<<<<<<< HEAD + request(app) + .post('/events') + .send({ ...example, url: 'examplecom' }) + .set('Accept', 'application/json') +======= agent .post('/events') .send({ ...example, url: 'examplecom' }) .set('Accept', 'application/json') .set('csrf-token', csrfToken) +>>>>>>> origin/main .expect(400) ) it('should not accept if vote is missing', () => +<<<<<<< HEAD + request(app) + .post('/events') + .send({ ...example, vote: undefined }) + .set('Accept', 'application/json') +======= agent .post('/events') .send({ ...example, vote: undefined }) .set('Accept', 'application/json') .set('csrf-token', csrfToken) +>>>>>>> origin/main .expect(400) ) it('should not accept if vote is not boolean', () => +<<<<<<< HEAD + request(app) + .post('/events') + .send({ ...example, vote: 'true' }) + .set('Accept', 'application/json') +======= agent .post('/events') .send({ ...example, vote: 'true' }) .set('Accept', 'application/json') .set('csrf-token', csrfToken) +>>>>>>> origin/main .expect(400) ) it('should not accept if email is misformatted', () => +<<<<<<< HEAD + request(app) + .post('/events') + .send({ ...example, email: 'testexample.com' }) + .set('Accept', 'application/json') +======= agent .post('/events') .send({ ...example, email: 'testexample.com' }) .set('Accept', 'application/json') .set('csrf-token', csrfToken) +>>>>>>> origin/main .expect(400) ) it('should not accept if comment is not string', () => +<<<<<<< HEAD + request(app) + .post('/events') + .send({ ...example, comment: [] }) + .set('Accept', 'application/json') +======= agent .post('/events') .send({ ...example, comment: [] }) .set('Accept', 'application/json') .set('csrf-token', csrfToken) +>>>>>>> origin/main .expect(400) ) it('should not accept if category is not an option', () => +<<<<<<< HEAD + request(app) + .post('/events') + .send({ ...example, category: 'Fabulous' }) + .set('Accept', 'application/json') +======= agent .post('/events') .send({ ...example, category: 'Fabulous' }) .set('Accept', 'application/json') .set('csrf-token', csrfToken) +>>>>>>> origin/main .expect(400) ) }) @@ -142,62 +221,109 @@ describe('POST /events', () => { } it('should accept a valid object', () => +<<<<<<< HEAD + request(app) + .post('/events') + .send(example) + .set('Accept', 'application/json') +======= agent .post('/events') .send(example) .set('Accept', 'application/json') .set('csrf-token', csrfToken) +>>>>>>> origin/main .expect(201) ) it('should reject extra fields', () => +<<<<<<< HEAD + request(app) + .post('/events') + .send({ ...example, toothpaste: false }) + .set('Accept', 'application/json') +======= agent .post('/events') .send({ ...example, toothpaste: false }) .set('Accept', 'application/json') .set('csrf-token', csrfToken) +>>>>>>> origin/main .expect(400) ) it('should require a long unique user-id', () => +<<<<<<< HEAD + request(app) + .post('/events') + .send({ ...example, 'user-id': 'short' }) + .set('Accept', 'application/json') +======= agent .post('/events') .send({ ...example, 'user-id': 'short' }) .set('Accept', 'application/json') .set('csrf-token', csrfToken) +>>>>>>> origin/main .expect(400) ) it('should require a test', () => +<<<<<<< HEAD + request(app) + .post('/events') + .send({ ...example, test: undefined }) + .set('Accept', 'application/json') +======= agent .post('/events') .send({ ...example, test: undefined }) .set('Accept', 'application/json') .set('csrf-token', csrfToken) +>>>>>>> origin/main .expect(400) ) it('should require a valid group', () => +<<<<<<< HEAD + request(app) + .post('/events') + .send({ ...example, group: 'revolution' }) + .set('Accept', 'application/json') +======= agent .post('/events') .send({ ...example, group: 'revolution' }) .set('Accept', 'application/json') .set('csrf-token', csrfToken) +>>>>>>> origin/main .expect(400) ) it('should default the success field', () => +<<<<<<< HEAD + request(app) + .post('/events') + .send({ ...example, success: undefined }) + .set('Accept', 'application/json') +======= agent .post('/events') .send({ ...example, success: undefined }) .set('Accept', 'application/json') .set('csrf-token', csrfToken) +>>>>>>> origin/main .expect(201) ) }) }) describe('PUT /events/:id', () => { +<<<<<<< HEAD + beforeEach(() => { + process.env.AIRTABLE_API_KEY = '$AIRTABLE_API_KEY$' + process.env.AIRTABLE_BASE_KEY = '$AIRTABLE_BASE_KEY$' +======= jest.setTimeout(60 * 1000) let csrfToken = '' @@ -209,12 +335,16 @@ describe('PUT /events/:id', () => { agent = request.agent(app) const csrfRes = await agent.get('/csrf') csrfToken = csrfRes.body.token +>>>>>>> origin/main }) afterEach(() => { delete process.env.AIRTABLE_API_KEY delete process.env.AIRTABLE_BASE_KEY +<<<<<<< HEAD +======= csrfToken = '' +>>>>>>> origin/main }) const example = { @@ -227,11 +357,18 @@ describe('PUT /events/:id', () => { } it('should update an existing HELPFULNESS event', () => +<<<<<<< HEAD + request(app) + .put('/events/TESTID') + .send(example) + .set('Accept', 'application/json') +======= agent .put('/events/TESTID') .send(example) .set('Accept', 'application/json') .set('csrf-token', csrfToken) +>>>>>>> origin/main .expect(200) ) }) From eda7057a803ecd36d113c4d876fdecaa7072884c Mon Sep 17 00:00:00 2001 From: Rachael Sewell Date: Mon, 28 Sep 2020 11:13:35 -0700 Subject: [PATCH 2/4] revert files --- includes/head.html | 16 ---- javascripts/get-csrf.js | 3 - javascripts/index.js | 6 -- javascripts/wrap-code-terms.js | 12 --- layouts/default.html | 3 - layouts/product-landing.html | 7 -- middleware/context.js | 11 --- middleware/csrf-route.js | 4 - middleware/csrf.js | 5 -- stylesheets/tables.scss | 3 - tests/browser/browser.js | 3 - tests/rendering/csrf-route.js | 4 - tests/rendering/events.js | 137 --------------------------------- 13 files changed, 214 deletions(-) diff --git a/includes/head.html b/includes/head.html index fd747991c381..3eba2d16102b 100644 --- a/includes/head.html +++ b/includes/head.html @@ -1,8 +1,5 @@ -<<<<<<< HEAD -======= ->>>>>>> origin/main {% if error == '404' %}{{ site.data.ui.errors.oops }}{% elsif currentVersion == 'homepage' %}GitHub Documentation{% else %}{{ page.fullTitle }}{% endif %} {% if page.hidden %} {% endif %} @@ -29,17 +26,4 @@ -<<<<<<< HEAD - - {% if page.relativePath contains "managing-your-work-on-github/disabling-project-boards-in-your-organization" %} - - {% if fastlyEnabled %} - - - {% else %} - - {% endif %} - {% endif %} -======= ->>>>>>> origin/main diff --git a/javascripts/get-csrf.js b/javascripts/get-csrf.js index fc469e090fc3..a2ea86db4666 100644 --- a/javascripts/get-csrf.js +++ b/javascripts/get-csrf.js @@ -1,5 +1,3 @@ -<<<<<<< HEAD -======= export async function fillCsrf () { const response = await fetch('/csrf', { method: 'GET', @@ -14,7 +12,6 @@ export async function fillCsrf () { document.querySelector('head').append(meta) } ->>>>>>> origin/main export default function getCsrf () { const csrfEl = document .querySelector('meta[name="csrf-token"]') diff --git a/javascripts/index.js b/javascripts/index.js index 9178fef3e81a..7eda77be376a 100644 --- a/javascripts/index.js +++ b/javascripts/index.js @@ -13,10 +13,7 @@ import print from './print' import localization from './localization' import helpfulness from './helpfulness' import experiment from './experiment' -<<<<<<< HEAD -======= import { fillCsrf } from './get-csrf' ->>>>>>> origin/main document.addEventListener('DOMContentLoaded', () => { displayPlatformSpecificContent() @@ -30,10 +27,7 @@ document.addEventListener('DOMContentLoaded', () => { wrapCodeTerms() print() localization() -<<<<<<< HEAD -======= fillCsrf() ->>>>>>> origin/main helpfulness() experiment() }) diff --git a/javascripts/wrap-code-terms.js b/javascripts/wrap-code-terms.js index 809ac997d445..e6f30260c6f6 100644 --- a/javascripts/wrap-code-terms.js +++ b/javascripts/wrap-code-terms.js @@ -1,11 +1,3 @@ -<<<<<<< HEAD -const wordsLongerThan18Chars = /[\w:]{18,}/g -const camelCaseChars = /([a-z])([A-Z])/g -const underscoresAfter12thChar = /([\w:]{12}[^_]*?)_/g - -// insert a tag in code terms that use camelcase or underscore -// inspired by http://heap.ch/blog/2016/01/19/camelwrap/ -======= const wordsLongerThan18Chars = /[\w:/\\]{18,}/g const camelCaseChars = /([a-z])([A-Z])/g const underscoresAfter12thChar = /([\w:]{12}[^_]*?)_/g @@ -14,7 +6,6 @@ const slashChars = /([/\\])/g // This module improves table rendering on reference pages by inserting a // tag in code terms that use camelcase, slashes, or underscores, inspired by // http://heap.ch/blog/2016/01/19/camelwrap/ ->>>>>>> origin/main export default function () { const codeTerms = document.querySelectorAll('#article-contents table code') if (!codeTerms) return @@ -28,11 +19,8 @@ export default function () { // to keep word breaks looking nice, only break on underscores after the 12th char // so `has_organization_projects` will break after `has_organization` instead of after `has_` .replace(underscoresAfter12thChar, '$1_') -<<<<<<< HEAD -======= // Some Actions reference pages have tables with code terms separated by slashes .replace(slashChars, '$1') ->>>>>>> origin/main }) }) } diff --git a/layouts/default.html b/layouts/default.html index b2b769ca5274..cbb745a516a2 100644 --- a/layouts/default.html +++ b/layouts/default.html @@ -1,7 +1,4 @@ -<<<<<<< HEAD -======= ->>>>>>> origin/main {% include head %} diff --git a/layouts/product-landing.html b/layouts/product-landing.html index 8020f000c66c..5bd1189b1ab1 100644 --- a/layouts/product-landing.html +++ b/layouts/product-landing.html @@ -1,17 +1,10 @@ -<<<<<<< HEAD -======= ->>>>>>> origin/main {% include head %} {% include sidebar %} -<<<<<<< HEAD - -======= ->>>>>>> origin/main
{% include header %} diff --git a/middleware/context.js b/middleware/context.js index c8f6ea2572b5..9b44ca2c9dc4 100644 --- a/middleware/context.js +++ b/middleware/context.js @@ -55,17 +55,6 @@ module.exports = async function contextualize (req, res, next) { req.context.siteTree = siteTree req.context.pages = pages -<<<<<<< HEAD - // To securely accept data from end users, - // we need to validate that they were actually on a docs page first. - // We'll put this token in the and call on it when we send user data - // to the docs server, so we know the request came from someone on the page. - req.context.csrfToken = req.csrfToken() - req.context.fastlyEnabled = process.env.NODE_ENV === 'production' && - req.hostname === 'docs.github.com' - -======= ->>>>>>> origin/main return next() } diff --git a/middleware/csrf-route.js b/middleware/csrf-route.js index c2da4f9b52ab..c884f294d83e 100644 --- a/middleware/csrf-route.js +++ b/middleware/csrf-route.js @@ -8,11 +8,7 @@ router.get('/', (req, res) => { 'surrogate-control': 'private, no-store', 'cache-control': 'private, no-store' }) -<<<<<<< HEAD - res.send(``) -======= res.json({ token: req.csrfToken() }) ->>>>>>> origin/main }) module.exports = router diff --git a/middleware/csrf.js b/middleware/csrf.js index d7b33e76bad1..914546178b5a 100644 --- a/middleware/csrf.js +++ b/middleware/csrf.js @@ -1,9 +1,4 @@ module.exports = require('csurf')({ cookie: require('../lib/cookie-settings'), -<<<<<<< HEAD - ignoreMethods: ['GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'] - // TODO CSRF edit this to include POST and PUT to require it -======= ignoreMethods: ['GET', 'HEAD', 'OPTIONS'] ->>>>>>> origin/main }) diff --git a/stylesheets/tables.scss b/stylesheets/tables.scss index 8e80294feb1d..cc20e3115d89 100644 --- a/stylesheets/tables.scss +++ b/stylesheets/tables.scss @@ -2,13 +2,10 @@ border-collapse: collapse; position: relative; font-size: 90%; -<<<<<<< HEAD -======= /* We want to keep table-layout: auto so that column widths dynamically adjust; otherwise entries get needlessly smushed into narrow columns. As a workaround, we use javascripts/wrap-code-terms.js to prevent some reference table content from expanding beyond the horizontal boundaries of the parent element. */ ->>>>>>> origin/main table-layout: auto; code { diff --git a/tests/browser/browser.js b/tests/browser/browser.js index c2f39e7c493c..bc133e09f8cb 100644 --- a/tests/browser/browser.js +++ b/tests/browser/browser.js @@ -135,8 +135,6 @@ describe('helpfulness', () => { }) }) -<<<<<<< HEAD -======= describe('csrf meta', () => { it('should have a csrf-token meta tag on the page', async () => { await page.goto('http://localhost:4001/en/actions/getting-started-with-github-actions/about-github-actions') @@ -144,7 +142,6 @@ describe('csrf meta', () => { }) }) ->>>>>>> origin/main async function getLocationObject (page) { const location = await page.evaluate(() => { return Promise.resolve(JSON.stringify(window.location, null, 2)) diff --git a/tests/rendering/csrf-route.js b/tests/rendering/csrf-route.js index 5afcd0518e1a..62609e7a1dbe 100644 --- a/tests/rendering/csrf-route.js +++ b/tests/rendering/csrf-route.js @@ -9,11 +9,7 @@ describe('GET /csrf', () => { it('should render a non-cache include for CSRF token', async () => { const res = await request(app).get('/csrf') expect(res.status).toBe(200) -<<<<<<< HEAD - expect(res.text).toMatch(/^$/) -======= expect(res.body).toHaveProperty('token') ->>>>>>> origin/main expect(res.headers['surrogate-control']).toBe('private, no-store') expect(res.headers['cache-control']).toBe('private, no-store') }) diff --git a/tests/rendering/events.js b/tests/rendering/events.js index 2c3f1f130fb3..0380ca09abe0 100644 --- a/tests/rendering/events.js +++ b/tests/rendering/events.js @@ -11,11 +11,6 @@ Airtable.mockImplementation(function () { }) describe('POST /events', () => { -<<<<<<< HEAD - beforeEach(() => { - process.env.AIRTABLE_API_KEY = '$AIRTABLE_API_KEY$' - process.env.AIRTABLE_BASE_KEY = '$AIRTABLE_BASE_KEY$' -======= jest.setTimeout(60 * 1000) let csrfToken = '' @@ -27,16 +22,12 @@ describe('POST /events', () => { agent = request.agent(app) const csrfRes = await agent.get('/csrf') csrfToken = csrfRes.body.token ->>>>>>> origin/main }) afterEach(() => { delete process.env.AIRTABLE_API_KEY delete process.env.AIRTABLE_BASE_KEY -<<<<<<< HEAD -======= csrfToken = '' ->>>>>>> origin/main }) describe('HELPFULNESS', () => { @@ -50,163 +41,93 @@ describe('POST /events', () => { } it('should accept a valid object', () => -<<<<<<< HEAD - request(app) - .post('/events') - .send(example) - .set('Accept', 'application/json') -======= agent .post('/events') .send(example) .set('Accept', 'application/json') .set('csrf-token', csrfToken) ->>>>>>> origin/main .expect(201) ) it('should reject extra properties', () => -<<<<<<< HEAD - request(app) - .post('/events') - .send({ ...example, toothpaste: false }) - .set('Accept', 'application/json') -======= agent .post('/events') .send({ ...example, toothpaste: false }) .set('Accept', 'application/json') .set('csrf-token', csrfToken) ->>>>>>> origin/main .expect(400) ) it('should not accept if type is missing', () => -<<<<<<< HEAD - request(app) - .post('/events') - .send({ ...example, type: undefined }) - .set('Accept', 'application/json') -======= agent .post('/events') .send({ ...example, type: undefined }) .set('Accept', 'application/json') .set('csrf-token', csrfToken) ->>>>>>> origin/main .expect(400) ) it('should not accept if url is missing', () => -<<<<<<< HEAD - request(app) - .post('/events') - .send({ ...example, url: undefined }) - .set('Accept', 'application/json') -======= agent .post('/events') .send({ ...example, url: undefined }) .set('Accept', 'application/json') .set('csrf-token', csrfToken) ->>>>>>> origin/main .expect(400) ) it('should not accept if url is misformatted', () => -<<<<<<< HEAD - request(app) - .post('/events') - .send({ ...example, url: 'examplecom' }) - .set('Accept', 'application/json') -======= agent .post('/events') .send({ ...example, url: 'examplecom' }) .set('Accept', 'application/json') .set('csrf-token', csrfToken) ->>>>>>> origin/main .expect(400) ) it('should not accept if vote is missing', () => -<<<<<<< HEAD - request(app) - .post('/events') - .send({ ...example, vote: undefined }) - .set('Accept', 'application/json') -======= agent .post('/events') .send({ ...example, vote: undefined }) .set('Accept', 'application/json') .set('csrf-token', csrfToken) ->>>>>>> origin/main .expect(400) ) it('should not accept if vote is not boolean', () => -<<<<<<< HEAD - request(app) - .post('/events') - .send({ ...example, vote: 'true' }) - .set('Accept', 'application/json') -======= agent .post('/events') .send({ ...example, vote: 'true' }) .set('Accept', 'application/json') .set('csrf-token', csrfToken) ->>>>>>> origin/main .expect(400) ) it('should not accept if email is misformatted', () => -<<<<<<< HEAD - request(app) - .post('/events') - .send({ ...example, email: 'testexample.com' }) - .set('Accept', 'application/json') -======= agent .post('/events') .send({ ...example, email: 'testexample.com' }) .set('Accept', 'application/json') .set('csrf-token', csrfToken) ->>>>>>> origin/main .expect(400) ) it('should not accept if comment is not string', () => -<<<<<<< HEAD - request(app) - .post('/events') - .send({ ...example, comment: [] }) - .set('Accept', 'application/json') -======= agent .post('/events') .send({ ...example, comment: [] }) .set('Accept', 'application/json') .set('csrf-token', csrfToken) ->>>>>>> origin/main .expect(400) ) it('should not accept if category is not an option', () => -<<<<<<< HEAD - request(app) - .post('/events') - .send({ ...example, category: 'Fabulous' }) - .set('Accept', 'application/json') -======= agent .post('/events') .send({ ...example, category: 'Fabulous' }) .set('Accept', 'application/json') .set('csrf-token', csrfToken) ->>>>>>> origin/main .expect(400) ) }) @@ -221,109 +142,62 @@ describe('POST /events', () => { } it('should accept a valid object', () => -<<<<<<< HEAD - request(app) - .post('/events') - .send(example) - .set('Accept', 'application/json') -======= agent .post('/events') .send(example) .set('Accept', 'application/json') .set('csrf-token', csrfToken) ->>>>>>> origin/main .expect(201) ) it('should reject extra fields', () => -<<<<<<< HEAD - request(app) - .post('/events') - .send({ ...example, toothpaste: false }) - .set('Accept', 'application/json') -======= agent .post('/events') .send({ ...example, toothpaste: false }) .set('Accept', 'application/json') .set('csrf-token', csrfToken) ->>>>>>> origin/main .expect(400) ) it('should require a long unique user-id', () => -<<<<<<< HEAD - request(app) - .post('/events') - .send({ ...example, 'user-id': 'short' }) - .set('Accept', 'application/json') -======= agent .post('/events') .send({ ...example, 'user-id': 'short' }) .set('Accept', 'application/json') .set('csrf-token', csrfToken) ->>>>>>> origin/main .expect(400) ) it('should require a test', () => -<<<<<<< HEAD - request(app) - .post('/events') - .send({ ...example, test: undefined }) - .set('Accept', 'application/json') -======= agent .post('/events') .send({ ...example, test: undefined }) .set('Accept', 'application/json') .set('csrf-token', csrfToken) ->>>>>>> origin/main .expect(400) ) it('should require a valid group', () => -<<<<<<< HEAD - request(app) - .post('/events') - .send({ ...example, group: 'revolution' }) - .set('Accept', 'application/json') -======= agent .post('/events') .send({ ...example, group: 'revolution' }) .set('Accept', 'application/json') .set('csrf-token', csrfToken) ->>>>>>> origin/main .expect(400) ) it('should default the success field', () => -<<<<<<< HEAD - request(app) - .post('/events') - .send({ ...example, success: undefined }) - .set('Accept', 'application/json') -======= agent .post('/events') .send({ ...example, success: undefined }) .set('Accept', 'application/json') .set('csrf-token', csrfToken) ->>>>>>> origin/main .expect(201) ) }) }) describe('PUT /events/:id', () => { -<<<<<<< HEAD - beforeEach(() => { - process.env.AIRTABLE_API_KEY = '$AIRTABLE_API_KEY$' - process.env.AIRTABLE_BASE_KEY = '$AIRTABLE_BASE_KEY$' -======= jest.setTimeout(60 * 1000) let csrfToken = '' @@ -335,16 +209,12 @@ describe('PUT /events/:id', () => { agent = request.agent(app) const csrfRes = await agent.get('/csrf') csrfToken = csrfRes.body.token ->>>>>>> origin/main }) afterEach(() => { delete process.env.AIRTABLE_API_KEY delete process.env.AIRTABLE_BASE_KEY -<<<<<<< HEAD -======= csrfToken = '' ->>>>>>> origin/main }) const example = { @@ -357,18 +227,11 @@ describe('PUT /events/:id', () => { } it('should update an existing HELPFULNESS event', () => -<<<<<<< HEAD - request(app) - .put('/events/TESTID') - .send(example) - .set('Accept', 'application/json') -======= agent .put('/events/TESTID') .send(example) .set('Accept', 'application/json') .set('csrf-token', csrfToken) ->>>>>>> origin/main .expect(200) ) }) From 055f434b349b6c0139b05cee624b5f6e0a3049a2 Mon Sep 17 00:00:00 2001 From: Rachael Sewell Date: Mon, 28 Sep 2020 11:47:36 -0700 Subject: [PATCH 3/4] add pull and stale workflows --- .github/workflows/triage-issue-comments.yml | 9 +++------ .github/workflows/triage-issues.yml | 9 +++------ .github/workflows/triage-pull-requests.yml | 22 +++++++++++++++++++++ .github/workflows/triage-stale-check.yml | 21 ++++++++++++++++++++ 4 files changed, 49 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/triage-pull-requests.yml create mode 100644 .github/workflows/triage-stale-check.yml diff --git a/.github/workflows/triage-issue-comments.yml b/.github/workflows/triage-issue-comments.yml index 9e0a8e409fbf..44e0674291e0 100644 --- a/.github/workflows/triage-issue-comments.yml +++ b/.github/workflows/triage-issue-comments.yml @@ -1,17 +1,14 @@ -name: First responder docs-content +name: Triage new issue comments on: issue_comment: types: [created] jobs: triage-issue-comments: + if: github.repository == 'github/docs' runs-on: ubuntu-latest steps: - - name: Dump GitHub context - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - run: echo "$GITHUB_CONTEXT" - name: Check if the event originated from a team member uses: actions/github-script@v2.0.0 id: set-result @@ -35,7 +32,7 @@ jobs: console.log(`This issue was commented on by an external contributor.`) return 'false' } - - name: Triage issues commented on by external contributors + - name: Label issues with new comments with 'triage' uses: andymckay/labeler@v1.0.2 if: (steps.set-result.outputs.result == 'false') with: diff --git a/.github/workflows/triage-issues.yml b/.github/workflows/triage-issues.yml index 5206347b173b..301592da99c7 100644 --- a/.github/workflows/triage-issues.yml +++ b/.github/workflows/triage-issues.yml @@ -1,18 +1,15 @@ -name: First responder docs-content +name: Triage new issues on: issues: types: [reopened, opened] jobs: triage_issues: + if: github.repository == 'github/docs' runs-on: ubuntu-latest steps: - - name: Dump GitHub context - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - run: echo "$GITHUB_CONTEXT" - - name: Label new issues with docs-content-fr + - name: Label new issues with 'triage' uses: andymckay/labeler@v1.0.2 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/triage-pull-requests.yml b/.github/workflows/triage-pull-requests.yml new file mode 100644 index 000000000000..07ebe4ba0a34 --- /dev/null +++ b/.github/workflows/triage-pull-requests.yml @@ -0,0 +1,22 @@ +name: Triage new pull requests +on: + pull_request: + types: [reopened, opened] + +jobs: + triage_issues: + if: github.repository == 'github/docs' + runs-on: ubuntu-latest + + steps: + - name: Label new pull requests with 'triage' + uses: andymckay/labeler@v1.0.2 + with: + repo-token: "${{ secrets.GITHUB_TOKEN }}" + add-labels: "triage" + - name: Triage to project board + uses: konradpabjan/actions-add-new-issue-to-column@v1.1 + with: + action-token: ${{ secrets.GITHUB_TOKEN }} + project-url: "https://github.com/github/docs/projects/1" + column-name: "Triage" diff --git a/.github/workflows/triage-stale-check.yml b/.github/workflows/triage-stale-check.yml new file mode 100644 index 000000000000..5ad29dc1d96a --- /dev/null +++ b/.github/workflows/triage-stale-check.yml @@ -0,0 +1,21 @@ +name: 14 Days Stale Check +on: + schedule: + - cron: "40 16 * * *" # Run each day at 16:40 UTC / 8:40 PST + +jobs: + stale: + if: github.repository == 'github/docs' + runs-on: ubuntu-latest + + steps: + - uses: actions/stale@v3 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + stale-issue-message: 'This issue is stale because it has been open 60 days with no activity.' + stale-pr-message: 'This PR is stale because it has been open 60 days with no activity.' + days-before-stale: 14 + days-before-close: 7 + stale-issue-label: 'stale' + stale-pr-label: 'stale' + From 9ad2df1e7080b3c7d3e74d068674709927f97578 Mon Sep 17 00:00:00 2001 From: Rachael Sewell Date: Thu, 10 Dec 2020 15:03:01 -0800 Subject: [PATCH 4/4] Update README.md --- content/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/README.md b/content/README.md index 901427d4be71..f70350d0d611 100644 --- a/content/README.md +++ b/content/README.md @@ -1,5 +1,5 @@ # Content - +:octocat: The `/content` directory is where all the site's (English) Markdown content lives! See the [markup reference guide](/contributing/content-markup-reference.md) for more information about supported Markdown features.