Start adding a set of automated scripts
- ID
0b2d5d6- date
2025-11-27 03:44:08+00:00- author
Alex Chan <alex@alexwlchan.net>- parent
4aaf80a- message
Start adding a set of automated scripts- changed files
Changed files
.github/dependabot.yml (0) → .github/dependabot.yml (243)
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..9452b32
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,12 @@
+version: 2
+updates:
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "daily"
+ time: "09:00"
+ - package-ecosystem: "pip"
+ directory: "/"
+ schedule:
+ interval: "daily"
+ time: "09:00"
.github/workflows/test.yml (0) → .github/workflows/test.yml (709)
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..a09eede
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,41 @@
+name: Test
+
+on:
+ push:
+ branches:
+ - main
+
+ pull_request:
+ branches:
+ - main
+
+jobs:
+ test:
+ strategy:
+ matrix:
+ python-version:
+ - "3.12"
+ - "3.13"
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: ${{ matrix.python-version }}
+ cache: 'pip'
+ cache-dependency-path: 'dev_requirements.txt'
+
+ - name: Install dependencies
+ run: pip install -r dev_requirements.txt
+
+ - name: Check formatting
+ run: |
+ ruff check .
+ ruff format --check .
+
+ - name: Check types
+ run: mypy scripts/*.py --strict
css/syntax_highlighting.1c4c93a.css (0) → css/syntax_highlighting.1c4c93a.css (3311)
diff --git a/css/syntax_highlighting.1c4c93a.css b/css/syntax_highlighting.1c4c93a.css
new file mode 100644
index 0000000..c2afd1a
--- /dev/null
+++ b/css/syntax_highlighting.1c4c93a.css
@@ -0,0 +1,130 @@
+/* Syntax highlighting styles. */
+pre {
+ --red: var(--default-primary-color-light);
+ --green: #1bad0e;
+ --blue: #115bda;
+ --magenta: #c311d0;
+ --yellow: #c8a711;
+
+ --comments: var(--red);
+ --literals: var(--magenta);
+ --strings: var(--green);
+ --names: var(--blue);
+ --punctuation: var(--accent-grey);
+ --mark: #ffeb12b3;
+
+ @media (prefers-color-scheme: dark) {
+ --red: var(--default-primary-color-dark);
+ --green: #5ff042;
+ --blue: #40c3ff;
+ --magenta: #ff42fc;
+ --yellow: #fffc42;
+ --mark: #fffc42cc;
+ }
+
+ /* Comment, Comment.{Hash, Multiline, Single, Special} */
+ .c, .ch, .cm, .c1, .cs {
+ color: var(--comments);
+ }
+
+ /* Numbers and booleans:
+ * Literal.Number, Literal.Number.{Bin, Float, Hex, Integer, Oct},
+ * Integer.Long,
+ * Keyword.{Constant, Reserved}
+ * Name.Constant
+ */
+ .m, .mb, .mf, .mh, .mi, .mo, .il, .kc, .bp, .no {
+ color: var(--literals);
+ }
+
+ /* Strings:
+ * Literal.String, Literal.String.{Affix, Backtick, Char, Delimiter,
+ * …, Doc, Escape, Double, Heredoc, Interpol, Other, Regex, Single,
+ * …, Symbol} */
+ .s, .sa, .sb, .sc, .dl, .sd, .se, .s2, .sh, .si, .sx, .sr, .s1, .ss {
+ color: var(--strings);
+ }
+
+ /* Name */
+ .n { color: var(--names); }
+
+ /* Punctuation, Generic.Output */
+ .p, .go {
+ color: var(--punctuation);
+ }
+
+ /* Rust-specific errors */
+ .rustc_error { color: var(--red); font-weight: bold; }
+ .rustc_warning { color: var(--green); }
+ .rustc_value { color: var(--yellow); }
+ .rustc_lineno { color: var(--blue); }
+
+ mark {
+ background-color: var(--mark);
+ padding: 2px 0;
+ margin: -2px 0;
+ border-radius: 2px;
+ }
+}
+
+/* In console snippets:
+ *
+ * - don't allow selecting the $ or the following space
+ * See https://alexwlchan.net/2021/10/console-copying/
+ * See https://stackoverflow.com/a/4407335/1558022
+ * - color the prompt blue
+ *
+ * gp = Generic.Prompt, w = Whitespace
+ */
+.language-console .gp,
+.language-console .gp + .w,
+.language-irb .gp,
+.language-irb .gp + .w {
+ color: var(--blue);
+ -webkit-touch-callout: none; /* iOS Safari */
+ -webkit-user-select: none; /* Safari */
+ -khtml-user-select: none; /* Konqueror HTML */
+ -moz-user-select: none; /* Firefox */
+ -ms-user-select: none; /* Internet Explorer/Edge */
+ user-select: none; /* Non-prefixed version, currently
+ supported by Chrome and Opera */
+}
+
+/* Don't highlight the XML declaration at the top of an XML snippet.
+ * cp = Comment.Preproc*/
+.language-xml .cp {
+ color: var(--punctuation);
+}
+
+/* All my Caddy and Go-formatted code uses tabs; ensure it's a 4-wide tab
+ * rather than 8-wide, which is the default stylesheet in Safari */
+.language-caddy,
+.language-go {
+ tab-size: 4;
+}
+
+/* Highlight HTML tags in blue.
+ * Name.{Tag, Attribute} */
+.language-html {
+ .nt, .na {
+ color: var(--names);
+ }
+}
+
+.language-css {
+ /* Don't highlight media queries. */
+ .n {
+ color: var(--body-text);
+ }
+
+ /* Highlight CSS colours in magenta. Name.Constant */
+ .no {
+ color: var(--literals);
+ }
+
+ /* Highlight selectors and properties in blue.
+ * Name.{Class, Tag, Literal, Property} */
+ .nc, .nt, .nl, .py {
+ color: var(--names)
+ };
+}
css/variables.2477498.scss (0) → css/variables.2477498.scss (4005)
diff --git a/css/variables.2477498.scss b/css/variables.2477498.scss
new file mode 100644
index 0000000..69a46fb
--- /dev/null
+++ b/css/variables.2477498.scss
@@ -0,0 +1,126 @@
+@use "sass:color";
+@use "utils/functions.scss" as *;
+
+/* Why 751px and not 750px? To work around a subpixel bug in WebKit,
+ * where I get a hairline crack on the right-hand side of my cards
+ * in the two column view. :( */
+$max-width: 751px;
+$default-padding: 20px;
+
+:root {
+ /* ====================
+ * Font and text styles
+ * ==================== */
+ --text-font-family: Charter, Georgia, Palatino, 'Palatino Linotype', Times, 'Times New Roman', serif;
+ --mono-font-family: Menlo, Consolas, monospace;
+
+ --font-size: 13pt;
+
+ --line-height: 1.5em;
+
+ --meta-scaling-factor: 0.82;
+ --footnote-scaling-factor: 0.95;
+
+ /* =============================
+ * Margins and page layout stuff
+ * ============================= */
+ --max-width: #{$max-width};
+
+ --grid-gap: 10px;
+
+ --default-padding: #{$default-padding};
+
+ --border-radius: 10px;
+ --border-style: solid;
+ --border-width: 3px;
+
+ /* Every page has a tint color that affects the nav, links, headings,
+ * and so on.
+ *
+ * Set a default color for every page, which may be overwritten by CSS
+ * appended to the <head>.
+ */
+ --default-primary-color-light: #d01c11;
+ --default-primary-color-dark: #f45858;
+
+ --primary-color-light: var(--default-primary-color-light);
+ --primary-color-dark: var(--default-primary-color-dark);
+
+ --nav-bg-image-light: url('/h/d01c11.png');
+ --nav-bg-image-dark: url('/h/ff4242.png');
+
+ --link-color: var(--primary-color);
+
+ /* A collection of light/dark greys tones.
+ *
+ * Every so often I wonder about rationalising the number of grey tones that
+ * I'm using, but I haven't found a selection that I like yet.
+ */
+ --body-text-light: #202020;
+ --body-text-dark: #c7c7c7;
+
+ --accent-grey-light: #999;
+ --accent-grey-dark: #9a9a9a;
+
+ --block-border-color-light: #dfdfdf;
+ --block-background-light: #f3f3f3;
+
+ --block-border-color-dark: #434343;
+ --block-background-dark: #1d1d1d;
+
+ --screenshot-border-light: #f0f0f0;
+ --screenshot-border-dark: #3f3f3f;
+
+ /* My background texture image is "White Waves Pattern" by Stas Pimenov,
+ * with reduced contrast. The dark variant has inverted colours.
+ *
+ * From https://www.toptal.com/designers/subtlepatterns/white-waves-pattern/
+ */
+ --background-texture-light: url("/static/white-waves.png");
+ --background-color-light: #fafafa;
+
+ --background-texture-dark: url("/static/black-waves.png");
+ --background-color-dark: #0d0d0d;
+}
+
+/* Select the appropriate light/dark shades depending on the user's theme.
+ *
+ * I could use the `light-dark()` CSS function, but it's fairly new and
+ * this approach is fine, so I'm sticking with media queries for now.
+ * See https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/light-dark
+ */
+:root {
+ --body-text: var(--body-text-light);
+ --primary-color: var(--primary-color-light);
+ --accent-grey: var(--accent-grey-light);
+ --block-border-color: var(--block-border-color-light);
+ --block-background: var(--block-background-light);
+ --screenshot-border: var(--screenshot-border-light);
+ --background-image: var(--background-texture-light);
+ --background-color: var(--background-color-light);
+ --nav-bg-image: var(--nav-bg-image-light);
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --body-text: var(--body-text-dark);
+ --primary-color: var(--primary-color-dark);
+ --accent-grey: var(--accent-grey-dark);
+ --block-border-color: var(--block-border-color-dark);
+ --block-background: var(--block-background-dark);
+ --screenshot-border: var(--screenshot-border-dark);
+ --background-image: var(--background-texture-dark);
+ --background-color: var(--background-color-dark);
+ --nav-bg-image: var(--nav-bg-image-dark);
+ }
+}
+
+@media print {
+ :root {
+ --accent-grey: #555;
+
+ /* Don't show the background texture when printing; it's not important */
+ --background-image: none;
+ --background-color: none;
+ }
+}
dev_requirements.in (0) → dev_requirements.in (10)
diff --git a/dev_requirements.in b/dev_requirements.in
new file mode 100644
index 0000000..8881953
--- /dev/null
+++ b/dev_requirements.in
@@ -0,0 +1,2 @@
+mypy
+ruff
dev_requirements.txt (0) → dev_requirements.txt (341)
diff --git a/dev_requirements.txt b/dev_requirements.txt
new file mode 100644
index 0000000..d5b2b08
--- /dev/null
+++ b/dev_requirements.txt
@@ -0,0 +1,12 @@
+# This file was autogenerated by uv via the following command:
+# uv pip compile dev_requirements.in --output-file dev_requirements.txt
+mypy==1.18.2
+ # via -r dev_requirements.in
+mypy-extensions==1.1.0
+ # via mypy
+pathspec==0.12.1
+ # via mypy
+ruff==0.14.6
+ # via -r dev_requirements.in
+typing-extensions==4.15.0
+ # via mypy
scripts/palette.py (0) → scripts/palette.py (228)
diff --git a/scripts/palette.py b/scripts/palette.py
new file mode 100644
index 0000000..7985b7c
--- /dev/null
+++ b/scripts/palette.py
@@ -0,0 +1,16 @@
+from typing import TypedDict
+
+
+class Colours(TypedDict):
+ red: str
+ green: str
+ blue: str
+ magenta: str
+ yellow: str
+ highlight: str
+
+
+class Palette(TypedDict):
+ id: str
+ light: Colours
+ dark: Colours
scripts/vendor_css_files.py (0) → scripts/vendor_css_files.py (1999)
diff --git a/scripts/vendor_css_files.py b/scripts/vendor_css_files.py
new file mode 100755
index 0000000..cc26aae
--- /dev/null
+++ b/scripts/vendor_css_files.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python3
+"""
+Copy a new set of CSS files from a local checkout of my website, and
+create a `palette.json` in the root of the repo.
+"""
+
+import glob
+from pathlib import Path
+import re
+import shutil
+import subprocess
+
+
+def get_alexwlchan_net_css(css_name: str) -> tuple[str, str]:
+ """
+ Get a copy of a CSS file from a local checkout of my website.
+
+ This returns a tuple: the commit ID and CSS text.
+ """
+ repo_path = Path.home() / "repos/alexwlchan.net"
+ css_path = repo_path / "src/_scss" / css_name
+
+ # Get the commit ID of the last change to this file
+ output = subprocess.check_output(
+ ["git", "rev-list", "-1", "HEAD", "--", str(css_path)],
+ text=True,
+ cwd=repo_path,
+ )
+ commit_id = output[:7]
+
+ vendor_path = Path("css") / f"{css_path.stem}.{commit_id}{css_path.suffix}"
+ vendor_path.parent.mkdir(exist_ok=True)
+
+ # If we don't have a vendored copy of the file in this repo, delete
+ # any previously-vendored copies then copy in the new version.
+ if not vendor_path.exists():
+ for f in glob.glob("syntax_highlighting.*.scss"):
+ Path(f).unlink()
+
+ shutil.copyfile(css_path, vendor_path)
+
+ return commit_id, vendor_path.read_text()
+
+
+def get_colour_variable(css: str, *, name: str) -> str:
+ """
+ Extracts a CSS variable from a snippet of CSS.
+ """
+ m = re.search(f"--{name}:" + r"\s+(?P<colour>#[0-9a-f]{6}([0-9a-f]{2})?);", css)
+ assert m is not None
+ return m.group("colour")
+
+
+if __name__ == "__main__":
+ variable_id, variable_css = get_alexwlchan_net_css("variables.scss")
+ syntax_id, syntax_css = get_alexwlchan_net_css("components/syntax_highlighting.css")
+
+ # Get the default primary colour, which is used for my two shades
+ # of red.
+ light_red = get_colour_variable(variable_css, name="default-primary-color-light")
+ dark_red = get_colour_variable(variable_css, name="default-primary-color-dark")
+
+ print(light_red)
+ print(dark_red)