fstool 0.3.0

Build disk images and filesystems (ext2/3/4, MBR, GPT) from a directory tree and TOML spec, in the spirit of genext2fs.
Documentation
name: CI

on:
  push:
    branches: [master]
  pull_request:

env:
  CARGO_TERM_COLOR: always
  RUSTFLAGS: "-D warnings"

jobs:
  # Linux runs the full battery: fmt, clippy, build, test, doc. The
  # external cross-validation tests (e2fsck, mke2fs, sgdisk, qemu-img,
  # …) only fire here because they need apt-supplied tools.
  test-linux:
    name: test (ubuntu)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - name: Install system tools
        run: |
          sudo apt-get update
          sudo apt-get install -y \
            gdisk \
            fdisk \
            e2fsprogs \
            genext2fs \
            dosfstools \
            mtools \
            qemu-utils \
            exfatprogs \
            xfsprogs \
            hfsprogs \
            ntfs-3g \
            f2fs-tools \
            squashfs-tools
          # Sanity-check that everything we cross-validate against is on PATH.
          which sgdisk
          which fdisk
          which e2fsck
          which debugfs
          which mke2fs
          which genext2fs
          which fsck.vfat
          which mdir
          which mtype
          which qemu-img
          # Newer FS validators — best-effort: log the binary name(s) we
          # find, but don't fail the job if a package is missing on the
          # runner. The per-FS tests skip when their tool is absent.
          for tool in fsck.exfat mkfs.exfat xfs_repair xfs_db mkfs.xfs \
                      fsck.hfsplus newfs_hfsplus ntfsfix ntfsls ntfscat \
                      mkntfs fsck.f2fs mkfs.f2fs dump.f2fs \
                      unsquashfs mksquashfs; do
            if command -v "$tool" >/dev/null 2>&1; then
              echo "found: $tool ($(command -v "$tool"))"
            else
              echo "missing: $tool (test will skip)"
            fi
          done

      - uses: dtolnay/rust-toolchain@stable
        with:
          components: rustfmt, clippy

      - uses: Swatinem/rust-cache@v2

      - name: cargo fmt --check
        run: cargo fmt --all -- --check

      - name: cargo clippy
        run: cargo clippy --all-targets --all-features -- -D warnings

      - name: cargo build
        run: cargo build --all-targets --verbose

      - name: cargo test
        run: cargo test --all-targets --verbose

      - name: cargo doc
        run: cargo doc --no-deps --all-features
        env:
          RUSTDOCFLAGS: "-D warnings"

  # macOS proves the lib + CLI compile and unit-test cleanly on a
  # non-Linux Unix. `qemu-img` is available via Homebrew so the qcow2
  # round-trip suite runs here too; sgdisk / e2fsck / mke2fs / mtools
  # aren't packaged in stock Homebrew, so those tests skip themselves.
  test-macos:
    name: test (macos)
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v6

      - name: Install validators (qemu + squashfs-tools)
        run: |
          # ntfs-3g was removed from homebrew-core (deprecated upstream
          # since 2022). Skip it on macOS; ntfs writer tests fall back
          # to the skip path. APFS validation uses Apple's `hdiutil` +
          # `fsck_apfs` which ship built-in; same for `fsck_hfs` /
          # `newfs_hfs`. No exfat / xfs / f2fs Linux validators on
          # macOS; those tests skip themselves.
          brew install qemu squashfs
          for tool in hdiutil fsck_apfs fsck_hfs newfs_hfs \
                      unsquashfs mksquashfs qemu-img; do
            if command -v "$tool" >/dev/null 2>&1; then
              echo "found: $tool ($(command -v "$tool"))"
            else
              echo "missing: $tool (test will skip)"
            fi
          done

      - uses: dtolnay/rust-toolchain@stable

      - uses: Swatinem/rust-cache@v2

      - name: cargo build
        run: cargo build --all-targets --verbose

      - name: cargo test
        run: cargo test --all-targets --verbose

  # Windows proves the Unix-only paths in populate_from_host_dir /
  # build_plan / file.rs are properly cfg-gated. Almost all external
  # cross-validation tests skip here (their tools aren't on the runner),
  # which is the contract: the in-process tests must still pass.
  test-windows:
    name: test (windows)
    runs-on: windows-latest
    steps:
      - uses: actions/checkout@v6

      - uses: dtolnay/rust-toolchain@stable

      - uses: Swatinem/rust-cache@v2

      - name: cargo build
        run: cargo build --all-targets --verbose

      - name: cargo test
        run: cargo test --all-targets --verbose