diff --git a/.github/workflows/syndikit.yml b/.github/workflows/syndikit.yml index fb5fe17..27e8e4b 100644 --- a/.github/workflows/syndikit.yml +++ b/.github/workflows/syndikit.yml @@ -1,92 +1,123 @@ name: SyndiKit on: push: - branches-ignore: - - '*WIP' - + branches: + - main + - 'v[0-9]+.[0-9]+.[0-9]+' + paths-ignore: + - '**.md' + - 'docs/**' + - 'LICENSE' + - '.github/ISSUE_TEMPLATE/**' + pull_request: + paths-ignore: + - '**.md' + - 'docs/**' + - 'LICENSE' + - '.github/ISSUE_TEMPLATE/**' + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.ref_name }} + cancel-in-progress: true + +env: + PACKAGE_NAME: SyndiKit + jobs: + configure: + name: Configure Matrix + runs-on: ubuntu-latest + outputs: + full-matrix: ${{ steps.matrix.outputs.full-matrix }} + ubuntu-os: ${{ steps.matrix.outputs.ubuntu-os }} + ubuntu-swift: ${{ steps.matrix.outputs.ubuntu-swift }} + ubuntu-type: ${{ steps.matrix.outputs.ubuntu-type }} + steps: + - id: check + name: Determine matrix scope + run: | + FULL=false + REF="${{ github.ref }}" + EVENT="${{ github.event_name }}" + BASE_REF="${{ github.base_ref }}" + if [[ "$REF" == "refs/heads/main" ]]; then + FULL=true + elif [[ "$REF" =~ ^refs/heads/v?[0-9]+\.[0-9]+\.[0-9]+ ]]; then + FULL=true + elif [[ "$EVENT" == "workflow_dispatch" ]]; then + FULL=true + elif [[ "$EVENT" == "pull_request" ]]; then + if [[ "$BASE_REF" == "main" || "$BASE_REF" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+ ]]; then + FULL=true + fi + fi + echo "full=$FULL" >> "$GITHUB_OUTPUT" + - id: matrix + name: Build matrix values + run: | + if [[ "${{ steps.check.outputs.full }}" == "true" ]]; then + echo 'full-matrix=true' >> "$GITHUB_OUTPUT" + echo 'ubuntu-os=["noble","jammy"]' >> "$GITHUB_OUTPUT" + echo 'ubuntu-swift=[{"version":"5.10"},{"version":"6.0"},{"version":"6.1"},{"version":"6.2"},{"version":"6.3"}]' >> "$GITHUB_OUTPUT" + echo 'ubuntu-type=["","wasm","wasm-embedded"]' >> "$GITHUB_OUTPUT" + else + echo 'full-matrix=false' >> "$GITHUB_OUTPUT" + echo 'ubuntu-os=["noble"]' >> "$GITHUB_OUTPUT" + echo 'ubuntu-swift=[{"version":"6.3"}]' >> "$GITHUB_OUTPUT" + echo 'ubuntu-type=[""]' >> "$GITHUB_OUTPUT" + fi + build-ubuntu: name: Build on Ubuntu - env: - PACKAGE_NAME: SyndiKit + needs: configure runs-on: ubuntu-latest - container: ${{ matrix.swift.nightly && format('swiftlang/swift:nightly-{0}-noble', matrix.swift.version) || format('swift:{0}', matrix.swift.version) }} - if: ${{ !contains(github.event.head_commit.message, 'ci skip') }} + container: swift:${{ matrix.swift.version }}-${{ matrix.os }} strategy: matrix: - os: [noble, jammy] - swift: - - version: "5.10" - - version: "6.0" - - version: "6.1" - - version: "6.2" - - version: "6.3" + os: ${{ fromJSON(needs.configure.outputs.ubuntu-os) }} + swift: ${{ fromJSON(needs.configure.outputs.ubuntu-swift) }} + type: ${{ fromJSON(needs.configure.outputs.ubuntu-type) }} + exclude: + - swift: {version: "5.10"} + type: wasm + - swift: {version: "5.10"} + type: wasm-embedded + - swift: {version: "6.0"} + type: wasm + - swift: {version: "6.0"} + type: wasm-embedded + - swift: {version: "6.1"} + type: wasm + - swift: {version: "6.1"} + type: wasm-embedded steps: - uses: actions/checkout@v6 - - uses: brightdigit/swift-build@v1 - id: build + - id: build + uses: brightdigit/swift-build@v1 with: - scheme: ${{ env.PACKAGE_NAME }} + type: ${{ matrix.type }} skip-package-resolved: ${{ contains(matrix.swift.version, '5.') }} - name: Install curl if: steps.build.outputs.contains-code-coverage == 'true' run: | apt-get update -q - apt-get install -y curl - - uses: sersoft-gmbh/swift-coverage-action@v5 - id: coverage-files - with: - fail-on-empty-output: true - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v6 - with: - fail_ci_if_error: true - flags: ubuntu,ubuntu-${{ matrix.os }},swift-${{ matrix.swift.version }}${{ matrix.swift.nightly && '-nightly' || '' }} - verbose: true - token: ${{ secrets.CODECOV_TOKEN }} - files: ${{ join(fromJSON(steps.coverage-files.outputs.files), ',') }} - build-wasm: - name: Build on WASM - env: - PACKAGE_NAME: SyndiKit - runs-on: ubuntu-latest - container: ${{ matrix.swift.nightly && format('swiftlang/swift:nightly-{0}-{1}', matrix.swift.version, matrix.os) || format('swift:{0}-{1}', matrix.swift.version, matrix.os) }} - if: ${{ !contains(github.event.head_commit.message, 'ci skip') }} - strategy: - fail-fast: false - matrix: - os: [noble, jammy] - type: ["wasm", "wasm-embedded"] - swift: - - version: ["6.2", "6.3"] - steps: - - uses: actions/checkout@v6 - - uses: brightdigit/swift-build@v1 - id: build - with: - scheme: ${{ env.PACKAGE_NAME }} - skip-package-resolved: ${{ contains(matrix.swift.version, '5.') }} - type: ${{ matrix.type }} - - uses: sersoft-gmbh/swift-coverage-action@v5 + apt-get install -y curl + - name: Process Coverage if: steps.build.outputs.contains-code-coverage == 'true' - id: coverage-files + uses: sersoft-gmbh/swift-coverage-action@v5 with: fail-on-empty-output: true - name: Upload coverage to Codecov if: steps.build.outputs.contains-code-coverage == 'true' uses: codecov/codecov-action@v6 with: - fail_ci_if_error: true - flags: ubuntu,ubuntu-${{ matrix.os }},swift-${{ matrix.swift.version }}${{ matrix.swift.nightly && '-nightly' || '' }},${{ matrix.type }} - verbose: true token: ${{ secrets.CODECOV_TOKEN }} - files: ${{ join(fromJSON(steps.coverage-files.outputs.files), ',') }} + flags: spm,${{ matrix.os }},${{ matrix.swift.version }} + build-macos: name: Build on macOS - env: - PACKAGE_NAME: SyndiKit runs-on: ${{ matrix.runs-on }} - if: ${{ !contains(github.event.head_commit.message, 'ci skip') }} strategy: fail-fast: false matrix: @@ -103,17 +134,17 @@ jobs: - runs-on: macos-26 xcode: "/Applications/Xcode_26.4.app" - # SPM Build Matrix - Xcode 15.4 + # macOS Build Matrix - Xcode 15.4 - type: macos runs-on: macos-14 xcode: "/Applications/Xcode_15.4.app" - # SPM Build Matrix - Xcode 16.4 + # macOS Build Matrix - Xcode 16.4 - type: macos runs-on: macos-15 xcode: "/Applications/Xcode_16.4.app" - # SPM Build Matrix - Xcode 26.4 + # macOS Build Matrix - Xcode 26.4 - type: macos runs-on: macos-26 xcode: "/Applications/Xcode_26.4.app" @@ -129,15 +160,15 @@ jobs: - type: ios runs-on: macos-15 xcode: "/Applications/Xcode_16.4.app" - deviceName: "iPhone 16 Pro" - osVersion: "18.6" - + deviceName: "iPhone 16" + osVersion: "18.5" + # iOS Build Matrix - Xcode 26.4 - type: ios runs-on: macos-26 xcode: "/Applications/Xcode_26.4.app" - deviceName: "iPhone 17 Pro" - osVersion: "26.4" + deviceName: "iPhone 17" + osVersion: "26.4" download-platform: true # watchOS Build Matrix - Xcode 15.4 @@ -159,22 +190,22 @@ jobs: runs-on: macos-26 xcode: "/Applications/Xcode_26.4.app" deviceName: "Apple Watch Ultra 3 (49mm)" - osVersion: "26.4" + osVersion: "26.4" download-platform: true # tvOS Build Matrix - Xcode 16.4 - type: tvos runs-on: macos-15 xcode: "/Applications/Xcode_16.4.app" - deviceName: "Apple TV" + deviceName: "Apple TV 4K (3rd generation)" osVersion: "18.5" # tvOS Build Matrix - Xcode 26.4 - type: tvos runs-on: macos-26 xcode: "/Applications/Xcode_26.4.app" - deviceName: "Apple TV" - osVersion: "26.4" + deviceName: "Apple TV 4K (3rd generation)" + osVersion: "26.4" download-platform: true # visionOS Build Matrix - Xcode 16.4 @@ -182,14 +213,14 @@ jobs: runs-on: macos-15 xcode: "/Applications/Xcode_16.4.app" deviceName: "Apple Vision Pro" - osVersion: "2.4" + osVersion: "2.5" # visionOS Build Matrix - Xcode 26.4 - type: visionos runs-on: macos-26 xcode: "/Applications/Xcode_26.4.app" deviceName: "Apple Vision Pro" - osVersion: "26.4" + osVersion: "26.4" download-platform: true steps: @@ -199,7 +230,8 @@ jobs: if: ${{ contains(matrix.xcode, 'Xcode_15.4') && matrix.type != '' }} run: defaults write com.apple.dt.Xcode IDESkipMacroFingerprintValidation -bool YES - - name: Build and Test + - id: build + name: Build and Test uses: brightdigit/swift-build@v1 with: scheme: ${{ env.PACKAGE_NAME }} @@ -207,126 +239,62 @@ jobs: xcode: ${{ matrix.xcode }} deviceName: ${{ matrix.deviceName }} osVersion: ${{ matrix.osVersion }} + download-platform: ${{ matrix.download-platform }} build-only: ${{ contains(matrix.xcode, 'Xcode_15.4') && matrix.type != '' }} skip-package-resolved: ${{ contains(matrix.xcode, 'Xcode_15.4') }} - # Common Coverage Steps - name: Process Coverage + if: ${{ steps.build.outputs.contains-code-coverage == 'true' }} uses: sersoft-gmbh/swift-coverage-action@v5 - - - name: Set coverage flags - id: coverage-flags - run: | - # Select Xcode version - sudo xcode-select -s "${{ matrix.xcode }}" - - # Extract Xcode version from path like /Applications/Xcode_16.4.app - XCODE_VERSION=$(echo "${{ matrix.xcode }}" | sed -n 's/.*Xcode_\([0-9.]*\).*/\1/p') - - # Extract Swift version - SWIFT_VERSION=$(xcrun swift --version | head -n 1 | sed -n 's/.*Swift version \([0-9.]*\).*/\1/p') - - # Extract macOS version from runs-on - MACOS_VERSION=$(echo "${{ matrix.runs-on }}" | sed 's/macos-//') - - # Determine platform - PLATFORM="${{ matrix.type || 'macos' }}" - - # Build flags - if [ -n "${{ matrix.osVersion }}" ]; then - # Platform build (iOS, tvOS, etc.) - FLAGS="${PLATFORM},${PLATFORM}-${{ matrix.osVersion }},swift-${SWIFT_VERSION},xcode-${XCODE_VERSION}" - else - # SPM/macOS build - FLAGS="macos,macos-${MACOS_VERSION},swift-${SWIFT_VERSION},xcode-${XCODE_VERSION}" - fi - - echo "flags=${FLAGS}" >> $GITHUB_OUTPUT + with: + fail-on-empty-output: true + search-paths: | + ./.build + /Users/runner/work/_temp/DerivedData - name: Upload Coverage + if: ${{ steps.build.outputs.contains-code-coverage == 'true' }} uses: codecov/codecov-action@v6 with: token: ${{ secrets.CODECOV_TOKEN }} - flags: ${{ steps.coverage-flags.outputs.flags }} + flags: ${{ matrix.type && format('{0}{1}', matrix.type, matrix.osVersion) || 'spm' }} build-windows: name: Build on Windows - env: - PACKAGE_NAME: SyndiKit + needs: [configure] + if: needs.configure.outputs.full-matrix == 'true' runs-on: ${{ matrix.runs-on }} - if: ${{ !contains(github.event.head_commit.message, 'ci skip') }} strategy: fail-fast: false matrix: - include: - - runs-on: windows-2022 - swift: - version: swift-6.1-release - build: 6.1-RELEASE - - - runs-on: windows-2022 - swift: - version: swift-6.2-release - build: 6.2-RELEASE - - - runs-on: windows-2022 - swift: - version: swift-6.3-release - build: 6.3-RELEASE - - - runs-on: windows-2025 - swift: - version: swift-6.1-release - build: 6.1-RELEASE - - - runs-on: windows-2025 - swift: - version: swift-6.2-release - build: 6.2-RELEASE - - - runs-on: windows-2025 - swift: - version: swift-6.3-release - build: 6.3-RELEASE + runs-on: [windows-2022, windows-2025] + swift: + - version: swift-6.1-release + build: 6.1-RELEASE + - version: swift-6.2-release + build: 6.2-RELEASE + - version: swift-6.3-release + build: 6.3-RELEASE steps: - uses: actions/checkout@v6 - - - name: Build and Test - uses: brightdigit/swift-build@v1 + - uses: brightdigit/swift-build@v1 with: - scheme: ${{ env.PACKAGE_NAME }} windows-swift-version: ${{ matrix.swift.version }} windows-swift-build: ${{ matrix.swift.build }} - - name: Set coverage flags - id: coverage-flags - shell: bash - run: | - # Extract version from swift-6.1-release format - VERSION=$(echo "${{ matrix.swift.version }}" | sed 's/swift-\([0-9.]*\)-.*/\1/') - OS_VERSION=$(echo "${{ matrix.runs-on }}" | sed 's/windows-//') - echo "flags=windows,windows-${OS_VERSION},swift-${VERSION}" >> $GITHUB_OUTPUT - - - name: Upload Coverage - uses: codecov/codecov-action@v6 - with: - token: ${{ secrets.CODECOV_TOKEN }} - flags: ${{ steps.coverage-flags.outputs.flags }} - build-android: name: Build on Android - env: - PACKAGE_NAME: SyndiKit + needs: [configure] + if: needs.configure.outputs.full-matrix == 'true' runs-on: ubuntu-latest - if: ${{ !contains(github.event.head_commit.message, 'ci skip') }} strategy: fail-fast: false matrix: - swift-version: ["6.1", "6.2", "6.3"] - # Testing API 21+ (64-bit architectures) to avoid zlib linking issues on 32-bit ARM - # See: https://github.com/android/ndk/issues/1391 - api-level: [28, 33, 34] - + swift: + - version: "6.1" + - version: "6.2" + - version: "6.3" + android-api-level: [28, 33, 36] steps: - uses: actions/checkout@v6 @@ -339,46 +307,36 @@ jobs: sudo apt-get clean sudo docker image prune --all --force - - name: Build and Test - uses: brightdigit/swift-build@v1 - id: build + - uses: brightdigit/swift-build@v1 with: - scheme: ${{ env.PACKAGE_NAME }} type: android - android-swift-version: ${{ matrix.swift-version }} - android-api-level: ${{ matrix.api-level }} + android-swift-version: ${{ matrix.swift.version }} + android-api-level: ${{ matrix.android-api-level }} android-run-tests: true android-copy-files: Data/ - - name: Upload Coverage - uses: codecov/codecov-action@v6 - if: steps.build.outputs.contains-code-coverage == 'true' - with: - token: ${{ secrets.CODECOV_TOKEN }} - flags: android,android-api-${{ matrix.api-level }},swift-${{ matrix.swift-version }} - lint: name: Linting - if: ${{ !contains(github.event.head_commit.message, 'ci skip') }} + if: ${{ always() && !cancelled() && !failure() }} runs-on: ubuntu-latest - needs: [build-ubuntu, build-wasm, build-macos, build-windows, build-android] + needs: [build-ubuntu, build-macos, build-windows, build-android] env: MINT_PATH: .mint/lib MINT_LINK_PATH: .mint/bin steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v6 - name: Cache mint id: cache-mint - uses: actions/cache@v4 + uses: actions/cache@v4 env: cache-name: cache with: path: | .mint - Mint + Mint key: ${{ runner.os }}-mint-${{ hashFiles('**/Mintfile') }} restore-keys: | - ${{ runner.os }}-mint- + ${{ runner.os }}-mint- - name: Install mint if: steps.cache-mint.outputs.cache-hit == '' run: | @@ -391,7 +349,6 @@ jobs: swift-source-compat-suite: name: Test Swift 6.x For Source Compatibility Suite runs-on: ubuntu-latest - if: ${{ !contains(github.event.head_commit.message, 'ci skip') }} strategy: matrix: container: diff --git a/Package@swift-6.0.swift b/Package@swift-6.0.swift index 12ebfd1..d037d3f 100644 --- a/Package@swift-6.0.swift +++ b/Package@swift-6.0.swift @@ -39,7 +39,8 @@ let package = Package( .target( name: "SyndiKit", dependencies: ["XMLCoder"], - swiftSettings: swift6Features + swiftSettings: swift6Features, + linkerSettings: [.linkedLibrary("z", .when(platforms: [.android]))] ), .target(