# Development

> Build and test the Rust workspace, install the CLI locally, CI test matrix, and filesystem integration test environment variables.

- Repository: anomalyco/rift
- GitHub: https://github.com/anomalyco/rift
- Human docs: https://grok-wiki.com/public/docs/anomalyco-rift-ea3fd5dbf662
- Complete Markdown: https://grok-wiki.com/public/docs/anomalyco-rift-ea3fd5dbf662/llms-full.txt

## Source Files

- `README.md`
- `scripts/install.sh`
- `Cargo.toml`
- `.github/workflows/ci.yml`
- `crates/cli/tests/filesystem_e2e.rs`
- `scripts/ci/linux-fs.sh`

---

---
title: "Development"
description: "Build and test the Rust workspace, install the CLI locally, CI test matrix, and filesystem integration test environment variables."
---

The Rift repository is a Cargo workspace (`crates/core`, `crates/cli`, `crates/ffi`) versioned at `0.0.10` with edition 2024. Day-to-day development runs `cargo test --workspace --locked` for portable unit and manager tests, and `./scripts/install.sh` to place an optimized `rift` binary at `${CARGO_HOME:-$HOME/.cargo}/bin/rift`. Copy-on-write filesystem integration tests are opt-in: they early-return unless a `RIFT_REQUIRE_*` environment variable is set, and CI mounts synthetic filesystem fixtures before running them.

## Workspace layout

```text
rift/                          Cargo workspace root
├── crates/core/               `rift` library — Manager, strategies, registry
├── crates/cli/                `rift-cli` — `rift` binary and CLI e2e tests
├── crates/ffi/                `rift-ffi` — cdylib for JavaScript bindings
├── scripts/install.sh         Local CLI install
└── scripts/ci/linux-fs.sh     CI filesystem fixture runner
```

| Crate | Package name | Artifact |
| --- | --- | --- |
| `crates/core` | `rift` | Library + `create` / `compare` benches |
| `crates/cli` | `rift-cli` | `rift` executable |
| `crates/ffi` | `rift-ffi` | `cdylib` for npm FFI bindings |

<Note>
Always pass `--locked` when building or testing. CI and release workflows use the lockfile to pin dependency versions.
</Note>

## Build and test

### Default test run

The standard workspace test command exercises unit tests, manager logic with `TestStrategy`, CLI argument parsing, and FFI protocol serialization. It does **not** execute real copy-on-write operations unless the checkout filesystem happens to satisfy a strategy probe and the corresponding `RIFT_REQUIRE_*` variable is set.

```bash
cargo test --workspace --locked
```

### Per-crate testing

| Command | Scope |
| --- | --- |
| `cargo test --package rift --locked` | Core library, strategy modules, `linux_filesystem_tests` |
| `cargo test --package rift-cli --locked` | CLI unit tests + `filesystem_e2e` integration tests |
| `cargo test --package rift-ffi --locked` | FFI request/response protocol tests |

### Release build

```bash
cargo build --release --package rift-cli --locked
```

The release workflow builds `rift` for `x86_64-unknown-linux-gnu`, `x86_64-apple-darwin`, and `aarch64-apple-darwin` after the same `cargo test --workspace --locked` gate.

## Install the CLI locally

`scripts/install.sh` installs an optimized binary from the workspace checkout:

```bash
./scripts/install.sh
```

The script runs:

```bash
cargo install --path crates/cli --root "${CARGO_HOME:-$HOME/.cargo}" --force --locked
```

Verify with `rift --help`. The installed binary is the same `rift` target defined in `crates/cli/Cargo.toml`.

<Tip>
Use the locally installed binary when exercising shell integration (`rift shell-init`) or manual quickstart flows against real directories.
</Tip>

## Test layers

Rift separates tests by filesystem dependency. Understanding which layer a test belongs to explains why `cargo test --workspace` passes on a laptop without btrfs or APFS fixtures.

```text
┌─────────────────────────────────────────────────────────────┐
│  Layer 1 — Always runs (any OS, any filesystem)             │
│  core/tests.rs (TestStrategy), cli/main.rs unit tests,      │
│  ffi protocol tests, config/registry/git/filter tests       │
└─────────────────────────────────────────────────────────────┘
                              │
┌─────────────────────────────────────────────────────────────┐
│  Layer 2 — Strategy probes (auto-skip when probe fails)     │
│  btrfs/reflink/apfs module tests using tempdir_in(cwd)    │
└─────────────────────────────────────────────────────────────┘
                              │
┌─────────────────────────────────────────────────────────────┐
│  Layer 3 — Gated integration (requires RIFT_REQUIRE_* env)  │
│  linux_filesystem_tests, filesystem_e2e, CI env assertions  │
└─────────────────────────────────────────────────────────────┘
```

**Layer 1** uses `TestStrategy` and `FailureStrategy` mocks so manager, registry, hook, and Git semantics are validated without touching the production CoW backend.

**Layer 2** creates fixtures under `std::env::current_dir()` so temp directories land on the same mounted device as the checkout. Tests call `reflink_temp()` or `btrfs_temp()` and return early when the probe fails.

**Layer 3** checks `RIFT_REQUIRE_*` at the start of each test and returns immediately when unset. CI sets exactly one variable per matrix row before invoking tests on a prepared filesystem.

## CI test matrix

GitHub Actions workflow `.github/workflows/ci.yml` defines three job families.

### Unit test job

| Field | Value |
| --- | --- |
| Runner | `ubuntu-latest` |
| Command | `cargo test --workspace --locked` |
| Trigger | Push to `dev`, all pull requests |

### Linux filesystem matrix

| Filesystem fixture | APT packages | Environment variable | Expected behavior |
| --- | --- | --- | --- |
| `btrfs` | `btrfs-progs` | `RIFT_REQUIRE_BTRFS_TESTS=1` | Writable btrfs subvolumes, subvolume snapshots |
| `xfs-reflink` | `xfsprogs` | `RIFT_REQUIRE_REFLINK_TESTS=1` | Native per-file reflinks (`mkfs.xfs -m reflink=1`) |
| `zfs` | `zfsutils-linux` | `RIFT_REQUIRE_REFLINK_TESTS=1` | ZFS reflink clone support |
| `xfs-no-reflink` | `xfsprogs` | `RIFT_REQUIRE_UNSUPPORTED_LINUX_TESTS=1` | Reflink probe fails; init/create reject with `CowUnavailable` |
| `ext4` | `e2fsprogs` | `RIFT_REQUIRE_UNSUPPORTED_LINUX_TESTS=1` | No CoW; fail-closed |
| `tmpfs` | _(none)_ | `RIFT_REQUIRE_UNSUPPORTED_LINUX_TESTS=1` | No CoW; fail-closed |

Each matrix row runs:

```bash
bash scripts/ci/linux-fs.sh <filesystem> -- \
  env <required_env>=1 cargo test --package rift --package rift-cli --locked
```

`fail-fast: false` keeps independent filesystem rows running when one fails.

### APFS integration job

| Field | Value |
| --- | --- |
| Runner | `macos-14` |
| Environment | `RIFT_REQUIRE_APFS_TESTS=1` |
| Command | `cargo test --package rift --locked` |

## Filesystem integration environment variables

Four environment variables gate Layer 3 tests. When unset, gated tests return immediately without failure.

<ParamField body="RIFT_REQUIRE_BTRFS_TESTS" type="flag">
Set to any value (CI uses `1`). Enables btrfs round-trip tests in `linux_filesystem_tests`, CLI `filesystem_e2e`, and asserts initialized paths are btrfs subvolumes. The btrfs strategy integration test fails the run if the checkout is not on btrfs when this variable is set.
</ParamField>

<ParamField body="RIFT_REQUIRE_REFLINK_TESTS" type="flag">
Set to any value. Enables native reflink round-trip tests on non-btrfs Linux filesystems (XFS with reflink, ZFS). Asserts shared extents when reliable and verifies copy divergence after mutation. The reflink strategy integration test fails if the checkout lacks reflink support when this variable is set.
</ParamField>

<ParamField body="RIFT_REQUIRE_UNSUPPORTED_LINUX_TESTS" type="flag">
Set to any value. Enables fail-closed tests: `init` returns `CowUnavailable`, no `.rift` marker is written, `create` returns `WorkspaceNotInitialized`, and reflink probe artifacts (`.rift-reflink-probe*`) are cleaned up.
</ParamField>

<ParamField body="RIFT_REQUIRE_APFS_TESTS" type="flag">
Set to any value on macOS. Enables the APFS CI integration assertion that `ApfsStrategy::copy_directory` succeeds on the runner filesystem.
</ParamField>

### Variable-to-test mapping

| Variable | Core tests | CLI e2e tests | Strategy assertions |
| --- | --- | --- | --- |
| `RIFT_REQUIRE_BTRFS_TESTS` | `production_supported_linux_filesystem_*` | `supported_filesystem_cli_round_trip` | `btrfs_integration_environment_is_available` |
| `RIFT_REQUIRE_REFLINK_TESTS` | `production_supported_linux_filesystem_*` | `supported_filesystem_cli_round_trip` | `linux_reflink_integration_environment_is_available` |
| `RIFT_REQUIRE_UNSUPPORTED_LINUX_TESTS` | `production_unsupported_linux_filesystem_rejects_management` | `unsupported_filesystem_cli_fails_closed` | Reflink probe preflight in `linux-fs.sh` |
| `RIFT_REQUIRE_APFS_TESTS` | — | — | `integration_environment_is_required_by_ci` |

<Warning>
`RIFT_REQUIRE_BTRFS_TESTS` and `RIFT_REQUIRE_REFLINK_TESTS` both enable the **supported** round-trip suites. CI sets only one per matrix row. Do not set both locally unless the checkout is btrfs (btrfs takes precedence in strategy selection).
</Warning>

## `linux-fs.sh` fixture runner

`scripts/ci/linux-fs.sh` prepares an isolated filesystem mount, copies the repository checkout onto it, runs capability preflights, and executes the supplied test command from the mounted copy.

```text
GITHUB_WORKSPACE ──copy──► /mnt/rift-<fs>/rift/  ──cd──► cargo test ...
         │                         │
         │                  loopback image or tmpfs
         │                  (1 GiB for block-backed FS)
         └─ required env var
```

### Usage

```bash
bash scripts/ci/linux-fs.sh <btrfs|xfs-reflink|xfs-no-reflink|ext4|tmpfs|zfs> [-- <command> ...]
```

### Required and optional host variables

| Variable | Required | Purpose |
| --- | --- | --- |
| `GITHUB_WORKSPACE` | Yes | Source checkout path to copy onto the fixture mount |
| `RUNNER_TEMP` | No | Directory for loopback images; defaults to `mktemp -d` |
| `GITHUB_RUN_ID` | No | ZFS pool name suffix for collision avoidance |

### Preflight checks

Before running tests, the script verifies:

- Mount fstype matches the requested fixture (`btrfs`, `xfs`, `ext4`, `tmpfs`, or `zfs`)
- Checkout is writable by the current user
- Registry temp paths reside on the same device as the checkout
- Capability probes pass or fail as expected:
  - **btrfs**: `btrfs subvolume create` / `delete`
  - **xfs-reflink, zfs**: `cp --reflink=always` succeeds
  - **xfs-no-reflink, ext4, tmpfs**: reflink probe fails

Cleanup unmounts loop devices, destroys ZFS pools, and removes mount points on exit.

### Run filesystem tests locally

Reproduce a CI matrix row on a Linux machine with `sudo` access:

```bash
export GITHUB_WORKSPACE="$(pwd)"

bash scripts/ci/linux-fs.sh btrfs -- \
  env RIFT_REQUIRE_BTRFS_TESTS=1 cargo test --package rift --package rift-cli --locked
```

For unsupported-filesystem fail-closed behavior:

```bash
bash scripts/ci/linux-fs.sh ext4 -- \
  env RIFT_REQUIRE_UNSUPPORTED_LINUX_TESTS=1 cargo test --package rift --package rift-cli --locked
```

On macOS with APFS, run the APFS job equivalent directly:

```bash
RIFT_REQUIRE_APFS_TESTS=1 cargo test --package rift --locked
```

<Note>
Filesystem fixtures create temp directories under `current_dir()` (the mounted checkout). Run `linux-fs.sh` from a clean working tree so probe and e2e artifacts do not pollute your primary checkout.
</Note>

## CLI e2e test isolation

`crates/cli/tests/filesystem_e2e.rs` exercises the real `rift` binary through `CliFixture`. Each invocation passes a dedicated registry via the hidden `--database` flag and isolates state with:

- `HOME` → fixture-local home directory
- `XDG_DATA_HOME` → fixture-local data directory (default DB path: `$XDG_DATA_HOME/rift/rift.sqlite`)

Supported round-trip e2e covers `init --here`, `create`, `--into` custom storage, `list`, `ancestors`, cross-filesystem `--into` failure, `remove`, and `gc`. Unsupported e2e verifies stderr contains `copy-on-write cloning unavailable` or `no initialized workspace found` and that no registry rows are written.

## Benchmarks

Performance measurement lives in `crates/core/benches/`. See the benchmarking page for `create` and `compare` bench usage, sample counts, and JSON output schema.

## Related pages

<CardGroup>
  <Card title="Installation" href="/installation">
    Install via npm, bun, or Cargo build script; prebuilt binary layout.
  </Card>
  <Card title="Copy strategies and platforms" href="/copy-strategies">
    btrfs, reflink, and APFS backends exercised by the filesystem test matrix.
  </Card>
  <Card title="Benchmarking" href="/benchmarking">
    Measure and compare `rift create` performance with Cargo benches.
  </Card>
  <Card title="CLI reference" href="/cli-reference">
    Hidden `--database` and `--shell-cwd` flags used by e2e fixtures.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    Common failure modes surfaced by integration tests: CoW unavailable, missing markers, unsafe Git state.
  </Card>
</CardGroup>
