prview 0.6.0

PR Review & Artifact Generator - cross-language PR analysis tool
Documentation
name: prview gate
description: Install prview and run the contractual prview gate automation check.

inputs:
  strict:
    description: Treat CONDITIONAL as a failing gate exit code.
    required: false
    default: "false"
  version:
    description: >-
      prview crate version to install (for example "0.5.0"), or "latest" for the
      newest published release. The gate command ships from the release line that
      first introduced `prview gate`; pin to that release once it is published.
      "latest" is a resilient default: it never fails to resolve a pinned version
      that does not yet exist on crates.io.
    required: false
    default: "latest"
  args:
    description: Additional whitespace-separated arguments passed to prview gate.
    required: false
    default: ""

outputs:
  exit-code:
    description: Raw prview gate process exit code.
    value: ${{ steps.gate.outputs.exit-code }}
  json-path:
    description: Path to the captured prview gate JSON output.
    value: ${{ steps.gate.outputs.json-path }}
  sarif-path:
    description: Path to INLINE_FINDINGS.sarif when the run produced one.
    value: ${{ steps.gate.outputs.sarif-path }}

runs:
  using: composite
  steps:
    - name: Add Cargo bin directory to PATH
      shell: bash
      run: echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"

    - name: Install prview
      id: install
      shell: bash
      env:
        PRVIEW_VERSION: ${{ inputs.version }}
      run: |
        set -euo pipefail

        if ! command -v cargo >/dev/null 2>&1; then
          echo "cargo is required to install prview" >&2
          exit 3
        fi

        if [ "$PRVIEW_VERSION" = "latest" ]; then
          BINSTALL_SPEC="prview"
          CARGO_VERSION_ARGS=()
        else
          BINSTALL_SPEC="prview@${PRVIEW_VERSION}"
          CARGO_VERSION_ARGS=(--version "$PRVIEW_VERSION")
        fi

        install_method=""
        if command -v cargo-binstall >/dev/null 2>&1; then
          if cargo binstall --no-confirm --force "$BINSTALL_SPEC"; then
            install_method="cargo-binstall"
          else
            echo "cargo-binstall could not install ${BINSTALL_SPEC}; falling back to cargo install" >&2
          fi
        fi

        if [ -z "$install_method" ]; then
          cargo install prview ${CARGO_VERSION_ARGS[@]+"${CARGO_VERSION_ARGS[@]}"} --locked --force
          install_method="cargo-install"
        fi

        if ! prview gate --help >/dev/null 2>&1; then
          installed_version="$(prview --version 2>/dev/null || true)"
          if [ -z "$installed_version" ]; then
            installed_version="unknown version"
          fi
          echo "installed ${installed_version} has no \`gate\` subcommand; pin a source build / wait for 0.6.0" >&2
          exit 3
        fi

        echo "method=${install_method}" >> "$GITHUB_OUTPUT"

    - name: Run prview gate
      id: gate
      shell: bash
      env:
        PRVIEW_STRICT: ${{ inputs.strict }}
        PRVIEW_ARGS: ${{ inputs.args }}
        INSTALL_METHOD: ${{ steps.install.outputs.method }}
      run: |
        set -euo pipefail

        case "$PRVIEW_STRICT" in
          true|false) ;;
          *)
            echo "strict must be 'true' or 'false', got '${PRVIEW_STRICT}'" >&2
            exit 3
            ;;
        esac

        JSON_PATH="${RUNNER_TEMP:-/tmp}/prview-gate-${GITHUB_RUN_ID:-local}.json"
        cmd=(prview gate --json)

        if [ "$PRVIEW_STRICT" = "true" ]; then
          cmd+=(--strict)
        fi

        if [ -n "$PRVIEW_ARGS" ]; then
          read -r -a extra_args <<< "$PRVIEW_ARGS"
          cmd+=(${extra_args[@]+"${extra_args[@]}"})
        fi

        set +e
        "${cmd[@]}" > "$JSON_PATH"
        status=$?
        set -e

        verdict=""
        output_dir=""
        merge_gate_json=""
        sarif_path=""

        if command -v jq >/dev/null 2>&1 && [ -s "$JSON_PATH" ]; then
          verdict="$(jq -r '.verdict // empty' "$JSON_PATH" 2>/dev/null || true)"
          output_dir="$(jq -r '.output_dir // empty' "$JSON_PATH" 2>/dev/null || true)"
          merge_gate_json="$(jq -r '.merge_gate_json // empty' "$JSON_PATH" 2>/dev/null || true)"
        fi

        if [ -n "$output_dir" ] && [ -f "${output_dir}/30_context/INLINE_FINDINGS.sarif" ]; then
          sarif_path="${output_dir}/30_context/INLINE_FINDINGS.sarif"
        fi

        case "$status" in
          0)
            result="PASS or CONDITIONAL accepted by non-strict mode"
            action_exit=0
            ;;
          1)
            result="BLOCK"
            action_exit=1
            ;;
          2)
            # Exit 2 is ambiguous: the gate contract uses it for CONDITIONAL
            # under --strict, but clap ALSO exits 2 on a CLI usage error (e.g.
            # a typo or unknown flag in the `args` input). A real verdict always
            # emits its gate JSON to stdout (captured in JSON_PATH); a usage
            # error writes nothing to stdout (clap prints to stderr). Use that
            # sentinel so an argument typo surfaces as a usage error, not a
            # phantom "CONDITIONAL rejected" verdict.
            if [ -s "$JSON_PATH" ]; then
              result="CONDITIONAL rejected by strict mode"
              action_exit=2
            else
              echo "prview gate exited 2 without emitting a gate verdict; treating as a CLI usage error (check the 'args' input), not a CONDITIONAL verdict" >&2
              result="usage error: prview gate rejected its arguments (exit 2, no verdict emitted)"
              action_exit=3
            fi
            ;;
          3)
            result="internal gate execution error"
            action_exit=3
            ;;
          *)
            result="unexpected non-contract exit code"
            action_exit=3
            ;;
        esac

        {
          echo "exit-code=${status}"
          echo "json-path=${JSON_PATH}"
          echo "sarif-path=${sarif_path}"
        } >> "$GITHUB_OUTPUT"

        if [ -n "${GITHUB_STEP_SUMMARY:-}" ]; then
          {
            echo "## prview gate"
            echo
            echo "| Field | Value |"
            echo "|---|---|"
            echo "| Decision source | exit code only |"
            echo "| Exit code | \`${status}\` |"
            echo "| Result | ${result} |"
            echo "| Strict | \`${PRVIEW_STRICT}\` |"
            echo "| Install method | \`${INSTALL_METHOD:-unknown}\` |"
            if [ -n "$verdict" ]; then
              echo "| JSON verdict | \`${verdict}\` |"
            fi
            if [ -n "$merge_gate_json" ]; then
              echo "| MERGE_GATE.json | \`${merge_gate_json}\` |"
            fi
            if [ -n "$sarif_path" ]; then
              echo "| SARIF | \`${sarif_path}\` |"
            else
              echo "| SARIF | not produced for this run |"
            fi
            echo
            echo "JSON is parsed only for summary details and artifact paths; pass/fail is mapped from the prview gate exit code contract."
          } >> "$GITHUB_STEP_SUMMARY"
        fi

        exit "$action_exit"