Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 32 additions & 5 deletions .github/scripts/check-colony-themes.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@
colony-accents.css sets the accent colours once for every theme, so a
variable it defines counts as defined everywhere, and every accent the
picker offers must be one of them.

Text on an accent fill (the selected contents entry, category and choice)
takes --colony-ink or --colony-paper, whichever contrasts more, as theme.js
does. That must reach 4.0:1 on every theme's own accent and on every accent
override.
"""

import json
Expand All @@ -20,21 +25,35 @@


def variables(css_file):
"""{selector: set of --colony-* names}, comments out first: the file
"""{selector: {--colony-* name: value}}, comments out first: the file
header quotes a selector."""
css = re.sub(r"/\*.*?\*/", "", (vendor / css_file).read_text(encoding="utf-8"), flags=re.S)
return {
selector.strip(): set(re.findall(r"(--colony-[a-z0-9-]+):", body))
selector.strip(): dict(re.findall(r"(--colony-[a-z0-9-]+):\s*([^;]+);", body))
for selector, body in re.findall(r"([^{}]+)\{([^}]*)\}", css)
}


def contrast(a, b):
"""WCAG contrast ratio of two #rrggbb colours, as theme.js computes it."""

def luminance(color):
r, g, b = (
c / 12.92 if c <= 0.03928 else ((c + 0.055) / 1.055) ** 2.4
for c in (int(color[i : i + 2], 16) / 255 for i in (1, 3, 5))
)
return 0.2126 * r + 0.7152 * g + 0.0722 * b

hi, lo = sorted((luminance(a), luminance(b)), reverse=True)
return (hi + 0.05) / (lo + 0.05)


themes = {
re.search(r'\[data-colony-theme="([^"]+)"\]', selector).group(1): defined
for selector, defined in variables("colony-themes.css").items()
if "[data-colony-theme=" in selector
}
accents = variables("colony-accents.css").get(":root", set())
accents = variables("colony-accents.css").get(":root", {})
theme_list = json.loads((vendor / "themes.json").read_text(encoding="utf-8"))
listed = {variant["slug"] for family in theme_list["families"] for variant in family["variants"]}

Expand All @@ -45,10 +64,18 @@ def variables(css_file):
if accent["cssVar"] not in accents:
errors.append(f"themes.json: accent {accent['key']} has no {accent['cssVar']} in colony-accents.css")
for path in sorted(ui.glob("*.*")):
for var in sorted(set(re.findall(r"--colony-[a-z0-9-]+", path.read_text(encoding="utf-8"))) - accents):
for var in sorted(set(re.findall(r"--colony-[a-z0-9-]+", path.read_text(encoding="utf-8"))) - accents.keys()):
missing = [slug for slug, defined in themes.items() if var not in defined]
if missing:
errors.append(f"{path.name}: {var} is missing from {', '.join(missing)}")
overrides = {accent["key"]: accents.get(accent["cssVar"]) for accent in theme_list["accents"]}
for slug, defined in themes.items():
for name, color in {"its own": defined.get("--colony-accent-blue"), **overrides}.items():
if not color or "--colony-ink" not in accents or "--colony-paper" not in accents:
continue # already reported above
best = max(contrast(accents[end], color) for end in ("--colony-ink", "--colony-paper"))
if best < 4.0:
errors.append(f"{slug}: text on {name} accent {color} reaches only {best:.2f}:1")

print("\n".join(errors) or f"OK: all {len(themes)} themes define every --colony-* variable in src/ui/")
print("\n".join(errors) or f"OK: all {len(themes)} themes define every --colony-* variable in src/ui/, and text on every accent reaches 4.0:1")
sys.exit(1 if errors else 0)
38 changes: 22 additions & 16 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ jobs:
with:
persist-credentials: false

# Pinned to master with `toolchain: stable` rather than to the `stable`
# branch: that branch is rebased onto every master commit, so a SHA
# pinned on it stops being reachable from any branch.
- uses: dtolnay/rust-toolchain@e2a55d2ffb04f378e9626c28d38b36d230d1e12f # master, 2026-10-09
with:
toolchain: stable
components: clippy, rustfmt
# rustup is on every GitHub-hosted runner image: no third-party action to
# pin, and nothing for Dependabot to lose track of.
- name: Install Rust
shell: bash
run: |
rustup toolchain install stable --profile minimal -c clippy,rustfmt --no-self-update
rustup default stable

- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with:
Expand Down Expand Up @@ -83,6 +83,10 @@ jobs:
# rust-version in Cargo.toml is a promise; this job is what keeps it. It
# builds and tests on exactly that toolchain, read from Cargo.toml so the
# two cannot drift apart.
#
# RUSTUP_TOOLCHAIN is what pins the cargo invocations: it outranks both the
# runner's rustup default and any rust-toolchain.toml. The rustc check makes
# a pin that silently stopped working fail loudly.
msrv:
name: MSRV
runs-on: ubuntu-latest
Expand All @@ -98,10 +102,10 @@ jobs:
version=$(sed -n 's/^rust-version = "\(.*\)"$/\1/p' Cargo.toml)
[ -n "$version" ] || { echo "::error::could not read rust-version from Cargo.toml"; exit 1; }
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "RUSTUP_TOOLCHAIN=$version" >> "$GITHUB_ENV"

- uses: dtolnay/rust-toolchain@e2a55d2ffb04f378e9626c28d38b36d230d1e12f # master, 2026-10-09
with:
toolchain: ${{ steps.msrv.outputs.version }}
- name: Install Rust ${{ steps.msrv.outputs.version }}
run: rustup toolchain install "$RUSTUP_TOOLCHAIN" --profile minimal --no-self-update

- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with:
Expand All @@ -113,7 +117,10 @@ jobs:
sudo apt-get install -y libwebkit2gtk-4.1-dev

- name: Build and test on the declared floor
run: cargo test --locked
run: |
rustc --version | grep -q "^rustc ${RUSTUP_TOOLCHAIN}[. ]" \
|| { echo "::error::expected rustc ${RUSTUP_TOOLCHAIN}, got $(rustc --version)"; exit 1; }
cargo test --locked

colony-themes:
name: Colony themes
Expand All @@ -122,10 +129,9 @@ jobs:
run:
shell: bash
env:
# A Project-Colony-Resources commit whose generated/ matches the one
# colony-ui 0.1.6 was published from. Bump it together with
# src/ui/vendor/colony/README.md.
COLONY_RESOURCES_REV: 7247b814fd9afbb17e3c4f44315ec9333c3a31b0
# The Project-Colony-Resources commit src/ui/vendor/colony/ is copied
# from. Bump it together with src/ui/vendor/colony/README.md.
COLONY_RESOURCES_REV: 8931f2b38422fe75757db6218a6bcbc8caa06a85

steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand All @@ -139,7 +145,7 @@ jobs:
curl -fsSL --retry 3 "$base/css/colony-accents.css" | cmp - src/ui/vendor/colony/colony-accents.css
curl -fsSL --retry 3 "$base/themes.json" | cmp - src/ui/vendor/colony/themes.json

- name: Every theme defines the colours src/ui/ uses
- name: Every theme defines the colours src/ui/ uses, and accent labels stay legible
run: python3 .github/scripts/check-colony-themes.py

# render() in src/ui/app.js is the only place a document's scripts, frames
Expand Down
16 changes: 9 additions & 7 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,15 @@ jobs:
ref: ${{ needs.release-please.outputs.tag_name || inputs.tag }}
persist-credentials: false

# Pinned to master with `toolchain: stable` rather than to the `stable`
# branch: that branch is rebased onto every master commit, so a SHA
# pinned on it stops being reachable from any branch.
- uses: dtolnay/rust-toolchain@e2a55d2ffb04f378e9626c28d38b36d230d1e12f # master, 2026-10-09
with:
toolchain: stable
targets: ${{ matrix.target }}
# rustup is on every GitHub-hosted runner image, so there is no
# third-party toolchain action to pin. Adding the target is a no-op on
# the legs that build for their own host; the Intel macOS leg needs it.
- name: Install Rust
shell: bash
run: |
rustup toolchain install stable --profile minimal --no-self-update
rustup default stable
rustup target add ${{ matrix.target }}

# No build cache here: SignPath forbids release builds that reuse
# outputs of earlier, unverified builds.
Expand Down
Loading
Loading