Skip to content

refactor: include what these two files use, and reverse by range #647

refactor: include what these two files use, and reverse by range

refactor: include what these two files use, and reverse by range #647

Workflow file for this run

name: apple
on:
push:
# branches only: publishing a release pushes a tag, which is not a change
branches: ['**']
release:
types: [published]
workflow_dispatch:
# `release.yml` builds the artifact it has to checksum. The version comes in
# so the framework identifies itself by the tag rather than by a commit that
# does not exist yet; the checksum goes back out for `Package.swift`.
workflow_call:
inputs:
version:
description: tag being cut (e.g. v6.2.0)
type: string
required: true
outputs:
checksum:
value: ${{ jobs.xcframework.outputs.checksum }}
# Keyed on the event too, so a dispatch is not cancelled by an unrelated push.
# A build `release.yml` is waiting on is never cancelled at all.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
cancel-in-progress: ${{ !inputs.version }}
# See the cache-key comment in `build_test.yml` for why the keys look like this.
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_MAXSIZE: 2G
CCACHE_KEY_SUFFIX: r1
CONAN_HOME: ${{ github.workspace }}/.conan2
CONAN_KEY_SUFFIX: r1
XCODE_VERSION: '26.0'
jobs:
# macOS runners bill at 10x. A push off main builds the two arm64 slices and
# stops there; main, a dispatch and a release build all five, assemble and run
# the suites — a dispatch being how a branch exercises the full matrix,
# including the simulator suite, before it is merged.
matrix:
runs-on: ubuntu-24.04
outputs:
profiles: ${{ steps.pick.outputs.profiles }}
full: ${{ steps.pick.outputs.full }}
steps:
- id: pick
run: |
if [ "${{ github.event_name }}" = push ] && [ "${{ github.ref }}" != refs/heads/main ] && [ -z "${{ inputs.version }}" ]; then
echo 'profiles=["apple-ios-armv8","apple-macos-armv8"]' >> "$GITHUB_OUTPUT"
echo 'full=false' >> "$GITHUB_OUTPUT"
else
echo 'profiles=["apple-ios-armv8","apple-iossim-armv8","apple-iossim-x86_64","apple-macos-armv8","apple-macos-x86_64"]' >> "$GITHUB_OUTPUT"
echo 'full=true' >> "$GITHUB_OUTPUT"
fi
framework:
needs: matrix
# A published release has nothing to build: `release` already produced and
# uploaded the artifact, and rebuilding it would only produce a second one
# nobody uses. `verify` is what runs then.
if: github.event_name != 'release'
runs-on: macos-15
env:
CACHE_FLAVOR: apple
strategy:
fail-fast: false
matrix:
profile: ${{ fromJSON(needs.matrix.outputs.profiles) }}
steps:
- name: checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
# `create-xcframework` behaviour and the default deployment targets move
# between Xcode versions, so the runner default is not good enough.
- name: select xcode
uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0
with:
xcode-version: ${{ env.XCODE_VERSION }}
- name: install ccache
run: |
brew install ccache
ccache -V
- name: setup python 3.14
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with:
python-version: 3.14
- name: install python dependencies
run: pip install conan
- name: conan cache key
shell: bash
run: echo "CONAN_CACHE_KEY=${{ hashFiles('conanfile.py', '.github/config/conan/**') }}" >> "$GITHUB_ENV"
- name: cache conan
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: ${{ env.CONAN_HOME }}
key: conan-${{ env.CACHE_FLAVOR }}-${{ matrix.profile }}-${{ env.CONAN_KEY_SUFFIX }}-${{ env.CONAN_CACHE_KEY }}
restore-keys: |
conan-${{ env.CACHE_FLAVOR }}-${{ matrix.profile }}-${{ env.CONAN_KEY_SUFFIX }}-
- name: conan config
run: conan config install .github/config/conan
- name: restore ccache
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: ${{ env.CCACHE_DIR }}
key: ccache-${{ env.CACHE_FLAVOR }}-${{ matrix.profile }}-${{ env.CCACHE_KEY_SUFFIX }}-${{ github.run_id }}
restore-keys: |
ccache-${{ env.CACHE_FLAVOR }}-${{ matrix.profile }}-${{ env.CCACHE_KEY_SUFFIX }}-
# Identify by the tag, not by the commit: the commit that carries the
# checksum does not exist yet, and would change the binary if it did.
- name: release version
if: inputs.version
run: echo "ODR_GIT_HEAD=${{ inputs.version }}" >> "$GITHUB_ENV"
- name: build
run: python apple/build_xcframework.py slice --profile ${{ matrix.profile }}
- name: save ccache
if: github.ref == 'refs/heads/main'
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: ${{ env.CCACHE_DIR }}
key: ccache-${{ env.CACHE_FLAVOR }}-${{ matrix.profile }}-${{ env.CCACHE_KEY_SUFFIX }}-${{ github.run_id }}
# The artifact is named after the profile because `download-artifact`
# unpacks each one into a directory named after the *artifact*, and
# `assemble` looks for `apple/build/<profile>`. Anything else lands in
# `apple/build/<artifact-name>/` and the assembly cannot find its slices.
- name: upload slice
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: ${{ matrix.profile }}
path: apple/build/${{ matrix.profile }}
if-no-files-found: error
xcframework:
needs: [matrix, framework]
if: needs.matrix.outputs.full == 'true'
runs-on: macos-15
outputs:
checksum: ${{ steps.checksum.outputs.checksum }}
steps:
- name: checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: select xcode
uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0
with:
xcode-version: ${{ env.XCODE_VERSION }}
# -> apple/build/<profile>/, which is where `assemble` looks
- name: download slices
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
pattern: apple-*
path: apple/build
# `assemble` asserts each slice's platform tag, install name, bundle
# contents and plist keys before merging them.
- name: assemble
run: python apple/build_xcframework.py assemble
- name: checksum
id: checksum
run: |
CHECKSUM=$(swift package compute-checksum OdrCoreObjC.xcframework.zip)
echo "$CHECKSUM"
echo "checksum=$CHECKSUM" >> "$GITHUB_OUTPUT"
# The archive alone — the checksum travels as a workflow output.
- name: upload xcframework
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: OdrCoreObjC.xcframework
path: OdrCoreObjC.xcframework.zip
if-no-files-found: error
# `release.yml` attaches every `release-asset-*` artifact of the run
- name: upload as a release asset
if: inputs.version
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: release-asset-xcframework
path: OdrCoreObjC.xcframework.zip
if-no-files-found: error
# The iOS *device* slice is only ever link checked; nothing runs it. The
# simulator run is the analogue of android's instrumented job and the only one
# that sees what a device sees — that the framework loads, that rendering
# works unconfigured, that a temp directory is writable in an app container.
test:
needs: xcframework
runs-on: macos-15
strategy:
fail-fast: false
matrix:
include:
- { name: macos, destination: 'platform=macOS,arch=arm64' }
- { name: simulator, destination: 'platform=iOS Simulator,name=iPhone 16' }
steps:
- name: checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: select xcode
uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0
with:
xcode-version: ${{ env.XCODE_VERSION }}
- name: download xcframework
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: OdrCoreObjC.xcframework
- name: unpack
run: ditto -x -k OdrCoreObjC.xcframework.zip .
# `swift test` cannot target a simulator; `xcodebuild` on a package can.
- name: test
env:
ODR_XCFRAMEWORK: OdrCoreObjC.xcframework
run: >
xcodebuild test
-scheme OdrCore
-destination '${{ matrix.destination }}'
-skipPackagePluginValidation
# The only check that would catch a submodule creeping back into the package
# repo, which is invisible from inside it: SwiftPM initialises submodules on
# every consumer's checkout, and this repo used to carry 4.5 GB of them.
resolve:
runs-on: macos-15
steps:
- name: checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: resolve from a cold cache
env:
ODR_XCFRAMEWORK: OdrCoreObjC.xcframework
run: |
rm -rf ~/Library/Caches/org.swift.swiftpm
swift package dump-package > /dev/null
echo "manifest evaluates; submodules: $(git config --file .gitmodules --get-regexp path | wc -l)"
# A release cut by hand would leave the tag pointing at a `Package.swift`
# whose url and checksum belong to the previous version, and SwiftPM would
# serve that stale binary without complaint. Make it loud.
verify:
if: github.event_name == 'release'
runs-on: macos-15
steps:
- name: checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: the tag, the url and the checksum must agree
run: |
URL=$(grep -o 'https://[^"]*OdrCoreObjC.xcframework.zip' Package.swift)
DECLARED=$(grep -o 'checksum: "[0-9a-f]\{64\}"' Package.swift | cut -d'"' -f2)
case "$URL" in
*/download/${GITHUB_REF_NAME}/*) ;;
*) echo "Package.swift points at $URL, but the tag is ${GITHUB_REF_NAME}" >&2; exit 1 ;;
esac
curl -fsSL "$URL" -o artifact.zip
ACTUAL=$(swift package compute-checksum artifact.zip)
if [ "$ACTUAL" != "$DECLARED" ]; then
echo "checksum mismatch: declared $DECLARED, asset is $ACTUAL" >&2
exit 1
fi
echo "tag, url and checksum agree"