Files
shields/dangerfile.js
Paul Melnikow d8ce045ead Adopt Gatsby (#2906)
While Next.js can handle static sites, we've had a few issues with it, notably a performance hit at runtime and some bugginess around routing and SSR. Gatsby being fully intended for high-performance static sites makes it a great technical fit for the Shields frontend. The `createPages()` API should be a really nice way to add a page for each service family, for example.

This migrates the frontend from Next.js to Gatsby. Gatsby is a powerful tool, which has a bit of downside as there's a lot to dig through. Overall I found configuration easier than Next.js. There are a lot of plugins and for the most part they worked out of the box. The documentation is good.

Links are cleaner now: there is no #. This will break old links though perhaps we could add some redirection to help with that. The only one I’m really concerned about `/#/endpoint`. I’m not sure if folks are deep-linking to the category pages.

There are a lot of enhancements we could add, in order to speed up the site even more. In particular we could think about inlining the SVGs rather than making separate requests for each one.

While Gatsby recommends GraphQL, it's not required. To keep things simple and reduce the learning curve, I did not use it here.

Close #1943 
Fix #2837 Fix #2616
2019-02-06 16:37:55 -05:00

187 lines
5.3 KiB
JavaScript

'use strict'
// Have you identified a contributing guideline that should be included here?
// Please open a pull request!
//
// To test changes to this file, pick a PR to test against, then run
// `./node_modules/.bin/danger pr pr-url`
// Note that the line numbers in the runtime errors are incorrect.
// To test changes locally:
// DANGER_GITHUB_API_TOKEN=your-github-api-token npm run danger -- pr https://github.com/badges/shields/pull/2665
const { danger, fail, message, warn } = require('danger')
const chainsmoker = require('chainsmoker')
const { default: noTestShortcuts } = require('danger-plugin-no-test-shortcuts')
const fileMatch = chainsmoker({
created: danger.git.created_files,
modified: danger.git.modified_files,
createdOrModified: danger.git.modified_files.concat(danger.git.created_files),
deleted: danger.git.deleted_files,
})
const documentation = fileMatch(
'**/*.md',
'lib/all-badge-examples.js',
'frontend/components/usage.js'
)
const server = fileMatch('server.js')
const serverTests = fileMatch('server.spec.js')
const helpers = fileMatch(
'lib/**/*.js',
'!**/*.spec.js',
'!lib/all-badge-examples.js'
)
const logos = fileMatch('logo/*.svg')
const helperTests = fileMatch('lib/**/*.spec.js')
const packageJson = fileMatch('package.json')
const packageLock = fileMatch('package-lock.json')
const secretsDocs = fileMatch('doc/server-secrets.md')
const capitals = fileMatch('**/*[A-Z]*.js')
const underscores = fileMatch('**/*_*.js')
const targetBranch = danger.github.pr.base.ref
message(
[
':sparkles: Thanks for your contribution to Shields, ',
`@${danger.github.pr.user.login}!`,
].join('')
)
if (targetBranch !== 'master') {
const message = `This PR targets \`${targetBranch}\``
const idea = 'It is likely that the target branch should be `master`'
warn(`${message} - <i>${idea}</i>`)
}
if (documentation.createdOrModified) {
message(
[
'Thanks for contributing to our documentation. ',
'We :heart: our [documentarians](http://www.writethedocs.org/)!',
].join('')
)
}
if (packageJson.modified && !packageLock.modified) {
const message = 'This PR modified `package.json`, but not `package-lock.json`'
const idea = 'Perhaps you need to run `npm install`?'
warn(`${message} - <i>${idea}</i>`)
}
if (server.modified && !serverTests.modified) {
warn(
[
'This PR modified the server but none of its tests. <br>',
"That's okay so long as it's refactoring existing code.",
].join('')
)
}
if (helpers.created && !helperTests.created) {
warn(
[
'This PR added helper modules in `lib/` but not accompanying tests. <br>',
'Generally helper modules should have their own tests.',
].join('')
)
} else if (helpers.createdOrModified && !helperTests.createdOrModified) {
warn(
[
'This PR modified helper functions in `lib/` but not accompanying tests. <br>',
"That's okay so long as it's refactoring existing code.",
].join('')
)
}
if (logos.created) {
message(
[
':art: Thanks for submitting a logo. <br>',
'Please ensure your contribution follows our ',
'[guidance](https://github.com/badges/shields/blob/master/doc/logos.md#contributing-logos) ',
'for logo submissions.',
].join('')
)
}
if (capitals.created || underscores.created) {
fail(
[
'JavaScript source files should be named with `kebab-case` ',
'(dash-separated lowercase).',
].join('')
)
}
const allFiles = danger.git.created_files.concat(danger.git.modified_files)
if (allFiles.length > 100) {
warn("Lots 'o changes. Skipping diff-based checks.")
} else {
allFiles.forEach(file => {
// eslint-disable-next-line promise/prefer-await-to-then
danger.git.diffForFile(file).then(({ diff }) => {
if (/serverSecrets/.test(diff) && !secretsDocs.modified) {
warn(
[
`:books: Remember to ensure any changes to \`serverSecrets\` `,
`in \`${file}\` are reflected in the [server secrets documentation]`,
'(https://github.com/badges/shields/blob/master/doc/server-secrets.md)',
].join('')
)
}
if (/\+.*assert[(.]/.test(diff)) {
warn(
[
`Found 'assert' statement added in \`${file}\`. <br>`,
'Please ensure tests are written using Chai ',
'[expect syntax](http://chaijs.com/guide/styles/#expect)',
].join('')
)
}
})
})
}
function onlyUnique(value, index, self) {
return self.indexOf(value) === index
}
const affectedServices = allFiles
.map(file => {
const match = file.match(/^services\/(.+)\/.+\.service.js$/)
return match ? match[1] : undefined
})
.filter(Boolean)
.filter(onlyUnique)
const testedServices = allFiles
.map(file => {
const match = file.match(/^services\/(.+)\/.+\.tester.js$/)
return match ? match[1] : undefined
})
.filter(Boolean)
.filter(onlyUnique)
affectedServices.forEach(service => {
if (testedServices.indexOf(service) === -1) {
warn(
[
`This PR modified service code for <kbd>${service}</kbd> but not its test code. <br>`,
"That's okay so long as it's refactoring existing code.",
].join('')
)
}
})
// Prevent merging exclusive services tests.
noTestShortcuts({
testFilePredicate: filePath => filePath.endsWith('.tester.js'),
patterns: {
only: ['only()'],
},
})