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: 75 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@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 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)" 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"