refactor: include what these two files use, and reverse by range #647
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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" |