name: container project - common jobs

permissions:
  contents: read

on:
  workflow_call:
    inputs:
      release:
        type: boolean
        description: "Publish this build for release"
        default: false
      coverage:
        type: boolean
        description: "Run tests with code coverage enabled"
        default: false
      pr_number:
        type: string
        description: "PR number for coverage artifact naming (required when coverage is true)"
        default: ""

jobs:
  buildAndTest:
    name: Build and test the project
    if: github.repository == 'apple/container'
    timeout-minutes: 90
    runs-on: [self-hosted, macos, tahoe, ARM64]
    permissions:
      contents: read
      packages: read
    env:
      DEVELOPER_DIR: "/Applications/Xcode_swift_6.3.app/Contents/Developer"
    steps:
      - name: Checkout repository
        uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          fetch-depth: 0

      - name: Check formatting
        env:
          HAWKEYE_AUTO_INSTALL: "1"
        run: |
          ./scripts/install-hawkeye.sh
          make fmt

          if ! git diff --quiet -- . ; then
            echo "❌ The following files require formatting or license header updates:"
            git diff --name-only -- .
            false
          fi

      - name: Check protobuf
        run: |
          make protos
          if ! git diff --quiet -- . ; then
            echo "❌ The following files require formatting or license header updates:"
            git diff --name-only -- .
            false
          fi

      - name: Set build configuration
        env:
          RELEASE: ${{ inputs.release }}
        run: |
          echo "BUILD_CONFIGURATION=debug" >> $GITHUB_ENV
          if [[ "${RELEASE}" == "true" ]]; then
            echo "BUILD_CONFIGURATION=release" >> $GITHUB_ENV
          fi

      - name: Make the container project and docs
        run: |
          make container dsym docs
          tar cfz _site.tgz _site

      - name: Validate coverage inputs
        if: ${{ inputs.coverage }}
        env:
          PR_NUMBER: ${{ inputs.pr_number }}
        run: |
          if [ -z "${PR_NUMBER}" ]; then
            echo "::error::pr_number input is required when coverage is true"
            exit 1
          fi

      - name: Create package
        run: |
          mkdir -p outputs
          mv "bin/${BUILD_CONFIGURATION}/container-installer-unsigned.pkg" outputs
          mv "bin/${BUILD_CONFIGURATION}/bundle/container-dSYM.zip" outputs

      - name: Set up test environment
        run: |
          APP_ROOT=$(mktemp -d -p "${RUNNER_TEMP}")
          LOG_ROOT="${APP_ROOT}/logs"
          echo "created data directory: ${APP_ROOT}"
          echo "hostname: $(hostname)"
          echo "hw.physicalcpu: $(sysctl -n hw.physicalcpu)"
          echo "hw.logicalcpu: $(sysctl -n hw.logicalcpu)"
          export NO_PROXY="${NO_PROXY},192.168.0.0/16,fe80::/10"
          echo NO_PROXY=${NO_PROXY}
          export no_proxy="${no_proxy},192.168.0.0/16,fe80::/10"
          echo no_proxy=${no_proxy}
          echo "APP_ROOT=${APP_ROOT}" >> $GITHUB_ENV
          echo "LOG_ROOT=${LOG_ROOT}" >> $GITHUB_ENV

      - name: Test the container project
        if: ${{ !inputs.coverage }}
        run: make APP_ROOT="${APP_ROOT}" LOG_ROOT="${LOG_ROOT}" test install-kernel integration

      - name: Test the container project with coverage
        if: ${{ inputs.coverage }}
        run: make APP_ROOT="${APP_ROOT}" LOG_ROOT="${LOG_ROOT}" install-kernel coverage

      - name: Extract coverage percentages
        if: ${{ inputs.coverage }}
        env:
          PR_NUMBER: ${{ inputs.pr_number }}
        run: |
          mkdir -p pr-coverage
          jq -r '.data[0].totals.lines.percent | . * 100 | round | . / 100' coverage-reports/unit/coverage-summary.json > pr-coverage/unit-line-coverage.txt
          jq -r '.data[0].totals.lines.percent | . * 100 | round | . / 100' coverage-reports/integration/coverage-summary.json > pr-coverage/integration-line-coverage.txt
          jq -r '.data[0].totals.lines.percent | . * 100 | round | . / 100' coverage-reports/combined/coverage-summary.json > pr-coverage/combined-line-coverage.txt
          echo "${PR_NUMBER}" > pr-coverage/pr-number.txt
          echo "Coverage data:"
          cat pr-coverage/*.txt

      - name: Upload coverage data
        if: ${{ inputs.coverage }}
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: pr-coverage-${{ inputs.pr_number }}
          path: pr-coverage/
          retention-days: 1
          if-no-files-found: warn

      - name: Upload coverage HTML reports
        if: ${{ inputs.coverage }}
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: coverage-html-reports
          path: |
            coverage-reports/unit/html/
            coverage-reports/integration/html/
            coverage-reports/combined/html/
          retention-days: 14
          if-no-files-found: ignore

      - name: Archive test logs
        if: always()
        run: |
          if [ -d "${APP_ROOT}/containers" ] && [ -n "${LOG_ROOT}" ]; then
            rsync -a --include='*/' --include='*.log' --exclude='*' \
              "${APP_ROOT}/containers/" \
              "${LOG_ROOT}/containers/"
          fi
          if [ -n "${LOG_ROOT}" ] && [ -d "${LOG_ROOT}" ] ; then
            echo "Collecting logs from ${LOG_ROOT}..."
            tar czf container-logs.tar.gz -C "$(dirname "${LOG_ROOT}")" "$(basename "${LOG_ROOT}")"
            echo "Log archive created: container-logs.tar.gz"
          fi
          if [ -n "${APP_ROOT}" ] ; then
            rm -rf "${APP_ROOT}"
            echo "Removed data directory ${APP_ROOT}"
          fi
          rm -rf coverage-reports pr-coverage

      - name: Upload logs if present
        if: always()
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: container-test-logs
          path: container-logs.tar.gz
          retention-days: 14
          if-no-files-found: ignore

      - name: Save documentation artifact
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: api-docs
          path: "./_site.tgz"
          retention-days: 14

      - name: Save package artifacts
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: container-package
          path: ${{ github.workspace }}/outputs

  uploadPages:
    # Separate upload step required because upload-pages-artifact needs
    # gtar which is not on the macOS runner.
    name: Upload artifact for GitHub Pages
    needs: buildAndTest
    timeout-minutes: 5
    runs-on: ubuntu-latest
    steps:
      - name: Setup Pages
        uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6

      - name: Download a single artifact
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: api-docs

      - name: Add API docs to documentation
        run: |
          tar xfz _site.tgz

      - name: Upload Artifact
        uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5
        with:
          path: "./_site"
