repolens 1.4.0

A CLI tool to audit and prepare repositories for open source or enterprise standards
Documentation
# =============================================================================
# RepoLens GitHub Actions Reusable Workflow
# =============================================================================
#
# This is a reusable workflow that can be called from other workflows in your
# repository or from other repositories.
#
# USAGE:
# 1. Copy this file to `.github/workflows/repolens.yml` in your repository
# 2. Call it from another workflow or use it directly
#
# CALLING FROM ANOTHER WORKFLOW:
# ```yaml
# jobs:
#   audit:
#     uses: ./.github/workflows/repolens.yml
#     with:
#       preset: 'opensource'
#       format: 'json'
#       fail-on: 'critical'
# ```
#
# OR USING THE OFFICIAL ACTION:
# ```yaml
# - uses: systm-d/repolens@main
#   with:
#     preset: 'opensource'
# ```
#
# =============================================================================

name: RepoLens Audit

on:
  # Allow this workflow to be called from other workflows
  workflow_call:
    inputs:
      preset:
        description: 'Audit preset (opensource, enterprise, strict)'
        required: false
        type: string
        default: 'opensource'
      format:
        description: 'Output format (terminal, json, sarif, markdown, html)'
        required: false
        type: string
        default: 'json'
      fail-on:
        description: 'Fail on severity level (critical, high, medium, low, none)'
        required: false
        type: string
        default: 'critical'
      version:
        description: 'RepoLens version to use'
        required: false
        type: string
        default: 'latest'
      config:
        description: 'Path to custom .repolens.toml config file'
        required: false
        type: string
        default: ''
      upload-artifact:
        description: 'Upload report as artifact'
        required: false
        type: boolean
        default: true
      upload-sarif:
        description: 'Upload SARIF to GitHub Security'
        required: false
        type: boolean
        default: false
    outputs:
      report-path:
        description: 'Path to the generated report'
        value: ${{ jobs.audit.outputs.report-path }}
      findings-count:
        description: 'Number of findings detected'
        value: ${{ jobs.audit.outputs.findings-count }}
      exit-code:
        description: 'Exit code from the audit'
        value: ${{ jobs.audit.outputs.exit-code }}

  # Also allow direct triggering
  workflow_dispatch:
    inputs:
      preset:
        description: 'Audit preset'
        required: false
        type: choice
        options:
          - opensource
          - enterprise
          - strict
        default: 'opensource'
      format:
        description: 'Output format'
        required: false
        type: choice
        options:
          - terminal
          - json
          - sarif
          - markdown
          - html
        default: 'json'
      fail-on:
        description: 'Fail on severity'
        required: false
        type: choice
        options:
          - critical
          - high
          - medium
          - low
          - none
        default: 'critical'

# Concurrency control
concurrency:
  group: repolens-${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  audit:
    name: RepoLens Audit
    runs-on: ubuntu-latest

    outputs:
      report-path: ${{ steps.audit.outputs.report-path }}
      findings-count: ${{ steps.audit.outputs.findings-count }}
      exit-code: ${{ steps.audit.outputs.exit-code }}

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 1

      - name: Determine version
        id: version
        run: |
          VERSION="${{ inputs.version || 'latest' }}"
          if [ "$VERSION" = "latest" ]; then
            RELEASE_TAG=$(curl -s https://api.github.com/repos/systm-d/repolens/releases/latest | grep '"tag_name"' | sed -E 's/.*"tag_name": *"([^"]+)".*/\1/')
            if [ -z "$RELEASE_TAG" ] || [ "$RELEASE_TAG" = "null" ]; then
              echo "::error::Failed to determine latest RepoLens version"
              exit 1
            fi
            VERSION="${RELEASE_TAG#v}"
          fi
          echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
          echo "tag=v${VERSION}" >> "$GITHUB_OUTPUT"
          echo "Installing RepoLens version: ${VERSION}"

      - name: Cache RepoLens binary
        id: cache
        uses: actions/cache@v4
        with:
          path: /usr/local/bin/repolens
          key: repolens-${{ runner.os }}-${{ steps.version.outputs.version }}

      - name: Install RepoLens
        if: steps.cache.outputs.cache-hit != 'true'
        run: |
          DOWNLOAD_URL="https://github.com/systm-d/repolens/releases/download/${{ steps.version.outputs.tag }}/repolens-linux-x86_64.tar.gz"

          echo "Downloading RepoLens from ${DOWNLOAD_URL}..."
          curl -sL "${DOWNLOAD_URL}" -o /tmp/repolens.tar.gz

          if ! file /tmp/repolens.tar.gz | grep -q 'gzip'; then
            echo "::error::Downloaded file is not a valid gzip archive"
            exit 1
          fi

          tar xzf /tmp/repolens.tar.gz -C /tmp
          chmod +x /tmp/repolens
          sudo mv /tmp/repolens /usr/local/bin/repolens
          rm -f /tmp/repolens.tar.gz

          echo "RepoLens installed successfully"

      - name: Verify installation
        run: repolens --version

      - name: Run RepoLens audit
        id: audit
        run: |
          # Configuration
          PRESET="${{ inputs.preset || 'opensource' }}"
          FORMAT="${{ inputs.format || 'json' }}"
          FAIL_ON="${{ inputs.fail-on || 'critical' }}"
          CONFIG="${{ inputs.config }}"

          # Determine file extension
          case "${FORMAT}" in
            json)     EXT="json" ;;
            sarif)    EXT="sarif" ;;
            markdown) EXT="md" ;;
            html)     EXT="html" ;;
            *)        EXT="txt" ;;
          esac

          REPORT_FILE="${{ runner.temp }}/repolens-report.${EXT}"

          # Build command
          CMD="repolens plan --preset ${PRESET} --format ${FORMAT}"
          [ -n "${CONFIG}" ] && CMD="${CMD} --config ${CONFIG}"
          [ "${FORMAT}" != "terminal" ] && CMD="${CMD} --output ${REPORT_FILE}"

          # Run audit
          set +e
          eval "${CMD}"
          AUDIT_EXIT_CODE=$?
          set -e

          # Set outputs
          echo "exit-code=${AUDIT_EXIT_CODE}" >> "$GITHUB_OUTPUT"
          echo "report-path=${REPORT_FILE}" >> "$GITHUB_OUTPUT"

          # Count findings (for JSON/SARIF)
          FINDINGS_COUNT=0
          if [ "${FORMAT}" = "json" ] && [ -f "${REPORT_FILE}" ]; then
            FINDINGS_COUNT=$(python3 -c "
          import sys, json
          try:
              data = json.load(open('${REPORT_FILE}'))
              if isinstance(data, list):
                  print(len(data))
              elif isinstance(data, dict) and 'findings' in data:
                  print(len(data['findings']))
              else:
                  print(0)
          except:
              print(0)
          " 2>/dev/null || echo "0")
          fi
          echo "findings-count=${FINDINGS_COUNT}" >> "$GITHUB_OUTPUT"

          echo "::notice::RepoLens audit completed with ${FINDINGS_COUNT} finding(s)"

          # Handle fail-on logic
          if [ "${FAIL_ON}" = "none" ]; then
            echo "fail-on is 'none', ignoring exit code"
            exit 0
          fi

          if [ ${AUDIT_EXIT_CODE} -ne 0 ]; then
            case "${FAIL_ON}" in
              critical)
                [ ${AUDIT_EXIT_CODE} -eq 1 ] && exit 1
                ;;
              high|medium|low)
                exit 1
                ;;
            esac
          fi

      - name: Upload report artifact
        if: ${{ (inputs.upload-artifact == true || inputs.upload-artifact == 'true') && inputs.format != 'terminal' }}
        uses: actions/upload-artifact@v4
        with:
          name: repolens-report
          path: ${{ steps.audit.outputs.report-path }}
          retention-days: 30
          if-no-files-found: ignore

      - name: Generate SARIF for Security tab
        if: ${{ inputs.upload-sarif == true || inputs.upload-sarif == 'true' }}
        run: |
          repolens plan \
            --preset ${{ inputs.preset || 'opensource' }} \
            --format sarif \
            --output ${{ runner.temp }}/repolens-security.sarif

      - name: Upload SARIF to GitHub Security
        if: ${{ inputs.upload-sarif == true || inputs.upload-sarif == 'true' }}
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: ${{ runner.temp }}/repolens-security.sarif
          category: 'repolens'

# =============================================================================
# Example: Standalone Workflow
# =============================================================================
# To use this as a standalone workflow (not reusable), add these triggers:
#
# on:
#   push:
#     branches: [main, develop]
#   pull_request:
#     branches: [main]
#
# And remove the workflow_call trigger.

# =============================================================================
# Example: Calling This Workflow
# =============================================================================
# Create a workflow file that calls this reusable workflow:
#
# name: CI
# on: [push, pull_request]
#
# jobs:
#   lint:
#     runs-on: ubuntu-latest
#     steps:
#       - uses: actions/checkout@v4
#       - run: npm run lint
#
#   audit:
#     needs: lint
#     uses: ./.github/workflows/repolens.yml
#     with:
#       preset: 'opensource'
#       format: 'sarif'
#       upload-sarif: true
#
#   deploy:
#     needs: [lint, audit]
#     if: github.ref == 'refs/heads/main'
#     runs-on: ubuntu-latest
#     steps:
#       - run: echo "Deploying..."

# =============================================================================
# Example: Using the Official Action Instead
# =============================================================================
# If you prefer to use the official RepoLens action:
#
# jobs:
#   audit:
#     runs-on: ubuntu-latest
#     steps:
#       - uses: actions/checkout@v4
#       - uses: systm-d/repolens@main
#         with:
#           preset: 'opensource'
#           format: 'sarif'
#           fail-on: 'critical'