Skip to content

chore: bump version to 0.1.4 #85

chore: bump version to 0.1.4

chore: bump version to 0.1.4 #85

Workflow file for this run

name: CI
# Avoid running CI twice for branches with an open PR: `push` is
# scoped to the default branch and release tags (so merges to main
# and tag pushes still get CI), while feature branches get CI
# exclusively through `pull_request`.
on:
push:
branches: [main]
tags: ['v*']
pull_request:
# Cancel any still-running CI for the same workflow + ref when a new
# push lands; amend/force-push cycles no longer queue up obsolete runs.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
# Static checks + the dataset round-trip. Doesn't run the long
# cargo-test suite; that's the job below. Splitting these two
# lets fmt/clippy failures surface in under a minute even when
# tests are in flight.
checks:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- name: Cache cargo
uses: Swatinem/rust-cache@v2
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
pkg-config \
libfontconfig1-dev
- name: cargo fmt
run: cargo fmt --all -- --check
- name: cargo clippy
run: cargo clippy --all-targets --all-features -- -D warnings
- name: notebook executability
# Splice each evcxr notebook's cells into one program and
# compile + run it against this checkout, so a renamed module
# or changed signature fails the build instead of a user's
# kernel. Builds into the shared target dir, reusing deps from
# the clippy step. See notebooks/check.py for the model/caveats.
run: python3 notebooks/check.py
- name: round-trip test (asset emit + tools/reproduce.sh + diff)
run: |
set -euo pipefail
# Small but non-trivial input: ZZ12 n=7 enumerates 99
# rats and the asset uses the CLI default
# `--target-block-bytes`, so it lands in a single block.
# The round-trip exercise still validates manifest shape,
# content-hash filenames, decode.py, reproduce.sh
# invocation, and byte-identical re-emit. Total ~20s with
# a warm cache.
export SOURCE_DATE_EPOCH=1780000000
cargo build --release --bin rat_enum --features cli
rm -rf /tmp/asset-original /tmp/repro
./target/release/rat_enum \
--ring 12 -n 7 --free \
--mode dafsa-blocks --threads 0 \
--oeis-a-number A316192 \
-o /tmp/asset-original
# tools/decode.py round-trips through n_sequences.
decoded=$(python3 /tmp/asset-original/tools/decode.py /tmp/asset-original | wc -l)
declared=$(python3 -c "import json; print(json.load(open('/tmp/asset-original/block_index.json'))['n_sequences'])")
test "$decoded" = "$declared" || { echo "decode.py produced $decoded lines, manifest says $declared"; exit 1; }
# README's verification recipe: tools/verify_sha256.py
# exits non-zero if any recorded sha256 disagrees with the
# on-disk bytes.
# --strict: also fail on non-pristine provenance. CI builds
# rat_enum from a clean checkout, so the emitted asset must
# carry a bare commit -- if it ever shows -dirty/unknown here,
# the build environment regressed.
python3 /tmp/asset-original/tools/verify_sha256.py /tmp/asset-original --strict
# tools/count.py --verify re-derives the variableMeasured
# sub-family sequences (free, oneSided, achiral,
# rotationSymmetric, symmetric, subring, coset) from the
# DAFSA and checks them against what the writer emitted into
# ro-crate-metadata.json -- an independent (Python) check of
# the (Rust) emitter on a real asset.
python3 /tmp/asset-original/tools/count.py /tmp/asset-original --verify | tee /tmp/py-count.out
# tools/verify_canonical.py independently recomputes each
# stored rat's dihedral-canonical CCW form and asserts the
# stored sequence equals it -- catches a non-canonical entry
# (and, with the DAFSA's one-string-per-set guarantee, any
# non-canonical duplicate) that a hash/count check cannot see.
python3 /tmp/asset-original/tools/verify_canonical.py /tmp/asset-original
# tools/verify.rs is the verifier the pipeline runs by
# default (compiled + multi-threaded; the Python tools above
# are the zero-dependency fallback). It performs the SAME
# checks -- per-block sha256, canonical-CCW, and the seven
# per-perimeter family re-derivation -- so CI runs it too and
# asserts the two agree on every family series. A disagreement
# means one of the two reimplementations (or the emitter)
# drifted.
cargo install rust-script --locked
rust-script /tmp/asset-original/tools/verify.rs /tmp/asset-original | tee /tmp/rust-verify.out
fams='^(free|oneSided|achiral|rotationSymmetric|symmetric|subring|coset):'
grep -E "$fams" /tmp/rust-verify.out | sort > /tmp/rust-series.txt
grep -E "$fams" /tmp/py-count.out | sort > /tmp/py-series.txt
diff -u /tmp/py-series.txt /tmp/rust-series.txt \
|| { echo "Rust verify.rs and Python count.py disagree on the family series"; exit 1; }
grep -q '^# OK' /tmp/rust-verify.out \
|| { echo "verify.rs did not report overall OK"; exit 1; }
# Run tools/reproduce.sh against the local checkout and
# the same SOURCE_DATE_EPOCH. The pre-built target dir
# inside the clone is fresh, so cargo build inside the
# clone DOES run; that's the cost of testing the real
# path users will take.
mkdir /tmp/repro
( cd /tmp/repro && \
REPO="$GITHUB_WORKSPACE" \
SRC_DIR=/tmp/repro/tilezz-local \
bash /tmp/asset-original/tools/reproduce.sh )
# Reproduced asset must match the original byte-for-byte
# under fixed SOURCE_DATE_EPOCH (including
# ro-crate-metadata.json -- timestamps are deterministic).
# Exclude __pycache__: the verify_*/count tools above
# import the sibling decode module, so Python writes
# tools/__pycache__/ into the original; it's a transient
# byproduct of running the tools, not part of the asset, and
# the freshly reproduced copy (tools never run) has none.
diff -r --exclude=__pycache__ \
/tmp/repro/tilezz-local/tilezz-rat-zz12-n7-free \
/tmp/asset-original
# `cargo test --all-targets --all-features` in both profiles, in
# parallel. The slowest combinatorial pins (oeis_a316192_*, deep
# cross-validate, dihedral quotient) are `#[cfg_attr(debug_assertions,
# ignore)]` so they only run in the release matrix entry -- in debug
# mode they took 5-17 minutes each, vs. tens of seconds in release.
# The debug job still covers every cheap test, including the i128-
# overflow-sensitive arithmetic where `debug_assertions` panics on
# the bugs `release` would silently wrap.
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
profile: [debug, release]
name: test (${{ matrix.profile }})
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo
uses: Swatinem/rust-cache@v2
with:
# Separate cache per profile -- target/debug and target/release
# are independent artifact trees.
key: ${{ matrix.profile }}
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
pkg-config \
libfontconfig1-dev
- name: cargo test
run: |
if [ "${{ matrix.profile }}" = "release" ]; then
cargo test --release --all-targets --all-features
else
cargo test --all-targets --all-features
fi
# Tag-gated release pipeline. Fires only on `v*` pushes and only
# after `checks` + the full `test` matrix go green. Verifies the
# tag matches `Cargo.toml`'s version (so a stray `git tag v0.0.5`
# doesn't publish 0.0.4 by accident), then publishes the crate.
# Token: CARGO_REGISTRY_TOKEN repo secret (Settings -> Secrets
# and variables -> Actions). Default features are empty by design,
# so what lands on crates.io is the lean library; CLI binaries
# stay behind the explicit `cli` feature.
publish-crate:
if: startsWith(github.ref, 'refs/tags/v')
needs: [checks, test]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo
uses: Swatinem/rust-cache@v2
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
pkg-config \
libfontconfig1-dev
- name: Verify tag matches Cargo.toml version
run: |
TAG_VERSION="${GITHUB_REF#refs/tags/v}"
PKG_VERSION="$(grep '^version *= *"' Cargo.toml | head -1 | cut -d'"' -f2)"
if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
echo "::error::Tag is v$TAG_VERSION but Cargo.toml says version = \"$PKG_VERSION\". Bump one or the other."
exit 1
fi
echo "OK: publishing tilezz $PKG_VERSION from tag v$TAG_VERSION"
- name: cargo publish
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: cargo publish --locked