macrame-db 0.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- macrame_db-0.7.0/.cargo/config.toml +119 -0
- macrame_db-0.7.0/.github/workflows/ci.yml +137 -0
- macrame_db-0.7.0/.github/workflows/python.yml +187 -0
- macrame_db-0.7.0/.github/workflows/release.yml +87 -0
- macrame_db-0.7.0/.github/workflows/wheels.yml +198 -0
- macrame_db-0.7.0/.gitignore +36 -0
- macrame_db-0.7.0/Cargo.lock +2626 -0
- macrame_db-0.7.0/Cargo.toml +171 -0
- macrame_db-0.7.0/LICENSE +6 -0
- macrame_db-0.7.0/LICENSES/Apache-2.0.txt +105 -0
- macrame_db-0.7.0/LICENSES/MIT.txt +21 -0
- macrame_db-0.7.0/PKG-INFO +497 -0
- macrame_db-0.7.0/README.md +478 -0
- macrame_db-0.7.0/benches/budgets.rs +1426 -0
- macrame_db-0.7.0/bindings/python/Cargo.toml +84 -0
- macrame_db-0.7.0/bindings/python/src/database.rs +1113 -0
- macrame_db-0.7.0/bindings/python/src/errors.rs +592 -0
- macrame_db-0.7.0/bindings/python/src/graph.rs +530 -0
- macrame_db-0.7.0/bindings/python/src/lib.rs +132 -0
- macrame_db-0.7.0/bindings/python/src/observe.rs +211 -0
- macrame_db-0.7.0/bindings/python/src/rows.rs +132 -0
- macrame_db-0.7.0/bindings/python/src/runtime.rs +143 -0
- macrame_db-0.7.0/bindings/python/src/temporal.rs +245 -0
- macrame_db-0.7.0/bindings/python/src/testing.rs +178 -0
- macrame_db-0.7.0/bindings/python/src/timestamps.rs +211 -0
- macrame_db-0.7.0/bindings/python/src/types.rs +443 -0
- macrame_db-0.7.0/bindings/python/src/vector.rs +237 -0
- macrame_db-0.7.0/docs/Macrame Hardening Plan v0.6.0.md +1222 -0
- macrame_db-0.7.0/docs/Macrame Implementation Plan v0.5.6.md +758 -0
- macrame_db-0.7.0/docs/Macrame Python Bindings Plan v0.7.0.md +1263 -0
- macrame_db-0.7.0/docs/architecture/README.md +70 -0
- macrame_db-0.7.0/docs/architecture/REJOIN.md +28 -0
- macrame_db-0.7.0/docs/architecture/appendices.md +277 -0
- macrame_db-0.7.0/docs/architecture/s0-s3-foundations.md +146 -0
- macrame_db-0.7.0/docs/architecture/s11-s12-milestones-and-risks.md +40 -0
- macrame_db-0.7.0/docs/architecture/s13-decision-register.md +1098 -0
- macrame_db-0.7.0/docs/architecture/s14-python-bindings.md +557 -0
- macrame_db-0.7.0/docs/architecture/s4-schema.md +424 -0
- macrame_db-0.7.0/docs/architecture/s5-modules.md +735 -0
- macrame_db-0.7.0/docs/architecture/s6-s10-flows-to-dependencies.md +285 -0
- macrame_db-0.7.0/docs/quickref.md +769 -0
- macrame_db-0.7.0/examples/archive_window_diag.rs +184 -0
- macrame_db-0.7.0/examples/budget_density_diag.rs +133 -0
- macrame_db-0.7.0/examples/bulk_atomic_diag.rs +133 -0
- macrame_db-0.7.0/examples/chunk_diag.rs +504 -0
- macrame_db-0.7.0/examples/fixture_matrix_diag.rs +159 -0
- macrame_db-0.7.0/examples/index_coverage_probe.rs +74 -0
- macrame_db-0.7.0/examples/pipeline_diag.rs +155 -0
- macrame_db-0.7.0/examples/r15_soak.rs +324 -0
- macrame_db-0.7.0/examples/readonly_open_probe.rs +187 -0
- macrame_db-0.7.0/examples/repair_diag.rs +145 -0
- macrame_db-0.7.0/examples/shadow_probe.rs +206 -0
- macrame_db-0.7.0/examples/shadow_rebuild_diag.rs +150 -0
- macrame_db-0.7.0/examples/traversal_diag.rs +353 -0
- macrame_db-0.7.0/examples/weight_check_probe.rs +275 -0
- macrame_db-0.7.0/pyproject.toml +73 -0
- macrame_db-0.7.0/python/macrame/__init__.py +240 -0
- macrame_db-0.7.0/python/macrame/_macrame.pyi +822 -0
- macrame_db-0.7.0/python/macrame/py.typed +0 -0
- macrame_db-0.7.0/src/connection.rs +2642 -0
- macrame_db-0.7.0/src/error.rs +387 -0
- macrame_db-0.7.0/src/graph/algorithms.rs +454 -0
- macrame_db-0.7.0/src/graph/builder.rs +411 -0
- macrame_db-0.7.0/src/graph/edge.rs +136 -0
- macrame_db-0.7.0/src/graph/mod.rs +13 -0
- macrame_db-0.7.0/src/graph/subgraph.rs +631 -0
- macrame_db-0.7.0/src/graph/vector_filter.rs +516 -0
- macrame_db-0.7.0/src/integrity/audit.rs +53 -0
- macrame_db-0.7.0/src/integrity/mod.rs +38 -0
- macrame_db-0.7.0/src/integrity/rebuild.rs +96 -0
- macrame_db-0.7.0/src/integrity/shadow.rs +369 -0
- macrame_db-0.7.0/src/lib.rs +42 -0
- macrame_db-0.7.0/src/metrics.rs +604 -0
- macrame_db-0.7.0/src/schema/ddl.rs +657 -0
- macrame_db-0.7.0/src/schema/migrations.rs +655 -0
- macrame_db-0.7.0/src/schema/mod.rs +5 -0
- macrame_db-0.7.0/src/schema/seed.rs +6 -0
- macrame_db-0.7.0/src/temporal/archive.rs +296 -0
- macrame_db-0.7.0/src/temporal/as_of.rs +241 -0
- macrame_db-0.7.0/src/temporal/interval.rs +34 -0
- macrame_db-0.7.0/src/temporal/mod.rs +13 -0
- macrame_db-0.7.0/src/temporal/replay.rs +720 -0
- macrame_db-0.7.0/src/temporal/snapshot.rs +478 -0
- macrame_db-0.7.0/src/util/clock.rs +174 -0
- macrame_db-0.7.0/src/util/ids.rs +170 -0
- macrame_db-0.7.0/src/util/limits.rs +49 -0
- macrame_db-0.7.0/src/util/mod.rs +9 -0
- macrame_db-0.7.0/src/util/timestamp.rs +308 -0
- macrame_db-0.7.0/src/vector/embedding.rs +38 -0
- macrame_db-0.7.0/src/vector/hybrid.rs +254 -0
- macrame_db-0.7.0/src/vector/mod.rs +11 -0
- macrame_db-0.7.0/src/vector/model.rs +129 -0
- macrame_db-0.7.0/src/vector/registry.rs +190 -0
- macrame_db-0.7.0/src/vector/search.rs +220 -0
- macrame_db-0.7.0/tests/actor_metrics_tests.rs +252 -0
- macrame_db-0.7.0/tests/archive_window_tests.rs +264 -0
- macrame_db-0.7.0/tests/bench_control_tests.rs +108 -0
- macrame_db-0.7.0/tests/common/fixtures.rs +502 -0
- macrame_db-0.7.0/tests/common/harness.rs +68 -0
- macrame_db-0.7.0/tests/compat_contract_tests.rs +290 -0
- macrame_db-0.7.0/tests/concurrency_tests.rs +508 -0
- macrame_db-0.7.0/tests/diagnostic_conn_tests.rs +244 -0
- macrame_db-0.7.0/tests/doc_link_tests.rs +276 -0
- macrame_db-0.7.0/tests/doc_sync_tests.rs +181 -0
- macrame_db-0.7.0/tests/doctrine_property_tests.proptest-regressions +8 -0
- macrame_db-0.7.0/tests/doctrine_property_tests.rs +879 -0
- macrame_db-0.7.0/tests/doctrine_static_tests.rs +81 -0
- macrame_db-0.7.0/tests/fixture_matrix_tests.rs +357 -0
- macrame_db-0.7.0/tests/graph_property_tests.proptest-regressions +12 -0
- macrame_db-0.7.0/tests/graph_property_tests.rs +360 -0
- macrame_db-0.7.0/tests/graph_tests.rs +763 -0
- macrame_db-0.7.0/tests/hybrid_tests.rs +474 -0
- macrame_db-0.7.0/tests/index_plan_tests.rs +257 -0
- macrame_db-0.7.0/tests/integrity_property_tests.proptest-regressions +7 -0
- macrame_db-0.7.0/tests/integrity_property_tests.rs +684 -0
- macrame_db-0.7.0/tests/integrity_tests.rs +675 -0
- macrame_db-0.7.0/tests/migration_tests.rs +844 -0
- macrame_db-0.7.0/tests/packaging_tests.rs +141 -0
- macrame_db-0.7.0/tests/replay_snapshot_tests.rs +1293 -0
- macrame_db-0.7.0/tests/shadow_rebuild_tests.rs +368 -0
- macrame_db-0.7.0/tests/snapshot_chain_tests.rs +267 -0
- macrame_db-0.7.0/tests/storage_boundary_tests.rs +417 -0
- macrame_db-0.7.0/tests/temporal_tests.rs +698 -0
- macrame_db-0.7.0/tests/vector_filter_tests.rs +397 -0
- macrame_db-0.7.0/tests/vector_tests.rs +700 -0
- macrame_db-0.7.0/tests/wave1_regression_tests.rs +1689 -0
- macrame_db-0.7.0/tests/write_path_tests.rs +468 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Test-harness settings for R15 (see the risk register in docs/).
|
|
2
|
+
#
|
|
3
|
+
# libSQL faults with STATUS_ACCESS_VIOLATION (0xc0000005) when local databases
|
|
4
|
+
# are opened *concurrently* in one process. It is not a Macrame defect: it
|
|
5
|
+
# reproduces with no Macrame types in the loop, it is not confined to any one
|
|
6
|
+
# test binary -- storage_boundary, vector_filter, replay_snapshot, write_path
|
|
7
|
+
# and the property binaries have all died mid-run -- and upgrading the
|
|
8
|
+
# dependency 0.6.0 -> 0.9.30 did not change it.
|
|
9
|
+
#
|
|
10
|
+
# ---------------------------------------------------------------------------
|
|
11
|
+
# If you are here because the build went red, read this paragraph first.
|
|
12
|
+
# ---------------------------------------------------------------------------
|
|
13
|
+
#
|
|
14
|
+
# Re-run the failing binary on its own. Every binary that has died during a
|
|
15
|
+
# suite run is clean in isolation -- four of them at 0/15 each, 60 isolated runs
|
|
16
|
+
# with no fault. A binary that fails alone is a real failure; one that only
|
|
17
|
+
# fails inside the suite is almost certainly this.
|
|
18
|
+
#
|
|
19
|
+
# And note the reporting hazard, because it is the opposite of what you expect:
|
|
20
|
+
# the fault kills the process, so the tests that already printed `ok` are
|
|
21
|
+
# reported, the ones behind it are silent, and that binary's `test result:` line
|
|
22
|
+
# never appears. **A pass-count sum therefore comes back SMALLER with no
|
|
23
|
+
# failures rather than red.** Anything gating on this suite must key on the
|
|
24
|
+
# absence of a per-target result line, and `--no-fail-fast` is not optional or
|
|
25
|
+
# everything alphabetically behind the fault is skipped as well.
|
|
26
|
+
#
|
|
27
|
+
# ---------------------------------------------------------------------------
|
|
28
|
+
# What is known, and what was believed here and is not true
|
|
29
|
+
# ---------------------------------------------------------------------------
|
|
30
|
+
#
|
|
31
|
+
# The trigger is CONCURRENT OPEN. A standalone reproducer separates the two
|
|
32
|
+
# candidate variables directly (libsql 0.9.30, Windows, release, tokio
|
|
33
|
+
# multi-thread, one file per task):
|
|
34
|
+
#
|
|
35
|
+
# 500 opens, sequential, one process 0/10
|
|
36
|
+
# 4 / 8 / 16 concurrent opens 0/10 each
|
|
37
|
+
# 32 concurrent opens 2/12
|
|
38
|
+
# 128 concurrent opens 5/12
|
|
39
|
+
# 128 concurrent, nothing dropped until end 3/12
|
|
40
|
+
#
|
|
41
|
+
# The threshold sits between 16 and 32 concurrent opens on this machine. The
|
|
42
|
+
# last row is the sharp one: holding every handle alive until the end still
|
|
43
|
+
# faults, so this is concurrent *open*, not an open/drop race and not teardown.
|
|
44
|
+
#
|
|
45
|
+
# REFUTED, and it was written here as the diagnosis until 0.6.0: that the
|
|
46
|
+
# correlate is database open/drop CHURN. The suite data reads that way -- the
|
|
47
|
+
# property binaries open one database per generated case and fault ~3/4 alone,
|
|
48
|
+
# where an ordinary 20-test binary is 0/15 -- but 500 sequential opens in one
|
|
49
|
+
# process is 0/10. Churn is not sufficient; concurrency is. The suite table
|
|
50
|
+
# cannot distinguish the two, and no inference of that kind should be drawn
|
|
51
|
+
# from it.
|
|
52
|
+
#
|
|
53
|
+
# ---------------------------------------------------------------------------
|
|
54
|
+
# What this setting does, and what it does not
|
|
55
|
+
# ---------------------------------------------------------------------------
|
|
56
|
+
#
|
|
57
|
+
# Serialising libtest REDUCES THE RATE. It does not remove the fault, and an
|
|
58
|
+
# earlier version of this file said that it did:
|
|
59
|
+
#
|
|
60
|
+
# 0.5.4, full suite, libtest default (parallel) 1/20 bad runs
|
|
61
|
+
# 0.5.4, full suite, --test-threads=1 0/30 bad runs
|
|
62
|
+
# 0.5.6, full suite, --test-threads=1 5/10 bad runs
|
|
63
|
+
#
|
|
64
|
+
# Same dependency version throughout, and the last row was measured WITH this
|
|
65
|
+
# setting in force. Either the rate rose with the suite (171 -> 221 tests) or
|
|
66
|
+
# the original 0/30 was a lucky streak. Read 0/30 as a data point, not as a
|
|
67
|
+
# guarantee this file provides.
|
|
68
|
+
#
|
|
69
|
+
# One thing this does not explain, flagged rather than guessed at: every binary
|
|
70
|
+
# is 0/15 alone while the suite that runs those same binaries serially is 5/10,
|
|
71
|
+
# all under RUST_TEST_THREADS = "1". If concurrent opens are required, the
|
|
72
|
+
# suite-level runs are finding concurrency that per-binary runs do not, and this
|
|
73
|
+
# data does not say where. `Database::open` taking three connections (four with
|
|
74
|
+
# the cadence) is the obvious suspect and is not evidence.
|
|
75
|
+
#
|
|
76
|
+
# `[env]` does not override a value already in the environment, so setting
|
|
77
|
+
# RUST_TEST_THREADS yourself still works -- which is how you reproduce the fault
|
|
78
|
+
# if you are chasing it, or check whether an upstream release has fixed it.
|
|
79
|
+
#
|
|
80
|
+
# The three generated-history targets (doctrine, integrity, graph) are behind
|
|
81
|
+
# the `property-tests` feature and run as their own step, because a property
|
|
82
|
+
# case needs a database of its own and serialising does not save them:
|
|
83
|
+
# doctrine_property_tests faulted 3/4 and integrity_property_tests 1/4 in the
|
|
84
|
+
# same session, against ~3/25 recorded at 0.5.4. Quarantined, not silenced --
|
|
85
|
+
# proptest replays every previously found failure from .proptest-regressions
|
|
86
|
+
# before a single new case is generated.
|
|
87
|
+
#
|
|
88
|
+
# Re-measured at 0.7.0 on a larger sample: doctrine_property_tests alone,
|
|
89
|
+
# **9 crashes in 15 runs**, on a machine that had been building wheels all
|
|
90
|
+
# session. That sits between the two figures above rather than contradicting
|
|
91
|
+
# either, and it is the clearest statement of why these are quarantined: at 60%
|
|
92
|
+
# no retry budget makes them a gate. The Rust source was unchanged across the
|
|
93
|
+
# whole of that measurement -- P4-P6 touched only the binding crate, tests_py
|
|
94
|
+
# and docs -- so the variable is load, not code.
|
|
95
|
+
#
|
|
96
|
+
# It also produced a textbook demonstration of the reporting hazard. With
|
|
97
|
+
# --no-fail-fast the run came back **308 passed, 0 failed** and exit 101: eight
|
|
98
|
+
# tests silently absent, and nothing in the pass/fail line saying so. See
|
|
99
|
+
# `tests_py/run_suite.py` for the Python-side gate built around exactly this,
|
|
100
|
+
# and D-107 for why the two sides need different checks.
|
|
101
|
+
#
|
|
102
|
+
# ---------------------------------------------------------------------------
|
|
103
|
+
# Production exposure
|
|
104
|
+
# ---------------------------------------------------------------------------
|
|
105
|
+
#
|
|
106
|
+
# Soaked and defended in 0.6.0 (T5.2, D-092), in the sharpened form -- ONE
|
|
107
|
+
# PROCESS, ONE FILE, A BOUNDED SET OF CONNECTIONS OPENED ONCE AND NEVER CHURNED.
|
|
108
|
+
# That wording matters: the old claim was about one `Database` handle, and a
|
|
109
|
+
# handle is not what the fault counts.
|
|
110
|
+
#
|
|
111
|
+
# cargo run --release --example r15_soak -- --arm claim --secs 60 --runs 6
|
|
112
|
+
# cargo run --release --example r15_soak -- --arm control --secs 15 --runs 10
|
|
113
|
+
#
|
|
114
|
+
# claim is 0/6 at 60s and 0/10 at 15s; control, which adds 48 concurrent opens
|
|
115
|
+
# to the same load, is 2/10. The control is what makes the claim arm mean
|
|
116
|
+
# anything -- a clean claim run on its own is indistinguishable from a harness
|
|
117
|
+
# that provoked nothing. Run both or neither.
|
|
118
|
+
[env]
|
|
119
|
+
RUST_TEST_THREADS = "1"
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_call: # so release.yml can gate on exactly this
|
|
8
|
+
|
|
9
|
+
env:
|
|
10
|
+
CARGO_TERM_COLOR: always
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
lint:
|
|
14
|
+
name: clippy
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
19
|
+
with:
|
|
20
|
+
components: rustfmt, clippy
|
|
21
|
+
- uses: Swatinem/rust-cache@v2
|
|
22
|
+
|
|
23
|
+
# `--all-targets` covers benches and examples, which is where several of
|
|
24
|
+
# this project's diagnostics live and where clippy has caught real defects
|
|
25
|
+
# (D-075's oversized DbError came out of a clippy warning). Verified clean
|
|
26
|
+
# under `-D warnings` at 0.6.0, so this is a gate that currently passes.
|
|
27
|
+
#
|
|
28
|
+
# `-D warnings` is scoped to this step rather than set for the workflow:
|
|
29
|
+
# as a global RUSTFLAGS it also applies to the `cargo publish --dry-run`
|
|
30
|
+
# verification build, where a warning from a dependency's build would fail
|
|
31
|
+
# a packaging check that has nothing to do with warnings.
|
|
32
|
+
- name: clippy
|
|
33
|
+
run: cargo clippy --all-targets --features metrics
|
|
34
|
+
env:
|
|
35
|
+
RUSTFLAGS: -D warnings
|
|
36
|
+
|
|
37
|
+
# **Report-only, deliberately.** This repo has never been rustfmt-clean —
|
|
38
|
+
# 246 diffs at 0.6.0, essentially all in tests and benches — so a blocking
|
|
39
|
+
# gate would go red on its first run for a pre-existing condition, and the
|
|
40
|
+
# only way to green it is a repo-wide reformat nobody reviewed. Adopting
|
|
41
|
+
# rustfmt is a decision worth taking on purpose: run `cargo fmt --all` in
|
|
42
|
+
# its own commit, then delete `continue-on-error` here.
|
|
43
|
+
- name: rustfmt (advisory)
|
|
44
|
+
run: cargo fmt --all -- --check
|
|
45
|
+
continue-on-error: true
|
|
46
|
+
|
|
47
|
+
# The README carries an MSRV badge and `Cargo.toml` a `rust-version`, and
|
|
48
|
+
# both are claims. Nothing else in the build would notice them going stale:
|
|
49
|
+
# the crate is developed on stable, so a newly used std method raises the real
|
|
50
|
+
# floor silently and the declared one stays where it was.
|
|
51
|
+
#
|
|
52
|
+
# `--all-targets` deliberately: tests and benches use `OnceLock` and other
|
|
53
|
+
# items the library does not, and a contributor hitting a build failure the
|
|
54
|
+
# badge said would not happen is the same defect as a wrong badge.
|
|
55
|
+
msrv:
|
|
56
|
+
name: MSRV (rust-version in Cargo.toml)
|
|
57
|
+
runs-on: ubuntu-latest
|
|
58
|
+
steps:
|
|
59
|
+
- uses: actions/checkout@v4
|
|
60
|
+
- id: msrv
|
|
61
|
+
run: echo "v=$(grep -m1 '^rust-version' Cargo.toml | sed 's/.*= *//; s/\"//g')" >> "$GITHUB_OUTPUT"
|
|
62
|
+
- uses: dtolnay/rust-toolchain@master
|
|
63
|
+
with:
|
|
64
|
+
toolchain: ${{ steps.msrv.outputs.v }}
|
|
65
|
+
- uses: Swatinem/rust-cache@v2
|
|
66
|
+
- run: cargo check --all-features --all-targets
|
|
67
|
+
|
|
68
|
+
test:
|
|
69
|
+
name: test (${{ matrix.os }})
|
|
70
|
+
runs-on: ${{ matrix.os }}
|
|
71
|
+
strategy:
|
|
72
|
+
fail-fast: false
|
|
73
|
+
matrix:
|
|
74
|
+
os: [ubuntu-latest, windows-latest]
|
|
75
|
+
steps:
|
|
76
|
+
- uses: actions/checkout@v4
|
|
77
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
78
|
+
- uses: Swatinem/rust-cache@v2
|
|
79
|
+
|
|
80
|
+
# `.cargo/config.toml` sets RUST_TEST_THREADS = "1" for R15 and applies
|
|
81
|
+
# here automatically. Read that file before changing anything below.
|
|
82
|
+
#
|
|
83
|
+
# R15 is an intermittent libSQL access violation (0xC0000005) triggered by
|
|
84
|
+
# concurrent database opens. It kills the process, so cargo reports
|
|
85
|
+
# "error: N targets failed" and the run goes red for a reason that has
|
|
86
|
+
# nothing to do with the change under test — measured at ~5/10 full-suite
|
|
87
|
+
# runs on Windows. A gate that fails half the time trains people to ignore
|
|
88
|
+
# red, which is this project's own stated position, so the run is retried
|
|
89
|
+
# rather than tolerated or ignored.
|
|
90
|
+
#
|
|
91
|
+
# Three attempts, not unlimited: R15 has always passed on re-run, so a
|
|
92
|
+
# genuine failure still goes red after three, and the attempt count is
|
|
93
|
+
# visible in the log rather than hidden by a `continue-on-error`.
|
|
94
|
+
- name: test (with R15 retry)
|
|
95
|
+
shell: bash
|
|
96
|
+
run: |
|
|
97
|
+
for attempt in 1 2 3; do
|
|
98
|
+
echo "::group::cargo test, attempt $attempt"
|
|
99
|
+
if cargo test --features metrics --no-fail-fast; then
|
|
100
|
+
echo "::endgroup::"
|
|
101
|
+
echo "passed on attempt $attempt"
|
|
102
|
+
exit 0
|
|
103
|
+
fi
|
|
104
|
+
echo "::endgroup::"
|
|
105
|
+
echo "::warning::attempt $attempt failed; see R15 in .cargo/config.toml"
|
|
106
|
+
done
|
|
107
|
+
echo "::error::three consecutive test runs failed — this is not R15"
|
|
108
|
+
exit 1
|
|
109
|
+
|
|
110
|
+
# The generated-history binaries are quarantined behind a feature (see
|
|
111
|
+
# .cargo/config.toml): a property case opens a database of its own, which
|
|
112
|
+
# is exactly R15's trigger, so they fault far more often than the rest.
|
|
113
|
+
# Run as their own step so their noise cannot be mistaken for the suite's.
|
|
114
|
+
- name: property tests (quarantined, R15-prone)
|
|
115
|
+
shell: bash
|
|
116
|
+
run: |
|
|
117
|
+
for attempt in 1 2 3; do
|
|
118
|
+
if cargo test --features property-tests --no-fail-fast; then
|
|
119
|
+
exit 0
|
|
120
|
+
fi
|
|
121
|
+
echo "::warning::property attempt $attempt failed"
|
|
122
|
+
done
|
|
123
|
+
echo "::error::property tests failed three times"
|
|
124
|
+
exit 1
|
|
125
|
+
|
|
126
|
+
# A `cargo publish` failure after a tag is pushed is expensive: the tag is
|
|
127
|
+
# already public and the version number is spent. This runs the same
|
|
128
|
+
# packaging and verification step on every PR, so the release job's only new
|
|
129
|
+
# variable is the upload itself.
|
|
130
|
+
package:
|
|
131
|
+
name: cargo publish --dry-run
|
|
132
|
+
runs-on: ubuntu-latest
|
|
133
|
+
steps:
|
|
134
|
+
- uses: actions/checkout@v4
|
|
135
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
136
|
+
- uses: Swatinem/rust-cache@v2
|
|
137
|
+
- run: cargo publish --dry-run
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
name: Python
|
|
2
|
+
|
|
3
|
+
# The Python side of the project: the binding crate compiles, the extension
|
|
4
|
+
# builds and installs the way a user installs it, and the 337-test suite passes
|
|
5
|
+
# on every platform this repository claims to support.
|
|
6
|
+
#
|
|
7
|
+
# ## Why this does not call `ci.yml` first
|
|
8
|
+
#
|
|
9
|
+
# The plan (§10) sketched this file as `rust: uses ./.github/workflows/ci.yml`
|
|
10
|
+
# with everything else behind `needs: rust`. That shape belonged to a file that
|
|
11
|
+
# also built the wheels — "the crate must be green before the wheel is built".
|
|
12
|
+
# P5 moved the wheel matrix into `wheels.yml`, tag-triggered, and what is left
|
|
13
|
+
# here is a test job whose failure mode is independent of the Rust suite's.
|
|
14
|
+
#
|
|
15
|
+
# `workflow_call` does not deduplicate: gating here would re-run clippy, MSRV,
|
|
16
|
+
# the two-OS Rust matrix (each with its own three R15 retries) and the publish
|
|
17
|
+
# dry-run on every pull request that already ran them, and would hold the Python
|
|
18
|
+
# answer behind ~15 minutes of work it does not depend on. Two independent
|
|
19
|
+
# signals, reported separately, is strictly more information than one signal
|
|
20
|
+
# reported late.
|
|
21
|
+
#
|
|
22
|
+
# Where the two *must* be green together is before an upload, and that is what
|
|
23
|
+
# `workflow_call` above is for: `wheels.yml`'s publish job gates on this file,
|
|
24
|
+
# exactly as `release.yml` gates on `ci.yml`. See D-108.
|
|
25
|
+
#
|
|
26
|
+
# ## The gap this closes
|
|
27
|
+
#
|
|
28
|
+
# `bindings/python` is a workspace *member* but never a *default* member — the
|
|
29
|
+
# root package is itself a member, so Cargo scopes bare commands to it alone,
|
|
30
|
+
# which is the property `tests/packaging_tests.rs` pins and the reason `cargo
|
|
31
|
+
# publish` stayed a one-package operation (D-098). The cost of that choice, not
|
|
32
|
+
# noticed until now: **nothing in CI ever compiled the binding crate.** Until
|
|
33
|
+
# this file existed, a pull request could break `bindings/python/src/*.rs` and
|
|
34
|
+
# every check would go green, because the only thing that built it was
|
|
35
|
+
# `wheels.yml`, which runs on tags.
|
|
36
|
+
|
|
37
|
+
on:
|
|
38
|
+
push:
|
|
39
|
+
branches: [main]
|
|
40
|
+
pull_request:
|
|
41
|
+
workflow_call: # so wheels.yml can gate an upload on exactly this
|
|
42
|
+
|
|
43
|
+
env:
|
|
44
|
+
CARGO_TERM_COLOR: always
|
|
45
|
+
|
|
46
|
+
jobs:
|
|
47
|
+
# `-p macrame-py` explicitly. Every other clippy invocation in this repository
|
|
48
|
+
# is scoped to the root package by Cargo's own default, so this one has to
|
|
49
|
+
# name the crate or it lints the same code `ci.yml` already linted.
|
|
50
|
+
#
|
|
51
|
+
# Deliberately **without** `--features extension-module`: that feature is
|
|
52
|
+
# turned on by `pyproject.toml` for maturin's build only, and the binding
|
|
53
|
+
# manifest states the crate must build without it. A check that silently
|
|
54
|
+
# required it would make that statement untestable.
|
|
55
|
+
lint:
|
|
56
|
+
name: clippy (binding crate)
|
|
57
|
+
runs-on: ubuntu-latest
|
|
58
|
+
steps:
|
|
59
|
+
- uses: actions/checkout@v4
|
|
60
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
61
|
+
with:
|
|
62
|
+
components: clippy
|
|
63
|
+
- uses: Swatinem/rust-cache@v2
|
|
64
|
+
- name: clippy
|
|
65
|
+
run: cargo clippy -p macrame-py --all-targets
|
|
66
|
+
env:
|
|
67
|
+
RUSTFLAGS: -D warnings
|
|
68
|
+
|
|
69
|
+
# `tests_py/test_stubs.py` compares the stub's *names* against the
|
|
70
|
+
# extension in both directions. It cannot see a wrong **type** — a stub
|
|
71
|
+
# that says `-> int` where the runtime answers a `datetime` passes every
|
|
72
|
+
# one of those checks. A type checker is what reads the annotations, so
|
|
73
|
+
# one runs here, over the stub and the package that re-exports it.
|
|
74
|
+
#
|
|
75
|
+
# No Rust toolchain needed and no extension built: `--strict` on a `.pyi`
|
|
76
|
+
# is a pure text analysis. Measured clean at 0.7.0, so this is a gate that
|
|
77
|
+
# passes rather than a backlog.
|
|
78
|
+
- uses: actions/setup-python@v5
|
|
79
|
+
with:
|
|
80
|
+
python-version: "3.13"
|
|
81
|
+
- name: mypy (the stub, and the package that re-exports it)
|
|
82
|
+
run: |
|
|
83
|
+
python -m pip install --upgrade pip mypy
|
|
84
|
+
python -m mypy --strict python/macrame
|
|
85
|
+
|
|
86
|
+
test:
|
|
87
|
+
name: suite (${{ matrix.os }}, py${{ matrix.python }})
|
|
88
|
+
runs-on: ${{ matrix.os }}
|
|
89
|
+
strategy:
|
|
90
|
+
# One platform failing must not hide the others: R15 is platform-sensitive
|
|
91
|
+
# and the whole point of a three-OS matrix is knowing which ones it hit.
|
|
92
|
+
fail-fast: false
|
|
93
|
+
matrix:
|
|
94
|
+
include:
|
|
95
|
+
- os: ubuntu-latest
|
|
96
|
+
python: "3.13"
|
|
97
|
+
# The floor `pyproject.toml` declares. `requires-python = ">=3.10"` is
|
|
98
|
+
# a claim pip enforces against users and nothing enforces against us.
|
|
99
|
+
- os: ubuntu-latest
|
|
100
|
+
python: "3.10"
|
|
101
|
+
- os: windows-latest
|
|
102
|
+
python: "3.13"
|
|
103
|
+
# macOS has never run this suite. `wheels.yml` builds a universal2
|
|
104
|
+
# wheel and smoke-tests it with six lines; this is the first time the
|
|
105
|
+
# ledger's Python surface is exercised on Apple silicon at all.
|
|
106
|
+
- os: macos-latest
|
|
107
|
+
python: "3.13"
|
|
108
|
+
|
|
109
|
+
steps:
|
|
110
|
+
- uses: actions/checkout@v4
|
|
111
|
+
- uses: actions/setup-python@v5
|
|
112
|
+
with:
|
|
113
|
+
python-version: ${{ matrix.python }}
|
|
114
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
115
|
+
- uses: Swatinem/rust-cache@v2
|
|
116
|
+
|
|
117
|
+
# `pip install .` rather than `maturin develop`: it goes through the PEP
|
|
118
|
+
# 517 backend, which means it reads `[tool.maturin]` and therefore builds
|
|
119
|
+
# with `--features extension-module` and in release, i.e. it is the same
|
|
120
|
+
# path a user takes. `maturin develop` would need a virtualenv here and
|
|
121
|
+
# would test a build configuration nobody installs.
|
|
122
|
+
- name: install the extension the way a user installs it
|
|
123
|
+
run: |
|
|
124
|
+
python -m pip install --upgrade pip
|
|
125
|
+
python -m pip install .
|
|
126
|
+
python -m pip install pytest
|
|
127
|
+
|
|
128
|
+
# **Not bare pytest** (D-107). R15 is an intermittent libSQL access
|
|
129
|
+
# violation on concurrent open, reproduced through this binding at 2 in 12
|
|
130
|
+
# runs, and it kills the interpreter rather than raising. `run_suite.py`
|
|
131
|
+
# distinguishes the four ways this suite can end — CRASH, FAILED,
|
|
132
|
+
# INCOMPLETE, TEARDOWN — retries only the first, three times, and reports
|
|
133
|
+
# the rest red on the first attempt. A green summary is not sufficient
|
|
134
|
+
# here and neither is a zero exit; it checks them against each other.
|
|
135
|
+
- name: python suite (through the R15 gate)
|
|
136
|
+
run: python tests_py/run_suite.py
|
|
137
|
+
|
|
138
|
+
# abi3 is what turns a 4-platform × 5-version matrix into 4 builds, and the
|
|
139
|
+
# claim underneath it — "one wheel serves CPython 3.10 through whatever ships
|
|
140
|
+
# next" — is not tested by any job above, because each of those builds and
|
|
141
|
+
# runs on a single interpreter.
|
|
142
|
+
#
|
|
143
|
+
# So: build once on the floor, install that same artifact into an interpreter
|
|
144
|
+
# it has never seen, and run the whole suite through it. If pyo3's abi3
|
|
145
|
+
# feature were ever dropped, every other job here would still pass and the
|
|
146
|
+
# wheel matrix would quietly become wrong by a factor of five.
|
|
147
|
+
abi3:
|
|
148
|
+
name: one wheel, two interpreters
|
|
149
|
+
runs-on: ubuntu-latest
|
|
150
|
+
steps:
|
|
151
|
+
- uses: actions/checkout@v4
|
|
152
|
+
- uses: actions/setup-python@v5
|
|
153
|
+
id: floor
|
|
154
|
+
with:
|
|
155
|
+
python-version: "3.10"
|
|
156
|
+
- uses: actions/setup-python@v5
|
|
157
|
+
id: newest
|
|
158
|
+
with:
|
|
159
|
+
python-version: "3.13"
|
|
160
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
161
|
+
- uses: Swatinem/rust-cache@v2
|
|
162
|
+
|
|
163
|
+
# Debug, deliberately: this job asks what the wheel is *tagged* and
|
|
164
|
+
# whether it imports, not how fast it runs. `wheels.yml` builds the
|
|
165
|
+
# release artifact.
|
|
166
|
+
- name: build one wheel, on 3.10
|
|
167
|
+
shell: bash
|
|
168
|
+
run: |
|
|
169
|
+
"${{ steps.floor.outputs.python-path }}" -m pip install --upgrade pip maturin
|
|
170
|
+
"${{ steps.floor.outputs.python-path }}" -m maturin build --out dist
|
|
171
|
+
|
|
172
|
+
- name: the tag names the ABI, not the interpreter that built it
|
|
173
|
+
shell: bash
|
|
174
|
+
run: |
|
|
175
|
+
ls -la dist
|
|
176
|
+
if ! ls dist/*-abi3-*.whl >/dev/null 2>&1; then
|
|
177
|
+
echo "::error::no abi3 wheel — pyo3's abi3-py310 feature is off, and"
|
|
178
|
+
echo "::error::the wheel matrix in wheels.yml is now wrong per Python version"
|
|
179
|
+
exit 1
|
|
180
|
+
fi
|
|
181
|
+
|
|
182
|
+
- name: run the whole suite on 3.13, against the wheel built by 3.10
|
|
183
|
+
shell: bash
|
|
184
|
+
run: |
|
|
185
|
+
"${{ steps.newest.outputs.python-path }}" -m pip install --upgrade pip pytest
|
|
186
|
+
"${{ steps.newest.outputs.python-path }}" -m pip install --no-index --find-links dist macrame-db
|
|
187
|
+
"${{ steps.newest.outputs.python-path }}" tests_py/run_suite.py
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Two ways in, and only one of them can publish.
|
|
4
|
+
#
|
|
5
|
+
# * push a tag `v0.6.0` -> verify, then upload to crates.io
|
|
6
|
+
# * run it by hand -> verify and dry-run only, unless `publish` is
|
|
7
|
+
# explicitly set to `true`
|
|
8
|
+
#
|
|
9
|
+
# The manual dry run exists so the whole path can be exercised without spending
|
|
10
|
+
# a version number. A crates.io version cannot be re-uploaded once taken, even
|
|
11
|
+
# if it is yanked, so a failed publish costs the number permanently.
|
|
12
|
+
on:
|
|
13
|
+
push:
|
|
14
|
+
tags: ["v*"]
|
|
15
|
+
workflow_dispatch:
|
|
16
|
+
inputs:
|
|
17
|
+
publish:
|
|
18
|
+
description: 'Actually upload to crates.io (otherwise dry-run only)'
|
|
19
|
+
type: boolean
|
|
20
|
+
default: false
|
|
21
|
+
|
|
22
|
+
env:
|
|
23
|
+
CARGO_TERM_COLOR: always
|
|
24
|
+
|
|
25
|
+
permissions:
|
|
26
|
+
contents: read
|
|
27
|
+
|
|
28
|
+
jobs:
|
|
29
|
+
# The full CI matrix, reused rather than reimplemented — a release gate that
|
|
30
|
+
# is a second copy of the test job is a gate that drifts from it.
|
|
31
|
+
verify:
|
|
32
|
+
uses: ./.github/workflows/ci.yml
|
|
33
|
+
|
|
34
|
+
# Cheap, and it runs *before* anything is uploaded: a tag that disagrees with
|
|
35
|
+
# the manifest publishes a version nobody can find from the tag, and the only
|
|
36
|
+
# fix is another version number.
|
|
37
|
+
check-version:
|
|
38
|
+
name: tag matches Cargo.toml
|
|
39
|
+
runs-on: ubuntu-latest
|
|
40
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
41
|
+
steps:
|
|
42
|
+
- uses: actions/checkout@v4
|
|
43
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
44
|
+
- name: compare
|
|
45
|
+
shell: bash
|
|
46
|
+
run: |
|
|
47
|
+
manifest=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[0].version')
|
|
48
|
+
tag="${GITHUB_REF_NAME#v}"
|
|
49
|
+
echo "manifest=$manifest tag=$tag"
|
|
50
|
+
if [ "$manifest" != "$tag" ]; then
|
|
51
|
+
echo "::error::tag $GITHUB_REF_NAME does not match Cargo.toml version $manifest"
|
|
52
|
+
exit 1
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
publish:
|
|
56
|
+
name: publish to crates.io
|
|
57
|
+
needs: [verify, check-version]
|
|
58
|
+
runs-on: ubuntu-latest
|
|
59
|
+
# Tag pushes publish; a manual run publishes only when asked. `always()` is
|
|
60
|
+
# deliberately absent — if `check-version` is skipped on a manual run, this
|
|
61
|
+
# still requires `verify` to have succeeded.
|
|
62
|
+
if: |
|
|
63
|
+
startsWith(github.ref, 'refs/tags/v') ||
|
|
64
|
+
inputs.publish == true
|
|
65
|
+
environment: crates-io # add a required reviewer here to gate uploads
|
|
66
|
+
steps:
|
|
67
|
+
- uses: actions/checkout@v4
|
|
68
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
69
|
+
- uses: Swatinem/rust-cache@v2
|
|
70
|
+
|
|
71
|
+
- name: publish
|
|
72
|
+
env:
|
|
73
|
+
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
|
74
|
+
run: cargo publish
|
|
75
|
+
|
|
76
|
+
# Runs on manual invocations that did not ask to publish: the same packaging
|
|
77
|
+
# and verification `cargo publish` performs, stopping before the upload.
|
|
78
|
+
dry-run:
|
|
79
|
+
name: publish --dry-run
|
|
80
|
+
needs: verify
|
|
81
|
+
runs-on: ubuntu-latest
|
|
82
|
+
if: github.event_name == 'workflow_dispatch' && inputs.publish != true
|
|
83
|
+
steps:
|
|
84
|
+
- uses: actions/checkout@v4
|
|
85
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
86
|
+
- uses: Swatinem/rust-cache@v2
|
|
87
|
+
- run: cargo publish --dry-run
|