hvac-transcoder 5.2.2

GPU-accelerated media transcoder (HEVC/h265 via NVENC, VAAPI, VideoToolbox). Single-binary Tdarr alternative.
name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

# Cancel superseded runs on the same branch. The matrix test job takes
# 40-90 s on each runner and there's no value in finishing a stale run
# after a force-push.
concurrency:
  group: ci-${{ github.ref }}
  cancel-in-progress: true

env:
  CARGO_TERM_COLOR: always
  RUST_BACKTRACE: short

jobs:
  # ── Core: fmt + clippy + test on Ubuntu and macOS ────────────────────────
  #
  # Same command set the pre-commit hook runs locally, so a green hook
  # means a green job here (modulo platform-specific test failures).
  test:
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, macos-latest]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy, rustfmt
      - uses: Swatinem/rust-cache@v2

      - name: Install ffmpeg
        if: runner.os == 'Linux'
        run: sudo apt-get update && sudo apt-get install -y ffmpeg
      - name: Install ffmpeg (macOS)
        if: runner.os == 'macOS'
        run: brew install ffmpeg

      - run: cargo fmt --check
      - run: cargo clippy -- -D warnings
      - run: cargo test

  # ── MSRV check: confirm the declared minimum still builds + tests ────────
  #
  # `rust-version` in Cargo.toml is the source of truth; this job re-runs
  # the binary build + unit tests on exactly that toolchain to catch any
  # silently-newer-Rust-feature creep.
  msrv:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Read rust-version from Cargo.toml
        id: msrv
        run: |
          msrv=$(grep -oE '^rust-version = "[^"]+"' Cargo.toml | sed -E 's/.*"([^"]+)"/\1/')
          if [ -z "$msrv" ]; then
            echo "rust-version not declared in Cargo.toml"; exit 1
          fi
          echo "msrv=$msrv" >> "$GITHUB_OUTPUT"
      # dtolnay/rust-toolchain has no @stable-equivalent ref for arbitrary
      # toolchain inputs (the @stable / @nightly / @beta refs only work
      # for those exact channels) — @master is the documented entry point
      # for passing a `toolchain:` input. The action is small and audit-
      # able, and Dependabot watches it for upstream changes.
      - uses: dtolnay/rust-toolchain@master
        with:
          toolchain: ${{ steps.msrv.outputs.msrv }}
      - uses: Swatinem/rust-cache@v2
        with:
          key: msrv-${{ steps.msrv.outputs.msrv }}
      - run: sudo apt-get update && sudo apt-get install -y ffmpeg
      - run: cargo build
      # Run the full suite (unit + integration). MSRV regressions in a
      # test target should fail here too.
      - run: cargo test --all-targets

  # ── Doc build: catches broken intra-doc links + missing `///` on pub ─────
  doc:
    runs-on: ubuntu-latest
    env:
      # Treat warnings (private item without docs, broken links) as errors
      # so doc rot is caught at PR time, not at docs.rs build time.
      RUSTDOCFLAGS: "-D warnings"
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - run: cargo doc --no-deps --document-private-items

  # ── Security audit ────────────────────────────────────────────────────────
  #
  # rustsec/audit-check runs against the RustSec advisory DB. Fails the
  # PR if any dep has a published CVE. Dependabot opens the upgrade PR;
  # this job ensures the advisory is surfaced even if dependabot is slow.
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: rustsec/audit-check@v2.0.0
        with:
          token: ${{ secrets.GITHUB_TOKEN }}

  # ── License + supply-chain policy via cargo-deny ─────────────────────────
  #
  # See deny.toml for the policy. Notably we reject GPL / AGPL deps (this
  # crate is MIT) and require every dep to ship at least one accepted
  # license. Bans on duplicate crates catch accidental MSRV-driven
  # double-pinning.
  deny:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: EmbarkStudios/cargo-deny-action@v2
        with:
          command: check
          arguments: --all-features

  # ── Spell-check prose in source files + docs ─────────────────────────────
  typos:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: crate-ci/typos@v1.46.1

  # ── Coverage upload (codecov.io) ─────────────────────────────────────────
  #
  # cargo-llvm-cov runs the test suite under instrumentation, emits an
  # lcov.info, and codecov-action posts to codecov.io. Best-effort
  # against an external service: failure to upload doesn't fail the PR.
  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
        with:
          key: coverage
      - run: sudo apt-get update && sudo apt-get install -y ffmpeg
      - name: Install cargo-llvm-cov
        uses: taiki-e/install-action@v2
        with:
          tool: cargo-llvm-cov
      - name: Collect coverage
        # `--all-targets` so the lcov reflects both unit and integration
        # tests; otherwise tests/cli_smoke.rs coverage is invisible.
        run: cargo llvm-cov --all-targets --lcov --output-path lcov.info
      - name: Upload to codecov.io
        uses: codecov/codecov-action@v6
        with:
          files: lcov.info
          fail_ci_if_error: false
        env:
          CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

  # ── apt repo script tests (BATS) ─────────────────────────────────────────
  #
  # Runs tests/apt_repo.bats, which exercises update-apt-repo.sh against
  # real minimal .deb fixtures.  Ubuntu-only: needs apt-ftparchive.
  apt-repo-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install dependencies
        run: sudo apt-get install -y --no-install-recommends apt-utils bats binutils
      - name: Run apt repo tests
        run: bats tests/apt_repo.bats

  # ── Publish prompt log to jackdanger.com ─────────────────────────────────
  #
  # Picks up new / modified files under `promptlog/*.md` on push to main
  # and POSTs each as text/markdown to https://jackdanger.com/promptlog/.
  # Authenticated via the PROMPTLOG_TOKEN repository secret; if absent
  # (forks, dependabot branches) the job logs what it would have done
  # and exits zero.
  publish-promptlog:
    runs-on: ubuntu-latest
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4
        with:
          # `0` fetches the full history. We diff `before..sha` to catch
          # promptlog touches anywhere in the pushed range (batch merges,
          # manual pushes of several commits) rather than just the tip.
          fetch-depth: 0
      - name: Find changed promptlog files
        id: changed
        env:
          BEFORE_SHA: ${{ github.event.before }}
          AFTER_SHA: ${{ github.sha }}
        run: |
          # For the very first push to a branch BEFORE_SHA is all zeros
          # (no prior commit on this ref). Fall back to HEAD~1 there;
          # the workflow only runs on main so the fallback is safe.
          if [ "$BEFORE_SHA" = "0000000000000000000000000000000000000000" ] || [ -z "$BEFORE_SHA" ]; then
            BEFORE_SHA="${AFTER_SHA}~1"
          fi
          changed=$(git diff --name-only --diff-filter=AM "$BEFORE_SHA".."$AFTER_SHA" \
                    | grep -E '^promptlog/.+\.md$' \
                    | grep -v '^promptlog/README\.md$' || true)
          if [ -z "$changed" ]; then
            echo "no promptlog files changed; nothing to publish"
            echo "files=" >> "$GITHUB_OUTPUT"
            exit 0
          fi
          echo "files<<EOF" >> "$GITHUB_OUTPUT"
          echo "$changed"   >> "$GITHUB_OUTPUT"
          echo "EOF"        >> "$GITHUB_OUTPUT"
      - name: Publish each file
        if: steps.changed.outputs.files != ''
        env:
          PROMPTLOG_TOKEN: ${{ secrets.PROMPTLOG_TOKEN }}
        run: |
          set -eu
          if [ -z "${PROMPTLOG_TOKEN:-}" ]; then
            echo "PROMPTLOG_TOKEN not configured — skipping upload."
            echo "Would have published:"
            echo "${{ steps.changed.outputs.files }}"
            exit 0
          fi
          while IFS= read -r file; do
            [ -z "$file" ] && continue
            slug=$(basename "$file" .md)
            echo "→ POST $file → https://jackdanger.com/promptlog/$slug"
            # --retry handles transient network blips so a 5xx burst
            # doesn't fail an otherwise-green main build. --retry-all-errors
            # so curl also retries connection-level failures, not just
            # HTTP status codes. --max-time guards against an endpoint
            # that hangs holding the connection.
            curl --fail-with-body -sS \
                 --retry 5 --retry-all-errors --retry-delay 5 \
                 --max-time 60 \
                 -X POST "https://jackdanger.com/promptlog/$slug" \
                 -H "Authorization: Bearer $PROMPTLOG_TOKEN" \
                 -H "Content-Type: text/markdown" \
                 --data-binary "@$file"
            echo
          done <<< "${{ steps.changed.outputs.files }}"