Skip to content

Commit

Permalink
Meta: Add a warning to PR previews
Browse files Browse the repository at this point in the history
  • Loading branch information
gibson042 committed Dec 27, 2024
1 parent 9e9299f commit 3be02d5
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 2 deletions.
22 changes: 21 additions & 1 deletion .github/workflows/publish-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,31 @@ jobs:
id: extract-pr-number
run: |
cd result
awk -vok=1 \
awk -v ok=1 \
'{ print; if(!match($0, /^[1-9][0-9]*$/)) ok=0; } END { exit !(NR==1 && ok); }' \
pr-number.txt
echo "number=$(cat pr-number.txt)" >> $GITHUB_OUTPUT
rm pr-number.txt
- name: Insert preview warning
env:
PR: ${{ steps.extract-pr-number.outputs.number }}
run: |
tmp="$(mktemp -u XXXXXXXX.json)"
repo_url="https://github.com/$GITHUB_REPOSITORY"
commit="$(git rev-parse --verify HEAD)"
jq -n --arg repo_url "$repo_url" --arg PR "$PR" --arg commit "$commit" '
def repo_link($args): $args as [$path, $contents]
| ($repo_url + ($path // "")) as $url
| "<a href=\"\($url | @html)\">\($contents // $url)</a>";
{
SUMMARY: "PR #\($PR)",
REPO_LINK: repo_link([]),
PR_LINK: repo_link(["/pull/" + $PR, "PR #\($PR)"]),
COMMIT_LINK: ("commit " + repo_link(["/commit/" + $commit, "<code>\($commit)</code>"])),
}
' > "$tmp"
find result -name '*.html' -exec \
node scripts/insert_warning.mjs --strict scripts/pr_preview_warning.html "$tmp" '{}' '+'
- name: Publish to gh-pages
uses: JamesIves/[email protected]
with:
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"license": "MIT",
"devDependencies": {
"@tc39/ecma262-biblio": "^2.1.2775",
"ecmarkup": "^20.0.0"
"ecmarkup": "^20.0.0",
"jsdom": "^25.0.1"
},
"engines": {
"node": ">= 12"
Expand Down
73 changes: 73 additions & 0 deletions scripts/insert_warning.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import fs from 'node:fs';
import pathlib from 'node:path';
import { parseArgs } from 'node:util';
import { JSDOM, VirtualConsole } from 'jsdom';

const { positionals: cliArgs, values: cliOpts } = parseArgs({
allowPositionals: true,
options: {
strict: { type: 'boolean' },
},
});
if (cliArgs.length < 3) {
const self = pathlib.relative(process.cwd(), process.argv[1]);
console.error(`Usage: node ${self} [--strict] <template.html> <data.json> <file.html>...
{{identifier}} substrings in template.html are replaced from data.json, then
the result is inserted at the start of the body element in each file.html.`);
process.exit(64);
}

const main = async (args, options) => {
const [templateFile, dataFile, ...files] = args;
const { strict } = options;

// Evaluate the template and parse it into nodes for inserting.
// Everything will be prepended to body elements except metadata elements,
// which will be appended to head elements.
// https://html.spec.whatwg.org/multipage/dom.html#metadata-content-2
const metadataNames =
'base, link, meta, noscript, script, style, template, title'
.toUpperCase()
.split(', ');
const template = fs.readFileSync(templateFile, 'utf8');
const { default: data } =
await import(pathlib.resolve(dataFile), { with: { type: 'json' } });
const namePatt = /[{][{]([\p{ID_Start}$_][\p{ID_Continue}$]*)[}][}]/gu;
const resolved = template.replaceAll(namePatt, (_, name) => {
if (Object.hasOwn(data, name)) return data[name];
if (strict) throw Error(`no data for {{${name}}}`);
return '';
});
const headInserts = [], bodyInserts = [];
let insertDom = JSDOM.fragment(resolved);
for (const node of insertDom.childNodes) {
if (metadataNames.includes(node.nodeName)) headInserts.push(node);
else bodyInserts.push(node);
}

// Perform the insertions, suppressing JSDOM warnings from e.g. unsupported
// CSS features.
const virtualConsole = new VirtualConsole();
virtualConsole.on('error', () => {});
const jsdomOpts = { contentType: 'text/html; charset=utf-8', virtualConsole };
const getInserts =
files.length > 1 ? nodes => nodes.map(n => n.cloneNode(true)) : x => x;
const results = await Promise.allSettled(files.map(async file => {
let dom = await JSDOM.fromFile(file, jsdomOpts);
const { head, body } = dom.window.document;
if (headInserts.length > 0) head.append(...getInserts(headInserts));
if (bodyInserts.length > 0) body.prepend(...getInserts(bodyInserts));
fs.writeFileSync(file, dom.serialize(), 'utf8');
}));

const failures = results.flatMap(result =>
result.status === 'fulfilled' ? [] : [result.reason],
);
if (failures.length > 0) throw AggregateError(failures);
};

main(cliArgs, cliOpts).catch(err => {
console.error(err);
process.exit(1);
});
58 changes: 58 additions & 0 deletions scripts/pr_preview_warning.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<style>
details.annoying-warning {
position: fixed;
top: 0;
right: 0;
z-index: 10;
border: 2px solid white;
background-color: #920800;
background-image: linear-gradient(transparent 40%, rgba(255, 255, 255, 0.2));
color: white;
opacity: .95;
}

details.annoying-warning[open] {
top: 10%;
top: calc(5vw + 5vh);
left: 5%;
right: 5%;
margin: 0 auto;
max-width: 800px;
outline: solid 10000px rgba(255, 255, 255, 0.6);
border-radius: 3px;
border-width: 1px 1px 0 1px;
box-shadow: 0 0 0.5em rgba(0, 0, 0, 0.5);
}

details.annoying-warning > summary {
padding: 0.5ex 1ex;
font-size: 0.875em;
font-weight: bold;
letter-spacing: 0.02em;
text-align: center;
text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.85);
cursor: default;
}

details.annoying-warning > p {
line-height: 1.4;
margin: 1em;
text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.85);
}

details.annoying-warning a {
color: inherit;
text-decoration: underline;
}
</style>

<details class="annoying-warning">
<summary>{{SUMMARY}}</summary>
<p>
This document is a preview of merging {{PR_LINK}}, resulting in {{COMMIT_LINK}}.
</p>
<p>
Do not reference it as authoritative in any way.
Instead, see {{REPO_LINK}} for the living specification.
</p>
</details>

0 comments on commit 3be02d5

Please sign in to comment.