Skip to content
Closed
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
114 changes: 114 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,120 @@ jobs:
- name: Test default features
run: cargo test

# The adapter's `memory-git` feature gates the diff family, and the test
# that the family is *withheld* without it is `#[cfg(not(feature =
# "memory-git"))]`. Both the `--all-features` and default runs above
# compile that test out, so without this step it would be checked by
# nothing — a feature-gated test whose default fate is to be built by one
# job and executed by none.
#
# This is also the configuration that keeps the promise the feature
# exists for: no `git2` / `libgit2-sys` in the graph.
# `api/Cargo.toml` spells out this exact command in a comment and asks
# that the contract crate never link a storage engine, a native library,
# an HTTP client, or an async runtime. It was left as a comment, so
# nothing checked it — and a forbidden dependency arrives transitively,
# through a feature someone enabled two crates away, which is precisely
# the way nobody notices.
#
# The FORWARD form is required. `cargo tree -i <crate> -p tinymemory-api`
# discards the `-p` scope, prints the whole-workspace inverse tree, and
# exits 0 looking clean even when this crate is the one at fault. The
# manifest says so; this runs what it says.
# `cargo build --all-targets` only *compiles* an example. `AGENTS.md`
# promises `cargo run --example basic` works, and a compiled example can
# still panic on its first line — which is the state the repository was in
# before issue #18 §E7, when the command was documented and there was no
# `examples/` directory at all.
# §D5. Prints the dependency count of every build configuration and fails
# when the minimal one grows past its ceiling. The property this protects
# — that asking for no features gets you the contract and nothing that
# links a storage engine or an HTTP stack — is invisible in a diff,
# because the dependency arrives transitively through a feature enabled
# two crates away.
- name: Dependency budget
run: ./scripts/ci/dependency-budget.sh

- name: Run the bundled example
run: cargo run --example basic

- name: Assert the contract crate stays free of heavy dependencies
run: |
forbidden="$(cargo tree -p tinymemory-api -e normal,build --prefix none \
| grep -Ei 'rusqlite|libsqlite|git2|reqwest|regex|tokio' || true)"
if [ -n "$forbidden" ]; then
echo "tinymemory-api pulled in a dependency its manifest forbids:" >&2
echo "$forbidden" >&2
echo >&2
echo "The contract is what hosts compile against. It must stay free of" >&2
echo "storage engines, native libraries, HTTP clients and async runtimes." >&2
exit 1
fi

# The minimal build has to stay genuinely usable, not merely compile:
# a host that wants the ports wired and nothing retained must be able to
# bind the null driver without pulling an engine in behind it.
- name: Build and bind the minimal configuration
run: |
cargo build -p tinymemory --no-default-features
cargo test -p tinymemory --no-default-features --test null_provider

- name: Lint and test the adapter without its optional engine features
run: |
cargo clippy -p tinymemory-tinycortex --all-targets --no-default-features -- -D warnings
cargo test -p tinymemory-tinycortex --no-default-features

- name: Assert the default adapter build links no native git
run: |
linked="$(cargo tree -p tinymemory-tinycortex --no-default-features \
-e normal --prefix none | grep -cE '^(git2|libgit2-sys)' || true)"
if [ "$linked" -ne 0 ]; then
echo "the default adapter build linked $linked native-git crate(s);" >&2
echo "the memory-git feature exists to keep them out" >&2
exit 1
fi

# Feature-unification and coverage. Their own job: both are slower than the
# main lane and independent of it, so a failure in one should not mask the
# other, and neither should delay the fast feedback the main job gives.
feature-matrix:
name: Feature powerset and coverage
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
persist-credentials: false

- uses: dtolnay/rust-toolchain@stable

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority medium security uncertain

dtolnay/rust-toolchain is pinned to stable, which is mutable

A tag or branch can be repointed by whoever owns dtolnay, and the new code runs with this workflow's secrets. Pin to a full commit SHA and let Dependabot bump it.

[RULE] unpinned-action ·

with:
components: llvm-tools-preview

- uses: Swatinem/rust-cache@v2

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority medium security uncertain

Swatinem/rust-cache is pinned to v2, which is mutable

A tag or branch can be repointed by whoever owns Swatinem, and the new code runs with this workflow's secrets. Pin to a full commit SHA and let Dependabot bump it.

[RULE] unpinned-action ·


- uses: taiki-e/install-action@v2

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority medium security uncertain

taiki-e/install-action is pinned to v2, which is mutable

A tag or branch can be repointed by whoever owns taiki-e, and the new code runs with this workflow's secrets. Pin to a full commit SHA and let Dependabot bump it.

[RULE] unpinned-action ·

with:
tool: cargo-hack,cargo-llvm-cov

# §E2's second half. Cargo features are additive: enabling one for crate A
# enables it for every consumer in the graph, so a pair that nobody builds
# deliberately can still be built by someone else's dependency. `--depth 2`
# covers every pair without the combinatorial blow-up of the full set.
#
# Check-only: this is about whether the combinations *compile*, and the
# behaviour of each is the main job's business.
- name: Feature powerset compiles
run: cargo hack --feature-powerset --depth 2 --workspace check --all-targets

# §E8. `AGENTS.md` asks for 80% of meaningful library behaviour and
# nothing measured it. Reported rather than enforced to begin with: a
# threshold picked before anyone has seen the number is a guess, and a
# failing gate on day one gets disabled rather than fixed.
- name: Coverage
run: |
cargo llvm-cov --all-features --workspace --summary-only \
| tee "$GITHUB_STEP_SUMMARY"

# The module crate is its own workspace root (see the `exclude` note in the
# root Cargo.toml), so NONE of the steps above touch it: `--all-targets`,
# `--all-features` and `--workspace` all stop at the workspace boundary and
Expand Down
8 changes: 8 additions & 0 deletions Cargo.lock

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

20 changes: 20 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,13 @@ serde = { version = "1", features = ["derive"] }
[dev-dependencies]
# The mandatory-family tests are async.
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
# `TestHostConfig`, for the driver-selection tests. A dev-dependency: the
# facade must not carry a test double into a consumer's graph.
tinymemory-api = { path = "api", features = ["test-support"] }
# The reference driver and the behavioural suite, for the workspace-level
# integration tests. A dev-dependency only: the facade must not carry a test
# harness into a consumer's dependency graph.
tinymemory-conformance = { path = "conformance" }

[features]
default = []
Expand Down Expand Up @@ -113,6 +120,19 @@ private_intra_doc_links = "warn"
# so a host that already pins its own engine checkout unifies onto one copy
# through its own patch table. These entries are what make a *standalone* build
# of this workspace resolve them to the nested `vendor/` submodules.
# `tinycortex-api` takes this workspace's own contract crate by git, because
# neither crate is published (tinymemory#18 §A1). Without this entry cargo
# resolves the git copy *and* the path copy as two distinct crates, and
# `tinymemory_api::MemoryCategory` from one is not the same type as from the
# other — which is the exact duplication §A1 exists to delete, reintroduced by
# the fix for it. The error is loud rather than silent, but only at the seam.
#
# Patch tables apply from the workspace root being built, so this covers builds
# and tests here. A host that embeds this workspace needs the same entry, the
# same way it already patches tinycortex.
[patch."https://github.com/tinyhumansai/tinymemory"]
tinymemory-api = { path = "api" }

[patch.crates-io]
tinycortex = { path = "vendor/tinycortex" }
tinycortex-api = { path = "vendor/tinycortex/api" }
Expand Down
Loading
Loading