puressh 0.1.2

A pure-Rust SSH (Secure Shell) protocol library, in the spirit of libssh, built on purecrypto.
Documentation
name: CI

on:
  push:
    branches: [master]
  pull_request:

permissions:
  contents: read

env:
  CARGO_TERM_COLOR: always

jobs:
  test:
    name: Test & lint (${{ matrix.os }})
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
    steps:
      - uses: actions/checkout@v6

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

      - name: Cache
        uses: Swatinem/rust-cache@v2
        with:
          key: ${{ matrix.os }}

      # `pam-client2` (gated to Linux in Cargo.toml) links against libpam,
      # so the Ubuntu runner needs the dev headers + shared library before
      # any `--all-features` build. macOS and Windows don't see the dep at
      # all (target-conditional) so nothing extra is needed there.
      - name: Install libpam (Linux only)
        if: runner.os == 'Linux'
        run: sudo apt-get update && sudo apt-get install -y libpam0g-dev

      - name: Format
        if: runner.os == 'Linux' # formatting is platform-independent
        run: cargo fmt --all --check

      - name: Clippy (all features, warnings denied)
        run: cargo clippy --all-targets --all-features -- -D warnings

      - name: Test (all features)
        run: cargo test --all-features

  interop:
    name: OpenSSH interop (real sshd)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - name: Install Rust
        uses: dtolnay/rust-toolchain@stable

      - name: Cache
        uses: Swatinem/rust-cache@v2
        with:
          key: interop

      # The interop tests spawn the system `sshd` (client-mode tests) and use
      # `ssh-keygen`; both come from the OpenSSH packages. The runner ships
      # the client but not always the server. `libpam0g-dev` is needed because
      # building the test graph links our own `sshd` binary, which pulls in
      # `pam-client2` (a default feature on Linux).
      - name: Install OpenSSH server + client and libpam
        run: sudo apt-get update && sudo apt-get install -y openssh-server openssh-client libpam0g-dev

      # A non-root sshd still fatals without its privilege-separation
      # directory; a fresh runner that never started the ssh service may not
      # have created it.
      - name: Ensure sshd privilege-separation dir exists
        run: sudo mkdir -p /run/sshd

      # These tests are `#[ignore]` by default (they need a real sshd), so the
      # normal `cargo test` job skips them. Run the deterministic ones here,
      # single-threaded so spawned sshd instances don't contend.
      #
      # client mode (puressh client ↔ real sshd): `exec_against_real_sshd`
      # (baseline) and `compression_zlib_interop_against_real_sshd` (the
      # `zlib@openssh.com` round-trip — our guard against the inflate-truncation
      # class of bug). The `interactive_shell_…chaff` test is excluded: it is
      # timing-sensitive and unsuitable for gating.
      - name: Run client-mode compression interop against real sshd
        run: >
          cargo test --test e2e_real_sshd --
          --ignored --test-threads=1 --nocapture
          exec_against_real_sshd compression_zlib_interop_against_real_sshd

      # server mode (real `ssh -C` client ↔ puressh sshd): proves our server
      # advertises, activates, and drives `zlib@openssh.com` compression.
      - name: Run server-mode compression interop against real ssh client
        run: >
          cargo test --test e2e_server_compression --
          --ignored --test-threads=1 --nocapture

  msrv:
    name: MSRV (Rust 1.88)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      # Pin the exact MSRV declared in Cargo.toml (`rust-version = "1.88"`).
      # Keep this version in sync with that field. This job exists so that an
      # accidental use of a newer-than-1.88 language/std feature — or a
      # dependency bump that raises the floor (as purecrypto <=0.6.13 silently
      # did to 1.95) — fails here instead of quietly bumping the real MSRV.
      # Cargo.lock is gitignored, so dependencies resolve fresh and cargo's
      # MSRV-aware resolver picks versions compatible with 1.88.
      - name: Install Rust 1.88
        uses: dtolnay/rust-toolchain@1.88

      - name: Cache
        uses: Swatinem/rust-cache@v2
        with:
          key: msrv

      # `--all-features` pulls in `pam` (Linux), which links libpam.
      - name: Install libpam
        run: sudo apt-get update && sudo apt-get install -y libpam0g-dev

      - name: Check (all features, all targets) on MSRV
        run: cargo check --all-targets --all-features

      - name: Build no_std + alloc on MSRV
        run: cargo build --no-default-features --features alloc

      - name: Build all module features, no std, on MSRV
        run: cargo build --no-default-features --features "alloc,client,server"

  no_std:
    name: no_std builds
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - name: Install Rust
        uses: dtolnay/rust-toolchain@stable

      - name: Cache
        uses: Swatinem/rust-cache@v2

      - name: Build no_std + alloc (no std)
        run: cargo build --no-default-features --features alloc

      - name: Build with all module features but no std
        run: cargo build --no-default-features --features "alloc,client,server"

  c_abi:
    name: C ABI smoke test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - name: Install Rust
        uses: dtolnay/rust-toolchain@stable

      - name: Cache
        uses: Swatinem/rust-cache@v2

      - name: Build the C library (static + shared)
        run: |
          cargo rustc --lib --release --features ffi --crate-type staticlib
          cargo rustc --lib --release --features ffi --crate-type cdylib

      - name: Compile and run the C smoke test
        run: |
          cc tests/ffi_smoke.c -I include target/release/libpuressh.a \
             -lpthread -ldl -lm -o ffi_smoke
          ./ffi_smoke

  docs:
    name: Docs build (warnings denied)
    runs-on: ubuntu-latest
    env:
      RUSTDOCFLAGS: -D warnings -D rustdoc::broken-intra-doc-links
    steps:
      - uses: actions/checkout@v6

      - name: Install Rust
        uses: dtolnay/rust-toolchain@stable

      - name: Cache
        uses: Swatinem/rust-cache@v2

      - name: Install libpam (for --all-features doc build)
        run: sudo apt-get update && sudo apt-get install -y libpam0g-dev

      - name: cargo doc --no-deps --all-features
        run: cargo doc --no-deps --all-features