Skip to content

Commit

Permalink
Merge pull request #40 from stuartlangridge/main
Browse files Browse the repository at this point in the history
Add past events to the website
  • Loading branch information
Waterdrips authored Aug 7, 2023
2 parents 2093bd5 + 9247fb7 commit 5017f07
Show file tree
Hide file tree
Showing 215 changed files with 688 additions and 1 deletion.
39 changes: 39 additions & 0 deletions link-to-the-past/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
_ _ _ _ _ _ ___ ____ ___ _ _ ____ ___ ____ ____ ___
| | |\ | |_/ | | | | |__| |___ |__] |__| [__ |
|___ | | \| | \_ | |__| | | | |___ | | | ___] |

# Building "past" versions of the conf.techmids.io.website


The conf.techmids.io website ought to have all the previous versions of the website available for browsing, so people can see what went on and who spoke at each previous event.

That's what this is for.

Every time there's a new TechMids conference, the website changes to be about that particular conference. This means that the previous conference website disappears. That's a shame, because it's useful to have that previous website around somewhere, so people can still reference it: to see who spoke and what they spoke about and to see details of the previous conference. It's also a small bit of extra advertising: TechMids looks better if the previous events are accessible because it shows that the conference has happened multiple times, which demonstrates consistency and staying power.

So, that's what this extra script does.

The idea here is that, when a TechMids event has happened, this script is run to "archive" the current state of the website; it will then be available at conf.techmids.io/past/2023-06/ or similar. The script is run once to archive the current version of the website; after this, the site can then safely be changed to reference and describe the _next_ upcoming event. The script doesn't need to be run every time or on every build; it's a specific action, taken on the day that the website starts being updated for a new event.

To run the script, do:

`npm run link-to-the-past 2023-06`

The date specified there is in `YYYY-MM` format, and is the date that the event on the website actually happened. (In particular, it is not the date that you're running it now.)

An example may make things clearer. Let's say there was a TechMids conf in June 2023. The website is all about this event, showing the speakers, sponsors, and details. The event has happened, and it's now July 2023, and you're in charge of updating the website to start talking about the _next_ TechMids conf in October 2023. So, before you do that updating, you run `npm run link-to-the-past 2023-06`. This will archive a static copy of the website as it currently looks in `/past/2023-06`, and that static copy will remain untouched forever. You can now update the website to describe the October event, and the July 2023 event's site will remain forever, frozen in carbonite, for people to reference.

## Technical details

Here we describe what the script actually _does_. You don't need this information to run the script, although you will do if you want to fiddle with it.

Essentially, the script's goal is to create a completely static version of the website as it currently exists, and put it in the `public/past` folder. It does this by:

1. moving `public/past` out of the way, so that the static version of this website does not itself include all the other past versions
2. editing `next.config.js` to describe how static versions of the site should be made
3. running `next build` to create a static version of the site according to the `next.config.js` changes, in a temporary directory
4. putting `public/past` back where it came from
5. undoing its changes to `next.config.js`
6. moving the newly-created static dump into `public/past` under the specified name, which should look like a date

Most of the script is concerned with reporting on what it's doing, and coping with errors by undoing its invasive changes.
23 changes: 23 additions & 0 deletions link-to-the-past/command-line.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
function complain() {
console.log(`Syntax: npm run link-to-the-past 2023-10
That is: you run this with a date, in YYYY-MM format.
That will take the current version of the website, and
create a /past/2023-10/ folder which is a static version
of the website, for later viewing.
The date should be the date of the conference, not the date today!`);
process.exit(1);
}

export function get_valid_output_name() {
// read the command line to get the output folder name
// and complain if it's not right or not present
if (process.argv.length < 3) {
complain();
}
const dt = process.argv[2];
if (!/20[0-9][0-9]-[01][0-9]/.test(dt)) {
console.log(`Problem: "${dt}" does not look like a proper YYYY-MM date.\n`);
complain();
}
return dt;
}
43 changes: 43 additions & 0 deletions link-to-the-past/header.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const link = ['CCCCCCCCCCCCCCCC', 'CCCCCCJJJJCCCCCC', 'CCCCCJAAFAJJCCCC',
'CCCCJAAFFFFAJCCC', 'CJCJJGAAFAGJAJJC', 'JLJEJJGGGGJJAJLJ',
'JLJEEEJJJJEEJJLJ', 'JDLJEEEEEEEEJLDJ', 'JDJEJJJJJJJJEJDJ',
'CJDJDNNDDNNDJDJC', 'CCJDDNJLLJNDDJCC', 'CCJJDLJLLJLDJJCC',
'CJIBJDLLLLDJBIJC', 'JOJBBJJDDJJBBJOJ', 'JOOJBIBJJBIBJOOJ',
'JHJJBBIIIIIBJJHJ', 'JHHJBBBIIBBBJHHJ', 'JHJBMMJMMJMMBJHJ',
'CJJJBBMMMMBBJJJC', 'CCJKJJBBBBJJKJCC', 'CCCJGKJJJJKGJCCC',
'CCCCJJJCCJJJCCCC', 'CCCCCCCCCCCCCCCC', 'CCCCCCCCCCCCCCCC'];
const cols = {'A': 64, 'B': 66, 'C': 231, 'D': 136, 'E': 169,
'F': 106, 'G': 172, 'H': 173, 'I': 78, 'J': 16, 'K': 124,
'L': 180, 'M': 185, 'N': 188, 'O': 94};

const lines = [
"_ _ _ _ _ _ ___ ____ ___ _ _ ____ ___ ____ ____ ___ ",
"| | |\\ | |_/ | | | | |__| |___ |__] |__| [__ | ",
"|___ | | \\| | \\_ | |__| | | | |___ | | | ___] | ",
"",
"The conf.techmids.io website ought to have all the previous",
"versions of the website available for browsing, so people",
"can see what went on and who spoke at each previous event.",
"",
"That's what this is for.",
"We shall now dump the current website into a folder, ready to",
"move it into the /past/ folder on the website!",
" ...please hold on while we do the necessary..."
]

export function header() {
const output = [];
for (let i=0; i<link.length; i+=2) {
const outline = [];
for (let x=0; x<link[i].length; x++) {
const top = cols[link[i][x]];
const bottom = cols[link[i+1][x]];
outline.push(`\x1b[38;5;${top};48;5;${bottom}m▀\x1b[0m`);
}
output.push(outline.join("") + " " + (lines[i/2] || ""));
}
console.log(output.join("\n"));
console.log("");
}


36 changes: 36 additions & 0 deletions link-to-the-past/link-to-the-past.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import path from 'path';

import { header } from "./header.mjs";
import { get_valid_output_name } from './command-line.mjs';
import { restore_their_config, rollback, copy_config } from './tidyup.mjs';
import { get_paths, move_past_dir_aside,
move_past_dir_back, move_export_into_past } from './paths.mjs';
import { export_site, assure_output_dir, write_our_config } from './make-changes.mjs';

async function remove_temporary_export(paths) {
console.log(`Removing temporary export directory ${paths.temp_export_dir}`);
await fs.rm(paths.temp_export_dir, {recursive: true});
}

async function main() {
const paths = await get_paths();
try {
header();
const output_name = get_valid_output_name();
await copy_config(paths);
await write_our_config(paths, output_name);
await move_past_dir_aside(paths);
await assure_output_dir(paths);
await export_site(paths);
await move_past_dir_back(paths);
await move_export_into_past(paths, output_name);
await restore_their_config(paths);
} catch(e) {
console.error("There was an error, sorry.");
await rollback(e, paths);
console.error("I shall now abort. The error in the script was this:");
console.log(e);
}
}

main();
62 changes: 62 additions & 0 deletions link-to-the-past/make-changes.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import * as fs from 'node:fs/promises';
import { R_OK, W_OK } from 'node:fs';
import { spawn } from 'child_process';

export async function write_our_config(paths, output_name) {
/* A custom next.config.js file
There's no provision for passing a separate file, so
we overwrite the main one (and put it back later).
Image optimisation requires a server, so it won't work
in a pure static HTML export, and thus we disable it.
*/
const ourConfig = `
/** @type {import('next').NextConfig} */
const nextConfig = {
// reactStrictMode: true,
output: 'export',
distDir: '${paths.temp_export_dir_short}',
basePath: '/past/${output_name}',
images: {
unoptimized: true,
}
}
module.exports = nextConfig
`;
await fs.writeFile(paths.next_config_js, ourConfig);
console.log("Written our custom link-to-the-past config");
}

export async function assure_output_dir(paths) {
console.log(`Confirming that we can write to the output directory ${paths.temp_export_dir}`);
let output_exists;
try {
output_exists = await fs.access(paths.temp_export_dir, R_OK | W_OK);
} catch(e) {
if (e.code == "ENOENT") {
// the output directory doesn't exist; this is fine
} else {
throw new Error(
`We cannot write to ${paths.temp_export_dir}. ` +
"This is not supposed to happen, and is a bug in this script.");
}
}
if (output_exists) {
console.log("Removing output directory");
await fs.rm(paths.temp_export_dir, {recursive: true});
}
}

export function export_site(paths) {
return new Promise((resolve, reject) => {
console.log(`Exporting the website as static to ${paths.temp_export_dir}`);
const proc = spawn("next", ["build"]);
proc.stdout.on("data", data => { console.log(data.toString().trimEnd()); })
proc.stderr.on("data", data => { console.error(data.toString().trimEnd()); })
proc.on("close", code => {
if (code === 0) { resolve(); }
reject();
})
proc.on("error", err => { reject(err); })
})
}
6 changes: 6 additions & 0 deletions link-to-the-past/next.config.js.bak
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
// reactStrictMode: true,
}

module.exports = nextConfig
68 changes: 68 additions & 0 deletions link-to-the-past/paths.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { fileURLToPath } from 'url';
import * as fs from 'node:fs/promises';
import { R_OK, W_OK } from 'node:fs';
import path from 'path';

export async function get_paths() {
const __filename = fileURLToPath(import.meta.url);
const script_folder = path.dirname(__filename);
const root_folder = path.dirname(script_folder);
const next_config_js = path.resolve(path.join(script_folder, "..", "next.config.js"));
try {
const ncj_exists = await fs.access(next_config_js, R_OK | W_OK);
} catch(e) {
throw new Error(
`Expected to find the next config in ${next_config_js}, but we didn't.\n` +
"This isn't supposed to happen, and is a bug in this script.\n" +
`(The actual error we got was this: ${e})`);
}
const public_dir = path.resolve(path.join(script_folder, "..", "public"));
try {
const public_dir_exists = await fs.access(public_dir, R_OK | W_OK);
} catch(e) {
throw new Error(
`Expected to find the public resources dir in ${public_dir}, but we didn't.\n` +
"This isn't supposed to happen, and is a bug in this script.");
}
const public_past_dir = path.join(public_dir, "past");
await fs.mkdir(public_past_dir, {recursive: true});

const temp_export_dir = path.join(script_folder, "export");
// the short version of the temp export dir is used in the next.config.js file
// because you can't provide a full path; nextjs assumes the folder supplied is
// inside the project root, so it ends up creating a home/you/blah/whatever/link-to-the-past/export
// folder, irritatingly. So we make the short version here.
const temp_export_dir_short = path.relative(root_folder, temp_export_dir);
const temp_past_location = path.join(script_folder, "temp_past");

return {
next_config_js,
next_config_js_backup: path.join(script_folder, "next.config.js.bak"),
script_folder,
public_past_dir,
temp_export_dir,
temp_export_dir_short,
temp_past_location
}
}

export async function move_past_dir_aside(paths) {
// put the real past dir from public temporarily out of the way
// this ensures that when we generate the static version of
// the website as it looks today, it doesn't have all the other
// past versions in it!
// this also avoids recursive issues where we're generating a static
// version of the website while also writing into it, which causes
// infinite nested /public/past/date/past/date/past/date/... problems
console.log(`Moving the 'past' folder out of the way, from ${paths.public_past_dir} to ${paths.temp_past_location}`);
await fs.rename(paths.public_past_dir, paths.temp_past_location);
}
export async function move_past_dir_back(paths) {
console.log(`Moving the 'past' folder back, from ${paths.temp_past_location} back to ${paths.public_past_dir}`);
await fs.rename(paths.temp_past_location, paths.public_past_dir);
}
export async function move_export_into_past(paths, output_name) {
const output_dir = path.join(paths.public_past_dir, output_name);
console.log(`Moving the newly exported static website to ${output_dir}`);
await fs.rename(paths.temp_export_dir, output_dir);
}
38 changes: 38 additions & 0 deletions link-to-the-past/tidyup.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import * as fs from 'node:fs/promises';
import { move_past_dir_back } from './paths.mjs';

export async function restore_their_config(paths, only_if_older=false) {
const ncj_stat = await fs.stat(paths.next_config_js);
let ncj_bak_stat;
try {
ncj_bak_stat = await fs.stat(paths.next_config_js_backup);
} catch(e) {
ncj_bak_stat = {ctimeMs: 0}
}
if ((only_if_older && ncj_stat.mtimeMs < ncj_bak_stat.mtimeMs) || !only_if_older) {
await fs.copyFile(paths.next_config_js_backup, paths.next_config_js);
console.log("Restored the original next.config.js successfully");
}
}

export async function rollback(script_err, paths) {
console.log("I am attempting to restore things after the error.");
try {
await restore_their_config(paths, true);
await move_past_dir_back(paths);
} catch(e) {
console.error("There was an error in the script:");
console.error(script_err);
console.error("In trying to recover from that error, there was ANOTHER error:");
console.error(e);
console.error("This is not great, and things may have been left in an inconsistent state.");
console.error("All I can do is abort.");
process.exit(2);
}
}

export async function copy_config(paths) {
console.log("Backing up the next.config.js before we fiddle with it...");
await fs.copyFile(paths.next_config_js, paths.next_config_js_backup);
}

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
"lint": "next lint",
"link-to-the-past": "node link-to-the-past/link-to-the-past.mjs"
},
"browserslist": "defaults, not ie <= 11",
"dependencies": {
Expand Down
Loading

1 comment on commit 5017f07

@vercel
Copy link

@vercel vercel bot commented on 5017f07 Aug 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.