diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..28f32471 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,82 @@ +name: CI + +# Compiles the app and its test target on every pull request and push to main, so a PR that +# doesn't build never reaches a reviewer. The build is for a generic iOS device with code +# signing off: that proves the code compiles against the SDK without needing certificates, +# and it matches how the app is actually run (on a phone; the simulator can't do Bluetooth +# or on-device MLX). +# +# Standard GitHub-hosted macOS runners are free for public repositories. Keep this on the +# plain `macos-*` labels — "larger" runners are billed even for public repos. + +on: + pull_request: + push: + branches: [main] + +concurrency: + group: ci-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +env: + # Pin the Xcode on the image so a runner image update can't change the SDK underneath a PR. + # Bump deliberately (and check the pinned mlx-swift-lm still compiles) when moving to a new + # Xcode. Xcode 27 is not on GitHub's images yet; see actions/runner-images. + DEVELOPER_DIR: /Applications/Xcode_26.6.app/Contents/Developer + +jobs: + build: + name: Build app + tests (iOS device, unsigned) + runs-on: macos-26 + timeout-minutes: 90 + steps: + - uses: actions/checkout@v4 + + - name: Provide the gitignored config files + # Same two copies the README asks a new contributor to make. The project refuses to + # build without them; the examples' placeholder values are enough to compile, and the + # real values only matter at runtime on a device. + run: | + cp Config.xcconfig.example Config.xcconfig + cp OpenVision/Config/Config.swift.example OpenVision/Config/Config.swift + + - name: Cache Swift packages + # Package.resolved is gitignored (whole xcworkspace), so key on the files that hold + # the pins. The MLX, Kokoro and Meta DAT checkouts are the slow part of a cold build. + uses: actions/cache@v4 + with: + path: .spm + key: spm-${{ runner.os }}-${{ hashFiles('OpenVision.xcodeproj/project.pbxproj', 'project.yml') }} + restore-keys: spm-${{ runner.os }}- + + - name: Show toolchain + run: | + xcodebuild -version + xcrun --sdk iphoneos --show-sdk-version + + - name: Build for testing + # mlx-swift ships a build plugin (CudaBuild) and mlx-swift-lm uses Swift macros; Xcode + # refuses both until a human approves them, which is what the two -skip flags do here. + run: | + set -o pipefail + xcodebuild build-for-testing \ + -project OpenVision.xcodeproj \ + -scheme OpenVision \ + -destination 'generic/platform=iOS' \ + -clonedSourcePackagesDirPath .spm \ + -derivedDataPath DerivedData \ + -skipPackagePluginValidation \ + -skipMacroValidation \ + CODE_SIGNING_ALLOWED=NO \ + CODE_SIGNING_REQUIRED=NO \ + CODE_SIGN_IDENTITY= \ + DEVELOPMENT_TEAM= \ + | xcbeautify --renderer github-actions + + - name: Upload build log on failure + if: failure() + uses: actions/upload-artifact@v4 + with: + name: xcodebuild-logs + path: DerivedData/Logs/Build/*.xcactivitylog + if-no-files-found: ignore diff --git a/README.md b/README.md index 9a05dc73..fbd56f7a 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ [![Swift 5](https://img.shields.io/badge/Swift-5-orange.svg)](https://swift.org) [![iOS 18+](https://img.shields.io/badge/iOS-18+-blue.svg)](https://developer.apple.com/ios/) [![Latest release](https://img.shields.io/github/v/release/rayl15/OpenVision?color=2ea44f)](https://github.com/rayl15/OpenVision/releases) +[![CI](https://github.com/rayl15/OpenVision/actions/workflows/ci.yml/badge.svg)](https://github.com/rayl15/OpenVision/actions/workflows/ci.yml) [![Stars](https://img.shields.io/github/stars/rayl15/OpenVision?logo=github)](https://github.com/rayl15/OpenVision/stargazers) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/rayl15/OpenVision/blob/main/CONTRIBUTING.md) [![good first issues](https://img.shields.io/github/issues/rayl15/OpenVision/good%20first%20issue?color=7057ff&label=good%20first%20issues)](https://github.com/rayl15/OpenVision/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)