sopsy 1.3.0

Public/private individual key encryption for repo secrets with biometrics support and explicit approval of who can decrypt. In other words — the missing good UX for SOPS
Documentation
# Continuous integration for sopsy.
#
# Runs on every push and pull request. The job fails if formatting, lints, or
# tests fail — each is a separate step so failures are easy to pin down.
#
# Integration tests shell out to real `sops` and `age`, so we install their
# Linux binaries here. `age-plugin-se` (Secure Enclave) is macOS-only and is
# mocked in tests, so it is intentionally not installed in CI.
name: CI

on:
  push:
    branches: [main]
  pull_request:

# Cancel superseded runs on the same ref to save CI minutes.
concurrency:
  group: ci-${{ github.ref }}
  cancel-in-progress: true

env:
  CARGO_TERM_COLOR: always
  # Pinned tool versions for reproducible integration tests.
  SOPS_VERSION: v3.9.4
  AGE_VERSION: v1.2.1

jobs:
  build:
    name: fmt + clippy + test
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

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

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

      - name: Install sops
        run: |
          curl -fsSL -o /usr/local/bin/sops \
            "https://github.com/getsops/sops/releases/download/${SOPS_VERSION}/sops-${SOPS_VERSION}.linux.amd64"
          chmod +x /usr/local/bin/sops
          sops --version

      - name: Install age
        run: |
          curl -fsSL -o age.tar.gz \
            "https://github.com/FiloSottile/age/releases/download/${AGE_VERSION}/age-${AGE_VERSION}-linux-amd64.tar.gz"
          tar -xzf age.tar.gz
          sudo install -m 0755 age/age /usr/local/bin/age
          sudo install -m 0755 age/age-keygen /usr/local/bin/age-keygen
          age --version

      # --- Quality gates: each step fails the build on its own. ---

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

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

      - name: Run tests
        run: cargo test --all

  coverage:
    name: coverage
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: llvm-tools-preview
      - uses: Swatinem/rust-cache@v2
      - uses: taiki-e/install-action@v2
        with:
          tool: cargo-llvm-cov

      # Tests shell out to real sops + age (same as the test job).
      - name: Install sops
        run: |
          curl -fsSL -o /usr/local/bin/sops \
            "https://github.com/getsops/sops/releases/download/${SOPS_VERSION}/sops-${SOPS_VERSION}.linux.amd64"
          chmod +x /usr/local/bin/sops
      - name: Install age
        run: |
          curl -fsSL -o age.tar.gz \
            "https://github.com/FiloSottile/age/releases/download/${AGE_VERSION}/age-${AGE_VERSION}-linux-amd64.tar.gz"
          tar -xzf age.tar.gz
          sudo install -m 0755 age/age age/age-keygen /usr/local/bin/

      - name: Generate coverage (lcov)
        run: cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info

      - name: Upload to Codecov
        uses: codecov/codecov-action@v5
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          files: lcov.info
          fail_ci_if_error: false # flip to true once the token is set and a run is green