# CI/CD Pipeline for apple/container: GitHub Actions Workflow Explained

> Explore the CI/CD pipeline for apple/container, powered by GitHub Actions. Learn how workflow files and self-hosted macOS runners orchestrate builds, tests, and releases.

- Repository: [Apple/container](https://github.com/apple/container)
- Tags: how-to-guide
- Published: 2026-06-30

---

**The apple/container project uses a GitHub Actions-based CI/CD pipeline where three workflow files orchestrate builds, tests, and releases by delegating to a shared [`common.yml`](https://github.com/apple/container/blob/main/common.yml) configuration executed on self-hosted macOS runners.**

The `apple/container` repository automates its software delivery through a comprehensive CI/CD pipeline defined in the `.github/workflows/` directory. This pipeline validates every contribution through automated checks and produces signed release artifacts for Apple's containerization framework. All workflow jobs execute on self-hosted macOS runners with Apple Silicon architecture to ensure native ARM64 compatibility.

## Workflow Architecture Overview

The CI/CD pipeline employs a modular design pattern that separates event triggers from implementation logic. Three entry-point workflows—[`pr-build.yml`](https://github.com/apple/container/blob/main/pr-build.yml), [`merge-build.yml`](https://github.com/apple/container/blob/main/merge-build.yml), and [`release.yml`](https://github.com/apple/container/blob/main/release.yml)—handle specific Git events while delegating execution to [`.github/workflows/common.yml`](https://github.com/apple/container/blob/main/.github/workflows/common.yml). This architecture eliminates duplication and ensures consistent build behavior across different trigger types.

## The Three Entry-Point Workflows

### PR Build (pr-build.yml)

Located at [`.github/workflows/pr-build.yml`](https://github.com/apple/container/blob/main/.github/workflows/pr-build.yml), this workflow triggers on `pull_request` events (opened, reopened, synchronize). It executes two sequential jobs: first, a signature verification step that validates all commits are cryptographically signed, followed by the shared build and test workflow. The workflow passes `release: false` and `coverage: true` to the common configuration.

```yaml
name: container project - PR build
on:
  pull_request:
    types: [opened, reopened, synchronize]

jobs:
  verify-signatures:
    runs-on: ubuntu-latest
    steps:
      - name: Check all commits are signed
        env:
          GH_TOKEN: ${{ github.token }}
          REPO: ${{ github.repository }}
          PR_NUMBER: ${{ github.event.pull_request.number }}
        run: |
          commits=$(gh api "repos/${REPO}/pulls/${PR_NUMBER}/commits" --paginate)
          # ... loop that fails on unsigned commits ...

  build:
    uses: ./.github/workflows/common.yml
    with:
      release: false
      coverage: true
      pr_number: ${{ github.event.pull_request.number }}

```

### Merge Build (merge-build.yml)

The [`.github/workflows/merge-build.yml`](https://github.com/apple/container/blob/main/.github/workflows/merge-build.yml) workflow activates on `push` events to the `main` branch or any `release/*` branch. It invokes [`common.yml`](https://github.com/apple/container/blob/main/common.yml) with `release: true` to ensure merged code produces releasable artifacts and passes all integration tests.

### Release Build (release.yml)

Triggered exclusively by version tags matching the `vX.Y.Z` pattern, [`.github/workflows/release.yml`](https://github.com/apple/container/blob/main/.github/workflows/release.yml) coordinates production releases. After executing the shared build logic with release configuration, it uses the `softprops/action-gh-release` action to create a draft GitHub release marked as prerelease, attaching the built `.pkg` and `.zip` artifacts.

## Shared Build Logic in common.yml

The pipeline's core implementation resides in [`.github/workflows/common.yml`](https://github.com/apple/container/blob/main/.github/workflows/common.yml), which executes on `runs-on: [self-hosted, macos, tahoe, ARM64]`. This workflow accepts boolean inputs including `release`, `coverage`, and `pr_number` to customize behavior.

The shared workflow executes the following stages:

1. **Checkout** - Retrieves full repository history using `actions/checkout@v7` with `fetch-depth: 0`
2. **Formatting Verification** - Executes `make fmt` after installing the Hawkeye formatter via [`scripts/install-hawkeye.sh`](https://github.com/apple/container/blob/main/scripts/install-hawkeye.sh), failing if uncommitted changes exist
3. **Protobuf Validation** - Runs `make protos` to ensure generated code is synchronized with source definitions
4. **Configuration** - Sets the `BUILD_CONFIGURATION` environment variable to `debug` or `release` based on the `inputs.release` value
5. **Compilation** - Executes `make container dsym docs` to build the binary, debug symbols, and documentation, then archives the site as `_site.tgz`
6. **Testing** - Runs `make test install-kernel integration integration-new` for standard validation, or `make coverage-new` when coverage analysis is requested
7. **Coverage Processing** - Extracts line-coverage percentages and archives HTML reports when enabled
8. **Artifact Packaging** - Bundles `container-installer-unsigned.pkg` and dSYM archives for distribution
9. **Log Collection** - Gathers container logs from temporary test directories and uploads them for debugging

```yaml
jobs:
  buildAndTest:
    runs-on: [self-hosted, macos, tahoe, ARM64]
    steps:
      - name: Checkout repository
        uses: actions/checkout@v7
        with:
          fetch-depth: 0

      - name: Check formatting
        run: |
          ./scripts/install-hawkeye.sh
          make fmt
          if ! git diff --quiet -- . ; then
            echo "❌ Formatting required"
            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

```

## Signature Verification and Security

Before compilation begins, the PR workflow enforces cryptographic signing requirements through a dedicated verification job. A Bash script queries the GitHub API using the `gh` CLI to enumerate commits in the pull request, inspecting the `verification.verified` field for each entry. If any commit lacks a valid signature, the script exits with an error, preventing unsigned code from entering the build pipeline.

## Release Publishing Process

When a maintainer pushes a semantic version tag, the release workflow automatically prepares distribution assets. The process generates a draft release containing the `container-installer-unsigned.pkg` installer and compressed archives, allowing maintainers to review release notes and assets before publication. The draft status ensures automated tooling does not immediately publicize potentially incomplete releases.

## Summary

- The pipeline uses three entry-point workflows ([`pr-build.yml`](https://github.com/apple/container/blob/main/pr-build.yml), [`merge-build.yml`](https://github.com/apple/container/blob/main/merge-build.yml), [`release.yml`](https://github.com/apple/container/blob/main/release.yml)) that delegate to a shared [`.github/workflows/common.yml`](https://github.com/apple/container/blob/main/.github/workflows/common.yml)
- All builds execute on self-hosted macOS ARM64 runners (`[self-hosted, macos, tahoe, ARM64]`) for native Apple Silicon compatibility
- Mandatory commit signature verification prevents unsigned code from merging via the GitHub API verification check
- The `Makefile` drives build tasks including `make fmt`, `make protos`, `make container`, and `make test`
- Automated drafting of GitHub releases occurs when version tags are pushed, producing `.pkg` and `.zip` artifacts

## Frequently Asked Questions

### What triggers the CI/CD pipeline in apple/container?

The pipeline triggers on three distinct events: pull request activity (opened, reopened, or synchronized), pushes to `main` or `release/*` branches, and pushes of version tags matching semantic versioning patterns. Each event type routes to a specific workflow file optimized for that validation scenario.

### Why does apple/container use self-hosted runners instead of GitHub-hosted runners?

The workflow specifies `runs-on: [self-hosted, macos, tahoe, ARM64]` to ensure builds execute on dedicated Apple Silicon hardware with specific kernel extension capabilities and environment configurations required for testing container functionality on macOS.

### How does the pipeline enforce commit signing requirements?

The PR build workflow executes a Bash script that queries the GitHub API for all commits in the pull request, inspecting the `verification.verified` field. If any commit lacks a valid cryptographic signature, the workflow fails immediately before reaching build or test stages.

### What build configurations are supported?

The pipeline supports `debug` and `release` configurations controlled by the `release` input parameter passed to [`common.yml`](https://github.com/apple/container/blob/main/common.yml). Debug builds execute during standard PR validation, while release builds produce optimized binaries, installer packages, and draft GitHub releases.