Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
59f9a25
♻️ Move the report types into sequent-core::election_config
edulix Aug 6, 2026
9c2a236
♻️ Move the import bundle schema into sequent-core::election_config
edulix Aug 6, 2026
c92499c
✨ Add shared election config validation to sequent-core
edulix Aug 6, 2026
e7b6b68
✨ Validate an import bundle with the shared rules before importing it
edulix Aug 6, 2026
408c709
✨ Share the writers for the bundle's own file formats
edulix Aug 6, 2026
6e5df25
✨ Share how a spreadsheet cell becomes JSON
edulix Aug 6, 2026
26a7dea
✨ Read an authoring workbook in the shared core
edulix Aug 6, 2026
7fdfba9
✨ Share the deterministic ids a generated bundle uses
edulix Aug 6, 2026
e832712
✨ Share the base entity templates and how they render
edulix Aug 6, 2026
c23d234
✨ Build the bundle's JSON document in the shared core
edulix Aug 6, 2026
12af03f
✨ Build the bundle's CSV members in the shared core
edulix Aug 6, 2026
b1126d7
✨ Configure the login page from the event, in the shared core
edulix Aug 6, 2026
bade833
✨ Turn a bundle into the files it ships as
edulix Aug 6, 2026
d12052f
🔥 Delete windmill's copy of the CSV shapes
edulix Aug 7, 2026
b98b204
✨ Share the fixtures the two callers must agree on
edulix Aug 7, 2026
d670be4
✨ Compile an Election Architect plan into the same rows a workbook pr…
edulix Aug 7, 2026
49325e7
✨ Let the wizard district
edulix Aug 7, 2026
6f822d0
🐞 Gate the builder's re-export like the module it names
edulix Aug 7, 2026
ee3d5f6
📄 License the fixture bundles
edulix Aug 7, 2026
c408948
✅ Check sequent-core at every feature gate
edulix Aug 7, 2026
a85255f
🐞 Emit an instant, so the voting period actually opens
edulix Aug 7, 2026
7eecb3d
Merge branch 'main' into feat/meta-12769-shared-core/main
edulix Aug 7, 2026
26a1064
Merge branch 'feat/meta-12769-shared-core/main' into feat/meta-12769-…
edulix Aug 7, 2026
532acda
Merge remote-tracking branch 'origin/main' into stack/shared-core
edulix Aug 21, 2026
bbda53f
Merge branch 'stack/shared-core' into stack/build-bundle
edulix Aug 21, 2026
30799d6
Address the review: one list of algorithms, and a floor under the counts
edulix Aug 21, 2026
19711bc
Merge branch 'stack/shared-core' into stack/build-bundle
edulix Aug 21, 2026
89eac0b
Cover the two boundaries the review found untested
edulix Aug 21, 2026
24e399d
Merge branch 'stack/shared-core' into stack/build-bundle
edulix Aug 21, 2026
eaf7a30
Cut the commentary the last two commits carried
edulix Aug 21, 2026
4e16f3c
Merge branch 'stack/shared-core' into stack/build-bundle
edulix Aug 21, 2026
f248f42
An event with no areas is a warning, not a refusal
edulix Aug 22, 2026
f72da8e
Merge remote-tracking branch 'origin/feat/meta-12769-shared-core/main…
edulix Aug 22, 2026
1319677
Pin that empty areas is not a missing field either
edulix Aug 22, 2026
82062f9
Merge remote-tracking branch 'origin/feat/meta-12769-shared-core/main…
edulix Aug 22, 2026
ab5a5c8
Three defects the review found: a panic, an overflow, a lost schedule…
edulix Aug 22, 2026
96964d9
Three more from the review: blank cells, numeric ids, longer area loops
edulix Aug 22, 2026
3ba7736
Four more from the review, and a test that proves the numeric-id fix
edulix Aug 22, 2026
8452d2a
The processors and the task name come from the platform, not from a copy
edulix Aug 22, 2026
d398529
A typed requirement kind, and least privilege on the gate job
edulix Aug 22, 2026
2d5e16d
The plan validator's last duplicate: an identifier reused
edulix Aug 22, 2026
75b7f11
Say which ballot values the workbook did not give
edulix Aug 22, 2026
01cbc08
Name the value the bundle carries, not a copy of it
edulix Aug 22, 2026
d9c06a2
Pin that the wizard is not one of those workbooks
edulix Aug 22, 2026
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
64 changes: 64 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,70 @@ on:
branches: [ 'main', 'release/[0-9]+.[0-9]+' ]

jobs:
# sequent-core is compiled against a different feature set by nearly every
# consumer, and a module gated one way with its re-export gated another only
# fails for the sets nobody happens to build locally: `cargo build --workspace`
# unifies features across the graph and hides it entirely. That is exactly how
# an ungated `pub use build::{…}` reached CI and reddened fourteen jobs in the
# slow suites below, each of which reports it as its own failure.
#
# `--lib` rather than `--all-targets`: the question is whether the library
# compiles for a consumer that asked for this much and no more. The test
# modules need `default_features` and are covered by the suites below.
#
# This job is `cargo check` only, so it finishes in a couple of minutes and
# names the gate that broke.
feature-gates:
name: sequent-core at ${{ matrix.gate }}
runs-on: ubuntu-24.04
timeout-minutes: 20
# This job reads code and runs `cargo check`. Without a block it inherits the
# repository default, which can include write scopes.
permissions:
contents: read
strategy:
fail-fast: false
matrix:
include:
- gate: no features
cache: none
features: ""
- gate: default_features
cache: default
features: --features default_features
- gate: keycloak,default_features
cache: keycloak
features: --features keycloak,default_features
- gate: election_config_templates
cache: templates
features: --features election_config_templates
- gate: election_config_archive
cache: archive
features: --features election_config_archive
- gate: election_config_xlsx
cache: xlsx
features: --features election_config_xlsx
steps:
- name: Check out code
uses: actions/checkout@v4
with:
# Or the job token stays in .git/config, readable by every later step.
persist-credentials: false

- name: Set up Rust tests
uses: ./.github/actions/setup-rust-tests
with:
# Per gate, or six jobs would evict each other's artifacts all day.
cargo-build-name: sequent-core-${{ matrix.cache }}
cargo-build-path: packages/target
cargo-lock-path: packages/Cargo.lock

- name: cargo check ${{ matrix.gate }}
env:
PROTOC: "/usr/bin/protoc"
run: cargo check -p sequent-core --lib ${{ matrix.features }}
working-directory: packages
Comment thread
coderabbitai[bot] marked this conversation as resolved.

run-tests:
name: Run Rust tests
runs-on: ubuntu-24.04
Expand Down
42 changes: 40 additions & 2 deletions packages/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 25 additions & 1 deletion packages/sequent-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,20 @@ kuchiki = { version = "0.8", optional = true }
rusqlite = { version = "0.32", features = ["bundled"], optional = true }
csv = "1.3.0"

# reading authoring spreadsheets; optional so front ends with no workbook to read
# do not carry it into their WASM bundle
calamine = { version = "0.26", features = ["dates"], optional = true }

# uuid5 for the deterministic ids a generated bundle uses. The `uuid` crate is not
# used for this: its v4 feature pulls getrandom, whose WASM support is version
# specific and already pinned elsewhere in this workspace, and nothing here needs
# randomness.
sha1 = { version = "0.10", optional = true }

# writing the importable archive; optional so a front end that only validates a
# bundle carries no zip writer
zip = { version = "2.1", optional = true }


# WASM plugin management
wasmtime = {version = "41.0.2", optional = true}
Expand All @@ -150,7 +164,17 @@ lambda_inplace = []
lambda_openwhisk = []
lambda_aws_lambda = []
plugins_wit = ["dep:wasmtime", "dep:wasmtime-wasi"]
default_features = ["dep:strand", "dep:num-bigint", "dep:tempfile", "dep:ammonia"]
default_features = ["dep:strand", "dep:num-bigint", "dep:tempfile", "dep:ammonia", "dep:sha1"]
# Reading .xlsx into an election_config::sheet::Workbook. Separate from
# default_features because admin-portal and voting-portal have no workbook to
# read and should not pay for a spreadsheet parser in their bundle.
election_config_xlsx = ["default_features", "dep:calamine", "dep:zip"]
# Rendering the base entity templates. Separate from `reports`, which also brings
# a headless browser and three AWS SDKs; this needs only the template engine.
election_config_templates = ["default_features", "dep:handlebars"]
# Writing the importable zip. Separate again: a front end that only validates an
# existing bundle has nothing to write.
election_config_archive = ["election_config_templates", "dep:zip"]

sqlite = ["dep:tokio", "dep:rusqlite"]
time = ["dep:time"]
Expand Down
32 changes: 32 additions & 0 deletions packages/sequent-core/examples/validate_bundle.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-FileCopyrightText: 2026 Sequent Tech Inc <legal@sequentech.io>
//
// SPDX-License-Identifier: AGPL-3.0-only

//! Validate an election event bundle from a file.
//!
//! `cargo run -p sequent-core --features default_features --example validate_bundle -- <path>`
//!
//! A thin harness for checking a real export against the shared rules; the same
//! call step-cli and the browser make.

use sequent_core::election_config::{validate, ImportElectionEventSchema};

fn main() -> Result<(), Box<dyn std::error::Error>> {
let path = std::env::args()
.nth(1)
.ok_or("usage: validate_bundle <path>")?;
let text = std::fs::read_to_string(&path)?;
let bundle: ImportElectionEventSchema = serde_json::from_str(&text)?;

let report = validate(&bundle);
print!("{report}");
println!(
"{} error(s), {} warning(s)",
report.errors().count(),
report.warnings().count()
);
if report.has_errors() {
std::process::exit(1);
}
Ok(())
}
Loading
Loading