From 1a17772de8755a341469fd9908ca3ebfa53ed754 Mon Sep 17 00:00:00 2001 From: Vincent Rubinetti Date: Wed, 21 Feb 2024 13:45:43 -0500 Subject: [PATCH 1/5] make changes to check for broken links --- .github/workflows/check.yaml | 36 ++++++++++ .github/workflows/deploy.yaml | 15 +++-- check.js | 20 ++++++ core.js | 118 ++++++++++++++++++++++++++++++++ encode.js | 122 ++++------------------------------ 5 files changed, 197 insertions(+), 114 deletions(-) create mode 100644 .github/workflows/check.yaml create mode 100644 check.js create mode 100644 core.js diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml new file mode 100644 index 0000000..c42d3fd --- /dev/null +++ b/.github/workflows/check.yaml @@ -0,0 +1,36 @@ +name: Check links + +on: + # when someone makes a change directly to main branch + push: + branches: + - main + # when someone requests a change to main branch + pull_request: + branches: + - main + + # run periodically + schedule: + - cron: "0 0 * * *" + # run manually + workflow_dispatch: + +jobs: + check: + runs-on: ubuntu-latest + steps: + - if: runner.debug == '1' + uses: mxschmitt/action-tmate@v3 + + - name: Get this repo's code + uses: actions/checkout@v4 + + - name: Set up Bun + uses: oven-sh/setup-bun@v1 + + - name: Install packages + run: bun install glob@v9 yaml@v2 + + - name: Run check script + run: bun ./check.js diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index b67ab7e..916c585 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -15,25 +15,30 @@ env: jobs: encode: - name: Encode and deploy runs-on: ubuntu-latest steps: + - if: runner.debug == '1' + uses: mxschmitt/action-tmate@v3 + - name: Get this repo's code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: path: redirects-repo # save in separate sub-folder - name: Get website repo's code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: ${{ github.repository_owner }}/${{ env.website_repo }} # assume same user/org path: website-repo # save in separate sub-folder + - name: Set up Bun + uses: oven-sh/setup-bun@v1 + - name: Install packages - run: npm install glob@v9 yaml@v2 + run: bun install glob@v9 yaml@v2 - name: Run encode script - run: node ./redirects-repo/encode.js + run: bun ./redirects-repo/encode.js - name: Commit result to website repo if: ${{ github.event_name == 'push' }} diff --git a/check.js b/check.js new file mode 100644 index 0000000..cb950b8 --- /dev/null +++ b/check.js @@ -0,0 +1,20 @@ +import { addError, list } from "./core"; + +// for each redirect +await Promise.all( + list.map(async ({ to }) => { + try { + // do simple request to target url + const response = await fetch(to); + if ( + // only fail on certain status codes that might indicate link is "broken" + [ + 400, 404, 405, 406, 408, 409, 410, 421, 500, 501, 502, 503, 504, + ].includes(response.status) + ) + throw Error(response.status); + } catch (error) { + addError(`"to: ${to}" may be a broken link\n(${error})`); + } + }) +); diff --git a/core.js b/core.js new file mode 100644 index 0000000..ac357a9 --- /dev/null +++ b/core.js @@ -0,0 +1,118 @@ +import { readFileSync } from "fs"; +import { resolve } from "path"; +import { globSync } from "glob"; +import { parse } from "yaml"; + +// collect (caught) errors to report at end +const errors = []; + +// if running in github actions debug mode, do extra logging +export const verbose = !!process.env.RUNNER_DEBUG; + +// get yaml files that match glob pattern +const files = globSync("*.y?(a)ml", { cwd: __dirname }); + +log("Files", files.join(" ")); + +// start combined list of redirects +export const list = []; + +// keep track of duplicate entries +const duplicates = {}; + +// go through each yaml file +for (const file of files) { + // load file contents + const contents = readFileSync(resolve(__dirname, file), "utf8"); + + // try to parse as yaml + let data; + try { + data = parse(contents); + } catch (error) { + addError(`Couldn't parse ${file}. Make sure it is valid YAML.`); + continue; + } + + // check if top level is list + if (!Array.isArray(data)) { + addError(`${file} is not a list`); + continue; + } + + // go through each entry + for (let [index, entry] of Object.entries(data)) { + index = Number(index) + 1; + const trace = `${file} entry ${index}`; + + // check if dict + if (typeof entry !== "object") { + addError(`${trace} is not a dict`); + continue; + } + + // check "from" field + if (!(typeof entry.from === "string" && entry.from.trim())) { + addError(`${trace} "from" field invalid`); + continue; + } + + // check "to" field + if (!(typeof entry.to === "string" && entry.to.trim())) + addError(`${trace} "to" field invalid`); + + // normalize "from" field. lower case, remove leading slashes. + entry.from = entry.from.toLowerCase().replace(/^(\/+)/, ""); + + // add to combined list + list.push(entry); + + // add to duplicate list. record source file and entry number for logging. + duplicates[entry.from] ??= []; + duplicates[entry.from].push({ ...entry, file, index }); + } +} + +// check that any redirects exist +if (!list.length) addError("No redirects"); + +if (verbose) log("Combined redirects list", list); + +// trigger errors for duplicates +for (const [from, entries] of Object.entries(duplicates)) { + const count = entries.length; + if (count <= 1) continue; + const duplicates = entries + .map(({ file, index }) => `\n ${file} entry ${index}`) + .join(""); + addError(`"from: ${from}" appears ${count} time(s): ${duplicates}`); +} + +// add error +export function addError(error) { + errors.push(error); +} + +// when script finished +process.on("exit", () => { + // report all errors together + if (errors.length) { + process.exitCode = 1; + errors.forEach(logError); + logError(`${errors.length} error(s)`); + } else { + process.exitCode = 0; + log("No errors!"); + } +}); + +// formatted normal log +export function log(message, data) { + console.info("\x1b[1m\x1b[96m" + message + "\x1b[0m"); + if (data) console.log(data); +} + +// formatted error log +export function logError(message) { + console.error("\x1b[1m\x1b[91m" + message + "\x1b[0m"); +} diff --git a/encode.js b/encode.js index 9d94a55..7685b86 100644 --- a/encode.js +++ b/encode.js @@ -1,98 +1,5 @@ -const { readFileSync, writeFileSync } = require("fs"); -const { resolve } = require("path"); -const { globSync } = require("glob"); -const { parse } = require("yaml"); - -// collect (caught) errors to report at end -const errors = []; - -// report errors on exit -process.on("exit", () => { - errors.forEach(error); - if (errors.length) error(`${errors.length} error(s)`); -}); - -// if running in github actions debug mode, do extra logging -const verbose = !!process.env.RUNNER_DEBUG; - -// get yaml files that match glob pattern -const files = globSync("*.y?(a)ml", { cwd: __dirname }); - -log("Files", files.join(" ")); - -// start combined list of redirects -const list = []; - -// keep track of duplicate entries -const duplicates = {}; - -// go through each yaml file -for (const file of files) { - // load file contents - const contents = readFileSync(resolve(__dirname, file), "utf8"); - - // try to parse as yaml - let data; - try { - data = parse(contents); - } catch (error) { - errors.push(`Couldn't parse ${file}. Make sure it is valid YAML.`); - continue; - } - - // check if top level is list - if (!Array.isArray(data)) { - errors.push(`${file} is not a list`); - continue; - } - - // go through each entry - for (let [index, entry] of Object.entries(data)) { - index = Number(index) + 1; - const trace = `${file} entry ${index}`; - - // check if dict - if (typeof entry !== "object") { - errors.push(`${trace} is not a dict`); - continue; - } - - // check "from" field - if (!(typeof entry.from === "string" && entry.from.trim())) { - errors.push(`${trace} "from" field invalid`); - continue; - } - - // check "to" field - if (!(typeof entry.to === "string" && entry.to.trim())) - errors.push(`${trace} "to" field invalid`); - - // normalize "from" field. lower case, remove leading slashes. - entry.from = entry.from.toLowerCase().replace(/^(\/+)/, ""); - - // add to combined list - list.push(entry); - - // add to duplicate list. record source file and entry number for logging. - duplicates[entry.from] ??= []; - duplicates[entry.from].push({ ...entry, file, index }); - } -} - -// check that any redirects exist -if (!list.length) errors.push("No redirects"); - -if (verbose) log("Combined redirects list", list); - -// trigger errors for duplicates -for (const [from, entries] of Object.entries(duplicates)) { - const count = entries.length; - if (count <= 1) continue; - const duplicates = entries - .map(({ file, index }) => `\n ${file} entry ${index}`) - .join(""); - errors.push(`"from: ${from}" appears ${count} time(s): ${duplicates}`); -} +import { readFileSync, writeFileSync } from "fs"; +import { addError, list, verbose } from "./core"; // encode redirect list to base64 to obfuscate const encoded = Buffer.from(JSON.stringify(list)).toString("base64"); @@ -103,7 +10,12 @@ if (verbose) log("Encoded redirects list", encoded); const script = "./website-repo/redirect.js"; // load contents of script -const contents = readFileSync(script, "utf8").toString(); +let contents = ""; +try { + contents = readFileSync(script, "utf8").toString(); +} catch (error) { + addError(`Couldn't find script file at ${script}`); +} // pattern to extract encoded redirect list from script string const regex = /(list\s*=\s*")([A-Za-z0-9+\/=]*)(")/; @@ -115,22 +27,14 @@ if (verbose) log("Old encoded redirects list", oldEncoded); // check that we could find it (and thus can replace it) if (typeof oldEncoded !== "string") - errors.push("Couldn't find encoded redirects list in redirect script"); + addError("Couldn't find encoded redirects list in redirect script"); // update encoded string in script const newContents = contents.replace(regex, "$1" + encoded + "$3"); // write updated redirect script to website repo -writeFileSync(script, newContents, "utf-8"); - -// exit if collected errors -if (errors.length) process.exit(1); - -// debug util -function log(message, data) { - console.info("\033[1m\033[96m" + message + "\033[0m"); - console.log(data); -} -function error(message) { - console.error("\033[1m\033[91m" + message + "\033[0m"); +try { + writeFileSync(script, newContents, "utf-8"); +} catch (error) { + addError(`Couldn't write script file to ${script}`); } From 379a3fa31ff412074375379746d5a0f71496a8c7 Mon Sep 17 00:00:00 2001 From: Vincent Rubinetti Date: Wed, 13 Mar 2024 12:31:52 -0400 Subject: [PATCH 2/5] make more modular --- check.js | 45 +++++++++------- core.js | 154 ++++++++++++++++++++++++++++-------------------------- encode.js | 63 ++++++++++++---------- 3 files changed, 141 insertions(+), 121 deletions(-) diff --git a/check.js b/check.js index cb950b8..191607e 100644 --- a/check.js +++ b/check.js @@ -1,20 +1,27 @@ -import { addError, list } from "./core"; +import { addError, getList, onExit } from "./core"; -// for each redirect -await Promise.all( - list.map(async ({ to }) => { - try { - // do simple request to target url - const response = await fetch(to); - if ( - // only fail on certain status codes that might indicate link is "broken" - [ - 400, 404, 405, 406, 408, 409, 410, 421, 500, 501, 502, 503, 504, - ].includes(response.status) - ) - throw Error(response.status); - } catch (error) { - addError(`"to: ${to}" may be a broken link\n(${error})`); - } - }) -); +onExit(); + +// check list of redirects for broken links +async function checkList(list) { + return await Promise.all( + // for each redirect + list.map(async ({ to }) => { + try { + // do simple request to target url + const response = await fetch(to); + if ( + // only fail on certain status codes that might indicate link is "broken" + [ + 400, 404, 405, 406, 408, 409, 410, 421, 500, 501, 502, 503, 504, + ].includes(response.status) + ) + throw Error(response.status); + } catch (error) { + addError(`"to: ${to}" may be a broken link\n(${error})`); + } + }) + ); +} + +await checkList(getList()); diff --git a/core.js b/core.js index ac357a9..743d2a0 100644 --- a/core.js +++ b/core.js @@ -3,108 +3,114 @@ import { resolve } from "path"; import { globSync } from "glob"; import { parse } from "yaml"; -// collect (caught) errors to report at end -const errors = []; - // if running in github actions debug mode, do extra logging export const verbose = !!process.env.RUNNER_DEBUG; -// get yaml files that match glob pattern -const files = globSync("*.y?(a)ml", { cwd: __dirname }); +// get full list of redirects +export function getList() { + // get yaml files that match glob pattern + const files = globSync("*.y?(a)ml", { cwd: __dirname }); -log("Files", files.join(" ")); + log("Files", files.join(" ")); -// start combined list of redirects -export const list = []; + // start combined list of redirects + const list = []; -// keep track of duplicate entries -const duplicates = {}; + // keep track of duplicate entries + const duplicates = {}; -// go through each yaml file -for (const file of files) { - // load file contents - const contents = readFileSync(resolve(__dirname, file), "utf8"); + // go through each yaml file + for (const file of files) { + // load file contents + const contents = readFileSync(resolve(__dirname, file), "utf8"); - // try to parse as yaml - let data; - try { - data = parse(contents); - } catch (error) { - addError(`Couldn't parse ${file}. Make sure it is valid YAML.`); - continue; - } - - // check if top level is list - if (!Array.isArray(data)) { - addError(`${file} is not a list`); - continue; - } - - // go through each entry - for (let [index, entry] of Object.entries(data)) { - index = Number(index) + 1; - const trace = `${file} entry ${index}`; - - // check if dict - if (typeof entry !== "object") { - addError(`${trace} is not a dict`); + // try to parse as yaml + let data; + try { + data = parse(contents); + } catch (error) { + addError(`Couldn't parse ${file}. Make sure it is valid YAML.`); continue; } - // check "from" field - if (!(typeof entry.from === "string" && entry.from.trim())) { - addError(`${trace} "from" field invalid`); + // check if top level is list + if (!Array.isArray(data)) { + addError(`${file} is not a list`); continue; } - // check "to" field - if (!(typeof entry.to === "string" && entry.to.trim())) - addError(`${trace} "to" field invalid`); + // go through each entry + for (let [index, entry] of Object.entries(data)) { + index = Number(index) + 1; + const trace = `${file} entry ${index}`; + + // check if dict + if (typeof entry !== "object") { + addError(`${trace} is not a dict`); + continue; + } + + // check "from" field + if (!(typeof entry.from === "string" && entry.from.trim())) { + addError(`${trace} "from" field invalid`); + continue; + } + + // check "to" field + if (!(typeof entry.to === "string" && entry.to.trim())) + addError(`${trace} "to" field invalid`); - // normalize "from" field. lower case, remove leading slashes. - entry.from = entry.from.toLowerCase().replace(/^(\/+)/, ""); + // normalize "from" field. lower case, remove leading slashes. + entry.from = entry.from.toLowerCase().replace(/^(\/+)/, ""); - // add to combined list - list.push(entry); + // add to combined list + list.push(entry); - // add to duplicate list. record source file and entry number for logging. - duplicates[entry.from] ??= []; - duplicates[entry.from].push({ ...entry, file, index }); + // add to duplicate list. record source file and entry number for logging. + duplicates[entry.from] ??= []; + duplicates[entry.from].push({ ...entry, file, index }); + } } -} -// check that any redirects exist -if (!list.length) addError("No redirects"); + // check that any redirects exist + if (!list.length) addError("No redirects"); -if (verbose) log("Combined redirects list", list); + if (verbose) log("Combined redirects list", list); -// trigger errors for duplicates -for (const [from, entries] of Object.entries(duplicates)) { - const count = entries.length; - if (count <= 1) continue; - const duplicates = entries - .map(({ file, index }) => `\n ${file} entry ${index}`) - .join(""); - addError(`"from: ${from}" appears ${count} time(s): ${duplicates}`); + // trigger errors for duplicates + for (const [from, entries] of Object.entries(duplicates)) { + const count = entries.length; + if (count <= 1) continue; + const duplicates = entries + .map(({ file, index }) => `\n ${file} entry ${index}`) + .join(""); + addError(`"from: ${from}" appears ${count} time(s): ${duplicates}`); + } + + return list; } +// collect (caught) errors to report at end +const errors = []; + // add error export function addError(error) { errors.push(error); } -// when script finished -process.on("exit", () => { - // report all errors together - if (errors.length) { - process.exitCode = 1; - errors.forEach(logError); - logError(`${errors.length} error(s)`); - } else { - process.exitCode = 0; - log("No errors!"); - } -}); +// when script finished, report all errors together +export function onExit() { + process.on("exit", () => { + if (errors.length) { + process.exitCode = 1; + errors.forEach(logError); + logError(`${errors.length} error(s)`); + } else { + process.exitCode = 0; + log("No errors!"); + } + }); +} // formatted normal log export function log(message, data) { diff --git a/encode.js b/encode.js index 7685b86..cc84a25 100644 --- a/encode.js +++ b/encode.js @@ -1,40 +1,47 @@ import { readFileSync, writeFileSync } from "fs"; -import { addError, list, verbose } from "./core"; +import { addError, getList, onExit, verbose } from "./core"; -// encode redirect list to base64 to obfuscate -const encoded = Buffer.from(JSON.stringify(list)).toString("base64"); +onExit(); -if (verbose) log("Encoded redirects list", encoded); +// encode list of redirects into redirect script +function encodeList(list) { + // encode redirect list to base64 to obfuscate + const encoded = Buffer.from(JSON.stringify(list)).toString("base64"); -// redirect script from website repo -const script = "./website-repo/redirect.js"; + if (verbose) log("Encoded redirects list", encoded); -// load contents of script -let contents = ""; -try { - contents = readFileSync(script, "utf8").toString(); -} catch (error) { - addError(`Couldn't find script file at ${script}`); -} + // redirect script from website repo + const script = "./website-repo/redirect.js"; + + // load contents of script + let contents = ""; + try { + contents = readFileSync(script, "utf8").toString(); + } catch (error) { + addError(`Couldn't find script file at ${script}`); + } -// pattern to extract encoded redirect list from script string -const regex = /(list\s*=\s*")([A-Za-z0-9+\/=]*)(")/; + // pattern to extract encoded redirect list from script string + const regex = /(list\s*=\s*")([A-Za-z0-9+\/=]*)(")/; -// get encoded redirect list currently in script -const oldEncoded = contents.match(regex)?.[2]; + // get encoded redirect list currently in script + const oldEncoded = contents.match(regex)?.[2]; -if (verbose) log("Old encoded redirects list", oldEncoded); + if (verbose) log("Old encoded redirects list", oldEncoded); -// check that we could find it (and thus can replace it) -if (typeof oldEncoded !== "string") - addError("Couldn't find encoded redirects list in redirect script"); + // check that we could find it (and thus can replace it) + if (typeof oldEncoded !== "string") + addError("Couldn't find encoded redirects list in redirect script"); -// update encoded string in script -const newContents = contents.replace(regex, "$1" + encoded + "$3"); + // update encoded string in script + const newContents = contents.replace(regex, "$1" + encoded + "$3"); -// write updated redirect script to website repo -try { - writeFileSync(script, newContents, "utf-8"); -} catch (error) { - addError(`Couldn't write script file to ${script}`); + // write updated redirect script to website repo + try { + writeFileSync(script, newContents, "utf-8"); + } catch (error) { + addError(`Couldn't write script file to ${script}`); + } } + +encodeList(getList()); From 3140a3b1e25043fbf7e6d03ae3ead43a2076a03d Mon Sep 17 00:00:00 2001 From: Vincent Rubinetti Date: Wed, 13 Mar 2024 12:49:44 -0400 Subject: [PATCH 3/5] update readme --- README.md | 45 ++++++++++++++++++++++++--------------------- check.js | 1 + 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 2d23641..9105e16 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,12 @@ _Counterpart to the [redirects-website repo](../../../redirects-website)._ 1. Add/change/remove redirect entries in one or more [`.yaml` files in the top folder](../../blob/main/redirects.yaml). Note: the `from` field is **case-insensitive**. -2. Commit the changes to the `main` branch, either directly or with a pull request (recommended so the automatic process can catch errors before the changes go live). -3. Changes should take effect automatically within a minute or so. +1. Commit the changes to the `main` branch, either directly or with a pull request (recommended so the automatic process can catch errors before the changes go live). +1. Changes should take effect automatically within a minute or so. Verify that no errors occurred in the automatic process here: [![Encode and deploy](../../actions/workflows/deploy.yaml/badge.svg)](../../actions/workflows/deploy.yaml) +1. Verify that none of your redirect links are reported broken in the automatic process here: [![Check links](../../actions/workflows/check.yaml/badge.svg)](../../actions/workflows/check.yaml). + Note that this is only a **rough check**. + There may be false positives and true negatives, as it simply checks the [status code](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes) of the link, which the third-party site may choose inappropriately. You can do this [directly on github.com](../../edit/main/redirects.yaml) (tip: press . right now), or locally with git. @@ -96,19 +99,19 @@ After the one-time setup, **all you have to do is edit the `.yaml` files, and ev Adding/removing/changing a link goes like this: 1. You change one or more of the `.yaml` files in the _redirects repo_. -2. `deploy.yaml` tells [GitHub Actions](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions) that any time someone commits a change to the repo, it should automatically run the `encode.js` script. -3. The `encode.js` script combines all of your `.yaml` files into one, and encodes it[^1]. -4. `deploy.yaml` then tells GitHub to take the result of the `encode.js` script and commit it to the `redirect.js` script in the _website repo_. -5. In the _website repo_, GitHub Pages detects a change in the `redirect.js` script, and updates the website. +1. `deploy.yaml` tells [GitHub Actions](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions) that any time someone commits a change to the repo, it should automatically run the `encode.js` script. +1. The `encode.js` script combines all of your `.yaml` files into one, and encodes it[^1]. +1. `deploy.yaml` then tells GitHub to take the result of the `encode.js` script and commit it to the `redirect.js` script in the _website repo_. +1. In the _website repo_, GitHub Pages detects a change in the `redirect.js` script, and updates the website. Then, a user visiting a link goes like this: 1. They navigate to a link on the website, e.g. `/chatroom`. -2. `chatroom.html` isn't a file in the _website repo_, and thus isn't a page on the website, so GitHub loads [`404.html`](https://en.wikipedia.org/wiki/HTTP_404) for the user instead (but preserves the `/chatroom` url). +1. `chatroom.html` isn't a file in the _website repo_, and thus isn't a page on the website, so GitHub loads [`404.html`](https://en.wikipedia.org/wiki/HTTP_404) for the user instead (but preserves the `/chatroom` url). This file immediately runs some scripts: -3. The analytics code snippet sends[^2] stats like url, IP, date, time, location, etc. off to Google Analytics or whoever. -4. The `redirect.js` script decodes the redirect lists previously encoded from the _redirects repo_, finds the long url corresponding to "chatroom" (**case-insensitive**), and navigates there instead. -5. They arrive at the intended destination, e.g. `zoom.us/j/12345abcdef`, with virtually no perceptible delay. +1. The analytics code snippet sends[^2] stats like url, IP, date, time, location, etc. off to Google Analytics or whoever. +1. The `redirect.js` script decodes the redirect lists previously encoded from the _redirects repo_, finds the long url corresponding to "chatroom" (**case-insensitive**), and navigates there instead. +1. They arrive at the intended destination, e.g. `zoom.us/j/12345abcdef`, with virtually no perceptible delay. ## Setup @@ -117,10 +120,10 @@ Then, a user visiting a link goes like this: 1. [Use the _redirects repo_ (this repo) as a template](https://github.com/CU-DBMI/redirects/generate). **Do not fork**, because you cannot make forks private. _Name it `redirects` and make it private_. -2. [Use the _website repo_ as a template](https://github.com/CU-DBMI/redirects-website/generate). +1. [Use the _website repo_ as a template](https://github.com/CU-DBMI/redirects-website/generate). _Name it `redirects-website` and make it public_. -3. [Enable GitHub Pages](https://docs.github.com/en/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site) on your copied _website repo_ with the default settings. -4. After a minute or so, GitHub should tell you that your site is now being hosted at `your-org.github.io/redirects-website`. +1. [Enable GitHub Pages](https://docs.github.com/en/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site) on your copied _website repo_ with the default settings. +1. After a minute or so, GitHub should tell you that your site is now being hosted at `your-org.github.io/redirects-website`. If you ever need to pull in updates from these templates, [see the instructions here](https://stackoverflow.com/questions/56577184/github-pull-changes-from-a-template-repository). @@ -129,8 +132,8 @@ If you ever need to pull in updates from these templates, [see the instructions To allow your _redirects repo_ to automatically write to your _website repo_, you need to "connect" them with a deploy key: 1. [Generate an SSH key pair](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent#generating-a-new-ssh-key). -2. In your _redirects repo_, [create a new repository actions secret](https://docs.github.com/en/actions/security-guides/encrypted-secrets#creating-encrypted-secrets-for-a-repository) named `DEPLOY_KEY`, and paste the private SSH key. -3. In your _website repo_, [create a new deploy key](https://docs.github.com/en/developers/overview/managing-deploy-keys#setup-2) with write/push access named `DEPLOY_KEY`, and paste the public SSH key. +1. In your _redirects repo_, [create a new repository actions secret](https://docs.github.com/en/actions/security-guides/encrypted-secrets#creating-encrypted-secrets-for-a-repository) named `DEPLOY_KEY`, and paste the private SSH key. +1. In your _website repo_, [create a new deploy key](https://docs.github.com/en/developers/overview/managing-deploy-keys#setup-2) with write/push access named `DEPLOY_KEY`, and paste the public SSH key. ### Set up analytics @@ -155,17 +158,17 @@ e.g. `your-domain.com/some-link` In summary: 1. Purchase a domain name from a reputable service. -2. Point your domain name provider to GitHub Pages using an `A` record. +1. Point your domain name provider to GitHub Pages using an `A` record. This is slightly different for each company; they should have their own instructions on how to do it. -3. Set the custom domain field in the "Pages" settings of your _website repo_ (automatically creates a `CNAME` file in the repo). -4. After a minute or so, GitHub should tell you that your site is now being hosted at `your-domain.com`. +1. Set the custom domain field in the "Pages" settings of your _website repo_ (automatically creates a `CNAME` file in the repo). +1. After a minute or so, GitHub should tell you that your site is now being hosted at `your-domain.com`. #### GitHub user/org site e.g. `your-org.github.io/some-link` 1. Name your _website repo_ `your-org.github.io` to match your GitHub user/organization name. -2. In your _redirects repo_, change `redirects-website` in `deploy.yaml` to the same name. +1. In your _redirects repo_, change `redirects-website` in `deploy.yaml` to the same name. [About GitHub user/org sites](https://docs.github.com/en/pages/getting-started-with-github-pages/about-github-pages#types-of-github-pages-sites). @@ -188,8 +191,8 @@ In your _website repo_: If you already have a website being hosted with GitHub Pages that you want to incorporate this approach into: 1. Skip templating the _website repo_. -2. Instead, copy its [`redirect.js` script](https://github.com/CU-DBMI/redirects-website/blob/main/redirect.js) into the **top folder** of your existing website repo, and modify `baseurl` in it as appropriate. -3. Include the script in your 404 page in the [same way it is done here](https://github.com/CU-DBMI/redirects-website/blob/main/404.html). +1. Instead, copy its [`redirect.js` script](https://github.com/CU-DBMI/redirects-website/blob/main/redirect.js) into the **top folder** of your existing website repo, and modify `baseurl` in it as appropriate. +1. Include the script in your 404 page in the [same way it is done here](https://github.com/CU-DBMI/redirects-website/blob/main/404.html). If an existing page and a redirect have same name/path, the redirect won't happen since the user won't get a [`404`](https://en.wikipedia.org/wiki/HTTP_404). If your existing website is built and hosted in a different way, this approach would require modification[^3] and might not be appropriate for you. diff --git a/check.js b/check.js index 191607e..a6a2160 100644 --- a/check.js +++ b/check.js @@ -12,6 +12,7 @@ async function checkList(list) { const response = await fetch(to); if ( // only fail on certain status codes that might indicate link is "broken" + // select as desired from https://en.wikipedia.org/wiki/List_of_HTTP_status_codes [ 400, 404, 405, 406, 408, 409, 410, 421, 500, 501, 502, 503, 504, ].includes(response.status) From fdbb799dbc9f74a4cf1c3548f223c89d066d8701 Mon Sep 17 00:00:00 2001 From: Vincent Rubinetti Date: Wed, 13 Mar 2024 12:50:42 -0400 Subject: [PATCH 4/5] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9105e16..ddc7a60 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ _Counterpart to the [redirects-website repo](../../../redirects-website)._ Verify that no errors occurred in the automatic process here: [![Encode and deploy](../../actions/workflows/deploy.yaml/badge.svg)](../../actions/workflows/deploy.yaml) 1. Verify that none of your redirect links are reported broken in the automatic process here: [![Check links](../../actions/workflows/check.yaml/badge.svg)](../../actions/workflows/check.yaml). Note that this is only a **rough check**. - There may be false positives and true negatives, as it simply checks the [status code](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes) of the link, which the third-party site may choose inappropriately. + There _may be false positives or true negatives_, as it simply checks the [status code](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes) of the link, which the third-party may choose inappropriately. You can do this [directly on github.com](../../edit/main/redirects.yaml) (tip: press . right now), or locally with git. From e9b99a63ff1b1c2b01af10865a7f9ed9052ddf9e Mon Sep 17 00:00:00 2001 From: Vincent Rubinetti Date: Wed, 13 Mar 2024 12:53:33 -0400 Subject: [PATCH 5/5] force exit(1) so check workflow fails --- core.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core.js b/core.js index 743d2a0..e0f0388 100644 --- a/core.js +++ b/core.js @@ -102,9 +102,9 @@ export function addError(error) { export function onExit() { process.on("exit", () => { if (errors.length) { - process.exitCode = 1; errors.forEach(logError); logError(`${errors.length} error(s)`); + process.exit(1); } else { process.exitCode = 0; log("No errors!");