term-transcript 0.3.0-beta.1

Snapshotting and snapshot testing for CLI / REPL applications
Documentation
name: CI

on:
  push:
    branches: [ master ]
    tags: [ "v*" ]
  pull_request:
    branches: [ master ]

env:
  # Minimum supported Rust version.
  msrv: 1.61.0
  # Nightly Rust necessary for building docs.
  nightly: nightly-2022-11-24

jobs:
  build-msrv:
    strategy:
      matrix:
        include:
          - os: windows-latest
            features: ""
          - os: ubuntu-latest
            features: --all-features

    runs-on: ${{ matrix.os }}

    steps:
      - uses: actions/checkout@v3

      - name: Install Rust
        uses: actions-rs/toolchain@v1
        with:
          toolchain: ${{ env.msrv }}
          override: true
      - name: Generate lockfile
        uses: actions-rs/cargo@v1
        with:
          command: generate-lockfile

      - name: Cache cargo build
        uses: actions/cache@v3
        with:
          path: |
            target
            cli/target
          key: ${{ runner.os }}${{ matrix.features }}-msrv-cargo-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: ${{ runner.os }}${{ matrix.features }}-msrv-cargo

      - name: Run tests
        uses: actions-rs/cargo@v1
        with:
          command: test
          args: --workspace ${{ matrix.features }} --all-targets
      - name: Run CLI tests
        uses: actions-rs/cargo@v1
        with:
          command: test
          args: --manifest-path=cli/Cargo.toml ${{ matrix.features }} --all-targets
      - name: Run doc tests
        uses: actions-rs/cargo@v1
        with:
          command: test
          args: --workspace ${{ matrix.features }} --doc

  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3

      - name: Install Rust
        uses: actions-rs/toolchain@v1
        with:
          toolchain: stable
          override: true
          components: rustfmt, clippy
      - name: Generate lockfile
        uses: actions-rs/cargo@v1
        with:
          command: generate-lockfile

      - name: Cache cargo build
        uses: actions/cache@v3
        with:
          path: |
            target
            cli/target
          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: ${{ runner.os }}-cargo

      - name: Format
        uses: actions-rs/cargo@v1
        with:
          command: fmt
          args: --all -- --check
      - name: Clippy
        uses: actions-rs/clippy-check@v1
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          args: --workspace --all-features --all-targets -- -D warnings
      - name: Clippy CLI
        uses: actions-rs/clippy-check@v1
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          args: --manifest-path=cli/Cargo.toml --all-features --all-targets -- -D warnings
          name: clippy (CLI)
      - name: Clippy (no features)
        uses: actions-rs/clippy-check@v1
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          args: -p term-transcript --no-default-features --lib
          name: clippy (no features)
      - name: Clippy (features = svg)
        uses: actions-rs/clippy-check@v1
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          args: -p term-transcript --no-default-features --features svg --lib -- -D warnings
          name: clippy (features = svg)
      - name: Clippy (features = test)
        uses: actions-rs/clippy-check@v1
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          args: -p term-transcript --no-default-features --features test --lib -- -D warnings
          name: clippy (features = test)

      - name: Run tests
        uses: actions-rs/cargo@v1
        with:
          command: test
          args: --workspace --all-features --all-targets
      - name: Run CLI tests
        uses: actions-rs/cargo@v1
        with:
          command: test
          args: --manifest-path=cli/Cargo.toml --all-features --all-targets
      - name: Run doc tests
        uses: actions-rs/cargo@v1
        with:
          command: test
          args: --workspace --all-features --doc

      - name: Generate snapshots
        run: ./examples/generate-snapshots.sh
      - name: Test CLI tracing
        run: |
          RUST_LOG=term_transcript=debug \
          cargo run --manifest-path=cli/Cargo.toml --all-features -- \
          exec 'echo Hello' |& grep INFO

  build-docker:
    needs:
      - build
      - build-msrv
    permissions:
      contents: read
      packages: write
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3

      - name: Cache Docker build
        uses: actions/cache@v3
        with:
          path: target/docker
          key: ${{ runner.os }}-docker-buildkit-${{ hashFiles('cli/Cargo.lock') }}
          restore-keys: ${{ runner.os }}-docker-buildkit

      - name: Install `socat`
        run: |
          sudo apt-get update && \
          sudo apt-get install -y --no-install-suggests --no-install-recommends socat

      - name: Extract Docker metadata
        id: meta
        uses: docker/metadata-action@v4
        with:
          images: ghcr.io/${{ github.repository }}

      - name: Log in to Container registry
        uses: docker/login-action@v2
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2
      - name: Identify Buildx container
        run: |
          CONTAINER_ID=$(docker ps --filter=ancestor=moby/buildkit:buildx-stable-1 --format='{{ .ID }}')
          echo "buildx_container=$CONTAINER_ID" | tee -a "$GITHUB_ENV"

      - name: Restore cache
        run: |
          if [[ -f target/docker/cache.db ]]; then
            docker cp target/docker/. "$buildx_container:/var/lib/buildkit"
            docker restart "$buildx_container"
            # Wait until the container is restarted
            sleep 5
          fi
          docker buildx du # Check the restored cache

      - name: Build image
        uses: docker/build-push-action@v3
        with:
          context: .
          file: cli/Dockerfile
          load: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}

      # We want to only store cache volumes (type=exec.cachemount) since
      # their creation is computationally bound as opposed to other I/O-bound volume types.
      - name: Extract image cache
        run: |
          docker buildx prune --force --filter=type=regular
          docker buildx prune --force --filter=type=source.local
          rm -rf target/docker && mkdir -p target/docker
          docker cp "$buildx_container:/var/lib/buildkit/." target/docker
          du -ah -d 1 target/docker

      - name: Test image (--help)
        run: docker run --rm "$IMAGE_TAG" --help
        env:
          IMAGE_TAG: ${{ fromJSON(steps.meta.outputs.json).tags[0] }}
      - name: Test image (print)
        run: |
          docker run -i --rm --env COLOR=always "$IMAGE_TAG" print - < examples/rainbow.svg
        env:
          IMAGE_TAG: ${{ fromJSON(steps.meta.outputs.json).tags[0] }}
      - name: Test image (exec, nc host)
        run: |
          mkfifo /tmp/shell.fifo
          cat /tmp/shell.fifo | bash -i 2>&1 | nc -lU /tmp/shell.sock > /tmp/shell.fifo &
          docker run --rm -v /tmp/shell.sock:/tmp/shell.sock "$IMAGE_TAG" \
            exec --shell nc --echoing --args=-U --args=/tmp/shell.sock 'ls -al' \
            > test.svg
          docker run -i --rm --env COLOR=always "$IMAGE_TAG" print - < test.svg
        env:
          IMAGE_TAG: ${{ fromJSON(steps.meta.outputs.json).tags[0] }}
      - name: Test image (exec, socat host)
        run: |
          rm -f /tmp/shell.sock
          socat UNIX-LISTEN:/tmp/shell.sock,fork EXEC:"bash -i",pty,setsid,ctty,stderr &
          docker run --rm -v /tmp/shell.sock:/tmp/shell.sock "$IMAGE_TAG" \
            exec --shell nc --echoing --args=-U --args=/tmp/shell.sock 'ls -al' \
            > test-pty.svg
          docker run -i --rm --env COLOR=always "$IMAGE_TAG" print - < test-pty.svg
        env:
          IMAGE_TAG: ${{ fromJSON(steps.meta.outputs.json).tags[0] }}

      - name: Publish image
        if: github.event_name == 'push'
        run: docker push "$IMAGE_TAG"
        env:
          IMAGE_TAG: ${{ fromJSON(steps.meta.outputs.json).tags[0] }}

  document:
    needs:
      - build
      - build-msrv
    if: github.event_name == 'push' && github.ref_type == 'branch'
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3

      - name: Install Rust
        uses: actions-rs/toolchain@v1
        with:
          toolchain: ${{ env.nightly }}
          profile: minimal
          override: true
      - name: Generate lockfile
        uses: actions-rs/cargo@v1
        with:
          command: generate-lockfile

      - name: Cache cargo build
        uses: actions/cache@v3
        with:
          path: target
          key: ${{ runner.os }}-cargo-document-${{ hashFiles('Cargo.lock') }}
          restore-keys: ${{ runner.os }}-cargo-document

      - name: Build docs
        run: |
          cargo clean --doc && \
          cargo rustdoc -p term-transcript --all-features -- --cfg docsrs

      - name: Copy examples
        run: |
          mkdir -p target/doc/examples && \
          cp examples/rainbow.html target/doc/examples

      - name: Deploy
        uses: JamesIves/github-pages-deploy-action@v4
        with:
          branch: gh-pages
          folder: target/doc
          single-commit: true