mirror of
https://git.pumpin.online/funkpower/typikon.git
synced 2026-10-22 18:57:13 +00:00
There is one engine and nine doors into it, and nothing until now checked that the doors agree. CI tested each binding against itself: the Python wheel imported, the C demo compiled, the npm package type-checked. None of that would have caught a field that crosses one boundary wrong, which is the failure this library is most exposed to, because a wrong answer about a fast day looks exactly like a right one. So: twelve days, chosen where the bindings could plausibly differ, and all twenty-two fields compared against what the command line says. The days are Pascha itself, the Nativity on both calendars, a Wednesday of the Great Fast under Byzantine Catholic law and again at the Typikon, a weekday of the Nativity Fast where Greek and Slavic usage part, the one Orthodox church on the Gregorian Paschalion, and a day with no feast. A release now runs what a push runs. It had been gated on cargo test alone, so the C ABI, the Lua binding, the Python parity check and the README examples were checked on the branch that led to a release and not on the release. Neither registry will reuse a version, so the gate belongs before the upload. Writing it found three things. The npm exports map did not expose the wasm by package path, so a bundler asked for typikon/web/typikon_bg.wasm was told no such path exists and had to reach into node_modules; the documented init() call was never affected. The suite could hang instead of fail, because the test web server was backgrounded inside a subshell and killed by job spec, leaving bash in do_wait forever. And pointing XDG_CONFIG_HOME at a path that does not exist isolates typikon and breaks every other program that keeps state under it, a browser included. A surface whose toolchain is missing is named as skipped, and fewer than three surfaces running is a failure rather than a pass, so a container with nothing installed cannot come out green.
137 lines
6.5 KiB
YAML
137 lines
6.5 KiB
YAML
name: ci
|
|
|
|
# Deliberately not triggered by pull_request.
|
|
#
|
|
# The forge this runs on is public and takes pull requests from strangers, and a
|
|
# runner executes whatever the workflow checks out. Triggering on pull_request
|
|
# would mean a stranger's first contribution runs code on the runner host before
|
|
# anyone has read it. Triggering only on push means the runner only ever executes
|
|
# code that someone with push rights put there.
|
|
#
|
|
# The cost is that a pull request gets no automatic check. Run it by pushing the
|
|
# branch yourself once you have read the diff.
|
|
on:
|
|
push:
|
|
# Branches only. A tag push does not match a branch filter, so release.yml
|
|
# owns tags and this owns everything else.
|
|
branches: ['**']
|
|
|
|
jobs:
|
|
test:
|
|
runs-on: docker
|
|
container:
|
|
image: rust:1.98-bookworm
|
|
steps:
|
|
# Not actions/checkout. That is a JavaScript action: the runner executes it
|
|
# with `node` *inside this container*, and a Rust image carries no node, so
|
|
# the job dies at the first step with "node: executable file not found in
|
|
# $PATH". actions/cache and actions/upload-artifact are the same. Using an
|
|
# image that also carries node would mean installing Rust on every run;
|
|
# cloning by hand costs four lines and keeps the lean image.
|
|
- name: Checkout
|
|
run: |
|
|
git init -q .
|
|
git remote add origin "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY"
|
|
git fetch -q --depth 1 origin "$GITHUB_SHA"
|
|
git checkout -q FETCH_HEAD
|
|
|
|
# No `cargo fmt --check`. The tables in this crate are aligned by hand —
|
|
# MONTHS, JURISDICTIONS — and rustfmt wants one entry per line, which loses
|
|
# the shape that makes them readable. A gate that fights the house style is
|
|
# a gate that gets ignored.
|
|
- name: Lint
|
|
run: |
|
|
rustup component add clippy
|
|
cargo clippy --all-targets -- -D warnings
|
|
# The binding code only exists under the feature, so a bare clippy
|
|
# run never looks at src/python.rs.
|
|
cargo clippy --features python --all-targets -- -D warnings
|
|
cargo clippy --features capi --all-targets -- -D warnings
|
|
rustup target add wasm32-unknown-unknown
|
|
cargo clippy --lib --features wasm --target wasm32-unknown-unknown -- -D warnings
|
|
|
|
- name: Rust tests
|
|
run: |
|
|
cargo test
|
|
cargo test --features capi
|
|
|
|
# The header and the LuaJIT cdef are checked against the library by the
|
|
# Rust tests; this checks that a C compiler agrees, which they cannot.
|
|
- name: C ABI
|
|
run: |
|
|
apt-get update && apt-get install -y --no-install-recommends gcc luajit
|
|
cargo build --release --features capi
|
|
cc -Wall -Wextra -Werror -Iinclude examples/c/demo.c \
|
|
-Ltarget/release -ltypikon -o /tmp/demo
|
|
LD_LIBRARY_PATH=target/release /tmp/demo rocor
|
|
LD_LIBRARY_PATH=target/release /tmp/demo --list
|
|
TYPIKON_LIB=target/release/libtypikon.so \
|
|
luajit examples/lua/statusline.lua rocor
|
|
|
|
# The CLI and the bindings are the same engine, so they have to agree. This
|
|
# is the check that caught the bindings resolving their own default
|
|
# jurisdiction while the CLI read the configured one.
|
|
- name: Python bindings
|
|
run: |
|
|
apt-get update && apt-get install -y --no-install-recommends python3 python3-venv python3-dev
|
|
python3 -m venv /tmp/venv
|
|
/tmp/venv/bin/pip install --quiet maturin pytest mypy
|
|
cargo build --release
|
|
/tmp/venv/bin/maturin build --release --out dist
|
|
/tmp/venv/bin/pip install --quiet --no-index --find-links dist typikon
|
|
/tmp/venv/bin/python -m pytest tests/python -q
|
|
# The bindings resolve the default jurisdiction themselves, and once
|
|
# read only the config file while the CLI read the environment first.
|
|
# The two then answered for different churches on the same machine.
|
|
cli=$(TYPIKON_JURISDICTION=rocor ./target/release/typikon \
|
|
--date 2026-03-18 --format tsv --fields jurisdiction | tail -1)
|
|
py=$(TYPIKON_JURISDICTION=rocor /tmp/venv/bin/python -c "import typikon, datetime; print(typikon.describe(datetime.date(2026, 3, 18)).jurisdiction)")
|
|
test "$cli" = "$py" || { echo "cli=$cli python=$py"; exit 1; }
|
|
|
|
- name: Type stubs
|
|
run: |
|
|
cat > /tmp/typecheck.py <<'EOF'
|
|
import datetime, typikon
|
|
d: typikon.Day = typikon.describe(datetime.date(2026, 4, 12))
|
|
day: datetime.date = d.date
|
|
feast: str | None = d.feast
|
|
n: int = d.days_to_pascha
|
|
EOF
|
|
/tmp/venv/bin/python -m mypy --strict /tmp/typecheck.py
|
|
|
|
# The npm package is the same engine again, so it has to agree with the
|
|
# command line again. The test compares a year of days field by field
|
|
# for four jurisdictions, and type-checks the published .d.ts the way the
|
|
# Python bindings are type-checked.
|
|
- name: JavaScript package
|
|
run: |
|
|
apt-get update && apt-get install -y --no-install-recommends nodejs npm
|
|
version=$(awk '/^name = "wasm-bindgen"$/{getline; gsub(/[",]/,""); print $3; exit}' Cargo.lock)
|
|
curl -sSfL "https://github.com/rustwasm/wasm-bindgen/releases/download/$version/wasm-bindgen-$version-x86_64-unknown-linux-musl.tar.gz" \
|
|
| tar xz -C /tmp
|
|
export PATH="/tmp/wasm-bindgen-$version-x86_64-unknown-linux-musl:$PATH"
|
|
npm install --global typescript@5
|
|
cargo build --release
|
|
npm/build.sh
|
|
node npm/test.mjs
|
|
|
|
- name: README examples
|
|
run: |
|
|
cargo build --release
|
|
python3 tests/readme-examples.py
|
|
|
|
# Every binding is the same engine reached by a different door, so the
|
|
# answers have to be identical, and the only way to know is to ask them
|
|
# all the same question. Twelve days chosen where they could plausibly
|
|
# differ, twenty-two fields each, against what the command line says.
|
|
#
|
|
# Chromium is installed for this: the web build is the one artifact whose
|
|
# real home is somewhere Node cannot stand in for, and loading it with
|
|
# Node's bytes proves the glue, not the fetch.
|
|
- name: Every binding agrees
|
|
run: |
|
|
apt-get install -y --no-install-recommends chromium
|
|
version=$(awk '/^name = "wasm-bindgen"$/{getline; gsub(/[",]/,""); print $3; exit}' Cargo.lock)
|
|
export PATH="/tmp/wasm-bindgen-$version-x86_64-unknown-linux-musl:$PATH"
|
|
tests/parity.sh
|