Skip to content

Commit

Permalink
test: test preview sites for broken links (#1007)
Browse files Browse the repository at this point in the history
* test: test preview sites for broken links

* fix: ignore gifs too

* test: add test for `_redirects` file

* ci: implement preliminary workflow

* fix: rename folder to `lambda`

* ci: make script executable

* chore: indicate code review together

Co-authored-by: Debbie O'Brien <[email protected]>

Co-authored-by: Debbie O'Brien <[email protected]>
  • Loading branch information
danielroe and debs-obrien authored Nov 20, 2020
1 parent 10aef6f commit 03d24e4
Show file tree
Hide file tree
Showing 12 changed files with 510 additions and 27 deletions.
24 changes: 24 additions & 0 deletions .github/bin/update-status.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/bin/bash

STATE=$(echo $1 | tr '[:upper:]' '[:lower:]')
CONTEXT=$2
DESCRIPTION=$3
GITHUB_URL="https://github.com"
GITHUB_API_URL="https://api.github.com"

echo "Updating status to ${STATE} for ${CONTEXT} with description ${DESCRIPTION}."

curl --silent --show-error --fail \
--trace ./${CONTEXT}.log \
-X POST "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/statuses/${GITHUB_SHA}" \
-H "Authorization: token ${GITHUB_TOKEN}" \
-H "Content-Type: text/json; charset=utf-8" \
-d @- <<EOF
{
"state": "${STATE}",
"context": "${CONTEXT}",
"description": "${DESCRIPTION}"
}
EOF

sleep 5
37 changes: 37 additions & 0 deletions .github/workflows/deployment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Deployment tests
on:
repository_dispatch:
types: [check_links]

jobs:
check-links:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2

- uses: actions/setup-node@v2-beta
with:
node-version: "14"

- uses: actions/cache@v2
id: cache
with:
path: node_modules
key: ${{ hashFiles('yarn.lock') }}
restore-keys: ${{ runner.os }}-yarn

- name: Install dependencies
if: steps.cache.outputs.cache-hit != 'true'
run: yarn install --frozen-lockfile

- name: Crawl site
run: node -r esm scripts/crawl.js
env:
BASE_URL: ${{ github.event.client_payload.deploy_url }}

- name: Set status
if: always()
run: ./.github/bin/update-status.sh ${{ job.status }} check-links 'Finished checking links'
env:
GITHUB_TOKEN: ${{ github.token }}
GITHUB_SHA: ${{ github.event.client_payload.commit_ref }}
10 changes: 10 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ on:
branches:
- master
jobs:
pending-link-check:
runs-on: ubuntu-18.04
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set status to pending
run: ./.github/bin/update-status.sh pending check-links 'Waiting for deployment'
env:
GITHUB_TOKEN: ${{ github.token }}

cypress-run:
runs-on: ubuntu-16.04
steps:
Expand Down
10 changes: 5 additions & 5 deletions components/templates/home/HomeWelcome.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
tag="h1"
class="text-4xl xl:text-5xl text-light-onSurfacePrimary dark:text-dark-onSurfacePrimary font-medium tracking-normal leading-tight mb-6"
>
<template v-slot:br>
<template #br>
<br />
</template>
<template v-slot:frameworkType>
<template #frameworkType>
<span class="text-nuxt-lightgreen"> Vue </span>
</template>
</i18n>
Expand All @@ -26,7 +26,7 @@
tag="h2"
class="xl:text-lg text-light-onSurfaceSecondary dark:text-dark-onSurfaceSecondary font-medium leading-relaxed mb-6"
>
<template v-slot:openSource>
<template #openSource>
<span title="Under MIT license">
{{ $t('homepage.welcome.openSource') }}
</span>
Expand Down Expand Up @@ -79,7 +79,7 @@
tag="p"
class="font-medium py-2 text-xs xl:text-sm text-center text-light-onSurfaceSecondary dark:text-dark-onSurfaceSecondary"
>
<template v-slot:company>
<template #company>
<span>
<a
href="https://www.vuemastery.com"
Expand All @@ -90,7 +90,7 @@
</a>
</span>
</template>
<template v-slot:cheatSheet>
<template #cheatSheet>
<span>
<a
href="https://www.vuemastery.com/nuxt-cheat-sheet/"
Expand Down
29 changes: 29 additions & 0 deletions lambda/webhook.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import fetch from 'node-fetch'
import jwt from 'jsonwebtoken'

exports.handler = async function ({ body, headers }) {
const signature = headers['X-Webhook-Signature']

try {
jwt.verify(signature, process.env.SECRET_TOKEN || '')
} catch {
return {
statusCode: 403
}
}

await fetch('https://api.github.com/repos/nuxt/nuxtjs.org/dispatches', {
method: 'post',
body: JSON.stringify({
event_type: 'check_links',
client_payload: {
deploy_url: body.deploy_url,
commit_ref: body.commit_ref
}
})
})

return {
statusCode: 204
}
}
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"chart.js": "^2.9.4",
"clipboard": "^2.0.6",
"intersection-observer": "^0.11.0",
"jsonwebtoken": "^8.5.1",
"lodash.groupby": "^4.6.0",
"lodash.sortby": "^4.7.0",
"node-fetch": "^2.6.1",
Expand All @@ -34,7 +35,10 @@
"@nuxtjs/pwa": "^3.2.2",
"@nuxtjs/style-resources": "^1.0.0",
"@nuxtjs/svg": "^0.1.12",
"@types/crawler": "^1.2.0",
"@types/fs-extra": "^9.0.3",
"babel-eslint": "^10.1.0",
"crawler": "^1.2.2",
"cross-env": "^7.0.2",
"cypress": "5.6.0",
"eslint": "^7.13.0",
Expand Down
12 changes: 6 additions & 6 deletions pages/docs/2.x/_book/_slug.vue
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import copyCodeBlock from '~/mixins/copyCodeBlock'
export default {
mixins: [copyCodeBlock],
scrollToTop: true,
async asyncData({ $content, $contributors, params, store, error, app }) {
let path = `/${app.i18n.defaultLocale}/guides/${params.book}`
let page, prev, next, langFallback
Expand Down Expand Up @@ -105,12 +106,6 @@ export default {
contributors
}
},
computed: {
docLink() {
return `https://github.com/nuxt/nuxtjs.org/blob/master/content${this.path}/${this.$route.params.slug}.md`
}
},
scrollToTop: true,
head() {
return {
title: this.page.title,
Expand Down Expand Up @@ -141,6 +136,11 @@ export default {
}
]
}
},
computed: {
docLink() {
return `https://github.com/nuxt/nuxtjs.org/blob/master/content${this.path}/${this.$route.params.slug}.md`
}
}
}
</script>
Expand Down
12 changes: 6 additions & 6 deletions pages/examples/_slug.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

<script>
export default {
scrollToTop: true,
async asyncData({ $content, $contributors, params, store, error, app }) {
const slug = params.slug || 'hello-world'
Expand Down Expand Up @@ -91,12 +92,6 @@ export default {
contributors
}
},
computed: {
docLink() {
return `https://github.com/nuxt/nuxtjs.org/blob/master/content${this.path}/${this.$route.params.slug}.md`
}
},
scrollToTop: true,
head() {
return {
title: this.page.title,
Expand Down Expand Up @@ -127,6 +122,11 @@ export default {
}
]
}
},
computed: {
docLink() {
return `https://github.com/nuxt/nuxtjs.org/blob/master/content${this.path}/${this.$route.params.slug}.md`
}
}
}
</script>
Expand Down
2 changes: 1 addition & 1 deletion pages/themes.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
class="text-3xl xl:text-4xl text-light-onSurfacePrimary dark:text-dark-onSurfacePrimary font-medium leading-normal mb-6 lg:pt-4"
>
{{ $t('themes.title') }}
<template v-slot:nuxt>
<template #nuxt>
<AppTitle />
</template>
</i18n>
Expand Down
38 changes: 38 additions & 0 deletions scripts/check-redirects.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import path from 'path'

import consola from 'consola'
import Crawler from 'crawler'
import fs from 'fs-extra'

const logger = consola.withTag('redirect-tester')

const redirects = fs
.readFileSync(path.resolve(__dirname, '../_redirects'))
.toString()
.split('\n')
.filter(redirect => redirect && !redirect.startsWith('#'))
.filter(redirect => redirect.startsWith('/'))
.map(redirect => redirect.split(' ')[1])
.filter(redirect => redirect.startsWith('/'))
.map(redirect => 'https://nuxtjs.org' + redirect)

const crawler = new Crawler({
maxConnections: 100,
callback(error, res, done) {
const { uri } = res.options
const { statusCode } = res.request.response

if (error || ![200, 301, 302].includes(statusCode)) {
logger.error('Error crawling', uri, `(status ${statusCode})`)
return done()
}

logger.success(uri)
done()
}
})

logger.log('')
logger.info(`Checking \`internal redirects\`.`)

redirects.forEach(redirect => crawler.queue(redirect))
104 changes: 104 additions & 0 deletions scripts/crawl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import Crawler from 'crawler'
import consola from 'consola'

const logger = consola.withTag('crawler')

const excludedExtensions = process.env.EXCLUDE
? process.env.EXCLUDE.split(',')
: ['svg', 'png', 'jpg', 'sketch', 'ico', 'gif']
const crawlExternal = !!process.env.CRAWL_EXTERNAL || false

let baseURL = process.env.BASE_URL || 'https://nuxtjs.org'
if (baseURL.endsWith('/')) baseURL = baseURL.slice(0, -1)
const startingURL = baseURL + '/'

// GLOBALS
const urls = new Set([startingURL])
const referrers = {}
const erroredUrls = []
const externalUrls = new Set()

/**
* @type {Crawler} crawler
*/
// eslint-disable-next-line
let crawler

/**
* @param {string} path
* @param {string | undefined} referrer
*/
function queue(path, referrer) {
const { pathname, origin } = new URL(path, referrer)

const url = `${origin}${pathname}`
if (!url || urls.has(url) || !crawler) return

const extension = url.split('.').pop()
if (excludedExtensions.includes(extension)) return

let external = false
if (origin !== baseURL) {
external = true
externalUrls.add(url)
if (!crawlExternal) return
}

urls.add(url)

if (referrer) referrers[url] = [...(referrers[url] || []), referrer]

if (external)
return crawler.queue({
uri: url,
method: 'GET',
rateLimit: 2000
})
crawler.queue(url)
}

crawler = new Crawler({
maxConnections: 100,
callback(error, res, done) {
const { $ } = res
const { uri } = res.options
const { statusCode } = res.request.response

if (error || ![200, 301, 302].includes(statusCode)) {
logger.error('Error crawling', uri, `(status ${statusCode})`)
if (referrers[uri]) logger.info(`${uri} referred by`, referrers[uri])
erroredUrls.push(uri)
return done()
}

if (!$) {
logger.error('Could not parse', uri)
return done()
}

if (uri.includes(baseURL)) {
$(`a:not([href*=mailto])`).each((_, el) => queue(el.attribs.href, uri))
}

logger.success(uri)
logger.debug(uri, `[${crawler.queueSize} / ${urls.size}]`)
if (crawler.queueSize === 1) {
logger.log('')
logger.info(`Checked \`${urls.size}\` pages.`)
// Tasks to run at the end.
if (erroredUrls.length)
throw new Error(
`\n\nErrors found when crawling ${erroredUrls.join(', ')}.`
)
}
done()
}
})

logger.log('')
logger.info(
`Checking \`${baseURL}\`${crawlExternal ? ' and external links' : ''}.`
)
logger.info(`Ignoring file extensions: \`${excludedExtensions.join(', ')}.\`\n`)

crawler.queue(startingURL)
Loading

0 comments on commit 03d24e4

Please sign in to comment.