ci #45
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: ci | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| schedule: | |
| - cron: '0 4 * * *' | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ci-${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} | |
| jobs: | |
| build-examples: | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| swift_version: ['6.3', 'nightly-6.3', 'nightly-main'] | |
| ndk_version: ['r27d', 'r29', 'r30-beta1'] | |
| os: ['ubuntu-latest', 'macos-latest'] | |
| configuration: ['Debug', 'Release'] | |
| runs-on: ${{ matrix.os }} | |
| env: | |
| NDK_VERSION: ${{ matrix.ndk_version }} | |
| SWIFT_VERSION: ${{ matrix.swift_version }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Set up JDK 25 | |
| uses: actions/setup-java@v5 | |
| with: | |
| distribution: temurin | |
| java-version: '25' | |
| - name: Install swiftly | |
| run: | | |
| set -euxo pipefail | |
| if [[ "${RUNNER_OS}" == "Linux" ]]; then | |
| sudo apt-get update | |
| sudo apt-get -yq install curl jq gpg unzip libcurl4-openssl-dev | |
| ARCH="$(uname -m)" | |
| curl -L -O --retry 3 "https://download.swift.org/swiftly/linux/swiftly-${ARCH}.tar.gz" | |
| tar -xzf "swiftly-${ARCH}.tar.gz" | |
| ./swiftly init \ | |
| --assume-yes \ | |
| --skip-install \ | |
| --no-modify-profile \ | |
| --quiet-shell-followup | |
| rm -f "swiftly-${ARCH}.tar.gz" swiftly | |
| # The example projects' Gradle scripts look for swiftly under | |
| # $HOME/.local/share/swiftly/bin, which is also where the official | |
| # installer puts it. Add it to PATH for subsequent steps. | |
| echo "$HOME/.local/share/swiftly/bin" >> "$GITHUB_PATH" | |
| "$HOME/.local/share/swiftly/bin/swiftly" --version | |
| elif [[ "${RUNNER_OS}" == "macOS" ]]; then | |
| curl -O https://download.swift.org/swiftly/darwin/swiftly.pkg | |
| installer -pkg swiftly.pkg -target CurrentUserHomeDirectory | |
| ~/.swiftly/bin/swiftly init --quiet-shell-followup | |
| . "${SWIFTLY_HOME_DIR:-$HOME/.swiftly}/env.sh" | |
| hash -r | |
| echo "$HOME/.swiftly/bin" >> "$GITHUB_PATH" | |
| "$HOME/.swiftly/bin/swiftly" --version | |
| else | |
| echo "Unknown OS: ${RUNNER_OS}" | |
| exit 1 | |
| fi | |
| - name: Cache Android NDK | |
| id: cache-ndk | |
| uses: actions/cache@v5 | |
| with: | |
| key: android-ndk-${{ runner.os }}-${{ runner.arch }}-${{ matrix.ndk_version }} | |
| path: ~/android-ndk-${{ matrix.ndk_version }} | |
| - name: Install Android NDK | |
| if: steps.cache-ndk.outputs.cache-hit != 'true' | |
| run: | | |
| set -euxo pipefail | |
| OS="$(uname -s | tr '[A-Z]' '[a-z]')" | |
| curl -L -o ndk.zip --retry 3 "https://dl.google.com/android/repository/android-ndk-${NDK_VERSION}-${OS}.zip" | |
| unzip -q ndk.zip -d "$HOME" | |
| rm ndk.zip | |
| - name: Set ANDROID_NDK_HOME | |
| run: echo "ANDROID_NDK_HOME=$HOME/android-ndk-${NDK_VERSION}" >> "$GITHUB_ENV" | |
| - name: Resolve Android Swift SDK download URL | |
| id: resolve-sdk | |
| # Look up the Android Swift SDK URL and checksum from the swift.org | |
| # install API. | |
| run: | | |
| set -euxo pipefail | |
| case "$SWIFT_VERSION" in | |
| nightly-*) | |
| nightly_version="${SWIFT_VERSION#nightly-}" | |
| sdk_json=$(curl -fsSL "https://www.swift.org/api/v1/install/dev/${nightly_version}/android-sdk.json") | |
| snapshot_tag=$(echo "$sdk_json" | jq -r '.[0].dir') | |
| sdk_checksum=$(echo "$sdk_json" | jq -r '.[0].checksum') | |
| if [ "$nightly_version" = "main" ]; then | |
| branch="development" | |
| else | |
| branch="swift-${nightly_version}-branch" | |
| fi | |
| sdk_url="https://download.swift.org/${branch}/android-sdk/${snapshot_tag}/${snapshot_tag}_android.artifactbundle.tar.gz" | |
| ;; | |
| *) | |
| releases_json=$(curl -fsSL "https://www.swift.org/api/v1/install/releases.json") | |
| # Pick the highest patch release whose name starts with the | |
| # requested version (e.g. "6.3" -> "6.3.1" if it exists). | |
| latest_version=$(echo "$releases_json" | jq -r --arg v "$SWIFT_VERSION" \ | |
| '[.[] | select(.name | startswith($v))] | |
| | sort_by(.name | split(".") | map(tonumber? // 0)) | |
| | last | |
| | .name') | |
| if [ -z "$latest_version" ] || [ "$latest_version" = "null" ]; then | |
| echo "Error: no Swift release matching '$SWIFT_VERSION' found in releases.json" >&2 | |
| exit 1 | |
| fi | |
| sdk_checksum=$(echo "$releases_json" | jq -r --arg v "$latest_version" \ | |
| '.[] | select(.name == $v) | .platforms[] | select(.platform == "android-sdk") | .checksum') | |
| snapshot_tag="swift-${latest_version}-RELEASE" | |
| sdk_url="https://download.swift.org/swift-${latest_version}-release/android-sdk/${snapshot_tag}/${snapshot_tag}_android.artifactbundle.tar.gz" | |
| ;; | |
| esac | |
| swift_install=${snapshot_tag} | |
| # trim leading "swift-" and trailing "-RELEASE" | |
| swift_install=${swift_install#swift-} | |
| swift_install=${swift_install/-RELEASE/} | |
| sdk_filename="${snapshot_tag}_android.artifactbundle.tar.gz" | |
| echo "snapshot_tag=${snapshot_tag}" >> "$GITHUB_OUTPUT" | |
| echo "sdk_url=${sdk_url}" >> "$GITHUB_OUTPUT" | |
| echo "sdk_checksum=${sdk_checksum}" >> "$GITHUB_OUTPUT" | |
| echo "swift_install=${swift_install}" >> "$GITHUB_OUTPUT" | |
| echo "sdk_filename=${sdk_filename}" >> "$GITHUB_OUTPUT" | |
| # Override the matrix-supplied SWIFT_VERSION (e.g. "6.3") with the | |
| # resolved patch version (e.g. "6.3.1") so the gradle scripts pick up | |
| # the actual artifactbundle directory name produced by `swift sdk | |
| # install`. SWIFT_ANDROID_SDK_VERSION pins the bundle suffix for the | |
| # same reason. | |
| echo "SWIFT_VERSION=${swift_install}" >> "$GITHUB_ENV" | |
| echo "SWIFT_ANDROID_SDK_VERSION=${snapshot_tag#swift-}_android" >> "$GITHUB_ENV" | |
| - name: Cache Android Swift SDK artifact | |
| id: cache-swift-sdk | |
| uses: actions/cache@v5 | |
| with: | |
| key: ${{ steps.resolve-sdk.outputs.sdk_filename }} | |
| path: ~/swift-sdk-cache/${{ steps.resolve-sdk.outputs.sdk_filename }} | |
| - name: Download Android Swift SDK artifact | |
| if: steps.cache-swift-sdk.outputs.cache-hit != 'true' | |
| run: | | |
| set -euxo pipefail | |
| mkdir -p "$HOME/swift-sdk-cache" | |
| curl -L -o "$HOME/swift-sdk-cache/${{ steps.resolve-sdk.outputs.sdk_filename }}" --retry 3 "${{ steps.resolve-sdk.outputs.sdk_url }}" | |
| echo "${{ steps.resolve-sdk.outputs.sdk_checksum }} $HOME/swift-sdk-cache/${{ steps.resolve-sdk.outputs.sdk_filename }}" | shasum -a 256 -c - | |
| - name: Cache Host Toolchain | |
| # Disabled because we are overflowing the 10GB GitHub cache limit with this | |
| # https://github.com/swiftlang/swift-android-examples/pull/44#discussion_r3236006987 | |
| if: false | |
| id: cache-swiftly-toolchain | |
| uses: actions/cache@v5 | |
| with: | |
| key: swiftly-${{ runner.os }}-${{ runner.arch }}-${{ steps.resolve-sdk.outputs.swift_install }} | |
| path: | | |
| ~/Library/Developer/Toolchains | |
| ~/.local/share/swiftly | |
| ~/.local/bin/swiftly | |
| - name: Install host toolchain and Android Swift SDK | |
| run: | | |
| set -euxo pipefail | |
| echo "Installing Android Swift SDK and host toolchain" | |
| echo " tag: ${{ steps.resolve-sdk.outputs.swift_install }}" | |
| echo " file: $HOME/swift-sdk-cache/${{ steps.resolve-sdk.outputs.sdk_filename }}" | |
| echo " checksum: ${{ steps.resolve-sdk.outputs.sdk_checksum }}" | |
| swiftly install "${{ steps.resolve-sdk.outputs.swift_install }}" | |
| swift sdk install "$HOME/swift-sdk-cache/${{ steps.resolve-sdk.outputs.sdk_filename }}" | |
| swift sdk list | |
| - name: Configure Swift Android SDK | |
| run: | | |
| set -euo pipefail | |
| # Locate the installed Android SDK artifactbundle. Its parent | |
| # directory varies by OS / swiftpm version, so try the known | |
| # candidates and pick the first one that actually matches. | |
| shopt -s nullglob | |
| candidates=( | |
| "$HOME"/.swiftpm/swift-sdks/*android*.artifactbundle | |
| "$HOME"/.config/swiftpm/swift-sdks/*android*.artifactbundle | |
| "$HOME"/Library/org.swift.swiftpm/swift-sdks/*android*.artifactbundle | |
| ) | |
| if [[ ${#candidates[@]} -eq 0 ]]; then | |
| echo "No android SDK artifactbundle found in any known location" >&2 | |
| exit 1 | |
| fi | |
| cd "${candidates[0]}" | |
| # Link the SDK against the NDK we installed in the previous step. | |
| # Someday we might not need this script, so gracefully skip it | |
| # if it does not exist. | |
| if [[ -x "./swift-android/scripts/setup-android-sdk.sh" ]]; then | |
| "./swift-android/scripts/setup-android-sdk.sh" | |
| fi | |
| - name: Publish swift-java packages to local Maven | |
| # The hashing-lib, weather-lib, and hello-cpp-swift/swift-lib modules | |
| # depend on org.swift.swiftkit:swiftkit-core:1.0-SNAPSHOT, which is not | |
| # published to a public Maven repo. The hashing-lib README documents | |
| # publishing it to mavenLocal from the swift-java checkout that | |
| # SwiftPM resolves into .build/checkouts/swift-java. | |
| working-directory: hello-swift-java/hashing-lib | |
| run: | | |
| set -euxo pipefail | |
| swift package resolve | |
| ./.build/checkouts/swift-java/gradlew \ | |
| --project-dir .build/checkouts/swift-java \ | |
| :SwiftKitCore:publishToMavenLocal | |
| - name: Build hello-swift-raw-jni APK | |
| run: ./gradlew :hello-swift-raw-jni:assemble${{ matrix.configuration }} --stacktrace | |
| - name: Build hello-swift-raw-jni-callback APK | |
| run: ./gradlew :hello-swift-raw-jni-callback:assemble${{ matrix.configuration }} --stacktrace | |
| - name: Build hello-swift-raw-jni-library | |
| run: ./gradlew :hello-swift-raw-jni-library:assemble${{ matrix.configuration }} --stacktrace | |
| - name: Build native-activity APK | |
| run: ./gradlew :native-activity:assemble${{ matrix.configuration }} --stacktrace | |
| - name: Build hello-swift-java APK | |
| run: ./gradlew :hello-swift-java-hashing-app:assemble${{ matrix.configuration }} --stacktrace | |
| - name: Build swift-java-weather-app APK | |
| run: ./gradlew :swift-java-weather-app-weather-app:assemble${{ matrix.configuration }} --stacktrace | |
| - name: Build hello-cpp-swift cpp-lib | |
| working-directory: hello-cpp-swift/cpp-lib | |
| run: ./build-android-static.sh | |
| - name: Build hello-cpp-swift APK | |
| run: ./gradlew :hello-cpp-swift:app:assemble${{ matrix.configuration }} --stacktrace | |
| - name: Summarize APK artifacts | |
| if: always() | |
| run: | | |
| echo "## APK Artifacts (os:${{ matrix.os }} / swift:${{ steps.resolve-sdk.outputs.swift_install }} / ndk:${{ matrix.ndk_version }} / configuration:${{ matrix.configuration }})" >> "$GITHUB_STEP_SUMMARY" | |
| echo "" >> "$GITHUB_STEP_SUMMARY" | |
| echo "| Project | APK | Size |" >> "$GITHUB_STEP_SUMMARY" | |
| echo "|---------|-----|------|" >> "$GITHUB_STEP_SUMMARY" | |
| found=0 | |
| while IFS= read -r apk; do | |
| found=1 | |
| # Derive a human-readable project name from the path | |
| project=$(echo "$apk" | sed -E 's#^\./##; s#/build/outputs/.*##') | |
| name=$(basename "$apk") | |
| # Human-readable size (du -h works on both Linux and macOS) | |
| size=$(du -h "$apk" | cut -f1 | tr -d '[:space:]') | |
| echo "| \`$project\` | \`$name\` | $size |" >> "$GITHUB_STEP_SUMMARY" | |
| done < <(find . -path '*/build/outputs/apk/*.apk' -type f | sort) | |
| if [ "$found" -eq 0 ]; then | |
| echo "| _(none)_ | — | — |" >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| - name: Upload APK artifacts | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: apks-${{ matrix.os }}-${{ steps.resolve-sdk.outputs.swift_install }}-${{ matrix.ndk_version }}-${{ matrix.configuration }} | |
| path: '**/build/outputs/apk/**/*.apk' | |
| if-no-files-found: warn |