From 2c05ff8d34e1a7eda4dcf44a8229a928fd6d5e49 Mon Sep 17 00:00:00 2001 From: Tim Taplin Date: Mon, 5 Oct 2026 12:08:33 -0600 Subject: [PATCH 1/2] Declare iOS 15 and add CI GitHub Actions on every push and pull request: Linux (swift:6.4-noble, libsqlite3-dev) and macOS, each in debug and release with warnings as errors, plus the tests on an iOS Simulator. The iOS job can't use SWIFT_TREAT_WARNINGS_AS_ERRORS: Xcode builds Perfect-CRUD with warnings suppressed and the two flags conflict. The tests discarded transaction results without saying so; that was the only warning. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 80 +++++++++++++++++++ Package.swift | 2 +- README.md | 6 +- .../PerfectSQLiteTests.swift | 6 +- .../ReconcileTableTests.swift | 4 +- 5 files changed, 89 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..08f152d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,80 @@ +name: CI + +on: + push: + pull_request: + +jobs: + linux: + name: Linux (Swift 6.4, ${{ matrix.configuration }}) + runs-on: ubuntu-24.04 + container: swift:6.4-noble + strategy: + fail-fast: false + matrix: + configuration: [debug, release] + steps: + - uses: actions/checkout@v7 + + - name: Install SQLite + run: | + apt-get update + apt-get install -y --no-install-recommends pkg-config libsqlite3-dev + + - name: Toolchain + run: swift --version + + - name: Build (warnings are errors) + run: | + flags="-c ${{ matrix.configuration }} -Xswiftc -warnings-as-errors" + # @testable imports need testing enabled in an optimized build. + [ "${{ matrix.configuration }}" = release ] && flags="$flags -Xswiftc -enable-testing" + echo "FLAGS=$flags" >> "$GITHUB_ENV" + swift build --build-tests $flags + + - name: Test + run: swift test $FLAGS + + macos: + name: macOS (${{ matrix.configuration }}) + runs-on: macos-26 + strategy: + fail-fast: false + matrix: + configuration: [debug, release] + steps: + - uses: actions/checkout@v7 + + - name: Toolchain + run: swift --version + + - name: Build (warnings are errors) + run: | + flags="-c ${{ matrix.configuration }} -Xswiftc -warnings-as-errors" + # @testable imports need testing enabled in an optimized build. + [ "${{ matrix.configuration }}" = release ] && flags="$flags -Xswiftc -enable-testing" + echo "FLAGS=$flags" >> "$GITHUB_ENV" + swift build --build-tests $flags + + - name: Test + run: swift test $FLAGS + + ios: + name: iOS Simulator + runs-on: macos-26 + steps: + - uses: actions/checkout@v7 + + - name: Pick a simulator + run: | + xcodebuild -version + # Simulator names change with each Xcode, so take the first available iPhone. + udid=$(xcrun simctl list devices available -j \ + | jq -r '[.devices | to_entries[] | select(.key | contains("iOS")) | .value[] | select(.name | startswith("iPhone"))][0].udid') + [ -n "$udid" ] && [ "$udid" != null ] || { echo "No iPhone simulator available"; exit 1; } + echo "SIMULATOR=$udid" >> "$GITHUB_ENV" + + # No SWIFT_TREAT_WARNINGS_AS_ERRORS here: Xcode builds the Perfect-CRUD dependency with + # warnings suppressed, and the two flags conflict. The macOS and Linux jobs check warnings. + - name: Test + run: xcodebuild test -scheme PerfectSQLite -destination "platform=iOS Simulator,id=$SIMULATOR" diff --git a/Package.swift b/Package.swift index 93b1298..f52f24e 100644 --- a/Package.swift +++ b/Package.swift @@ -3,7 +3,7 @@ import PackageDescription let package = Package( name: "PerfectSQLite", - platforms: [.macOS(.v12)], + platforms: [.macOS(.v12), .iOS(.v15)], products: [ .library(name: "PerfectSQLite", targets: ["PerfectSQLite"]), ], diff --git a/README.md b/README.md index 11ce8aa..e0510b3 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Swift 6.2 - Platforms macOS 12+ | Linux + Platforms macOS 12+ | iOS 15+ | Linux License Apache @@ -14,7 +14,7 @@ This project provides a Swift wrapper around the SQLite 3 C library, plus a [Perfect-CRUD](https://github.com/PerfectlySoft/Perfect-CRUD) database driver built on top of it. -**Modernized for Swift 6.** Requires **swift-tools-version 6.2** and builds under full **Swift 6 language mode** (strict concurrency checking on for both the library and test targets). Supports **macOS 12+** (`platforms: [.macOS(.v12)]`) and **Linux** (tested with Swift 6.2.4, 6.3.2 and 6.4 on Ubuntu 24.04). iOS/tvOS/watchOS aren't declared or tested. +**Modernized for Swift 6.** Requires **swift-tools-version 6.2** and builds under full **Swift 6 language mode** (strict concurrency checking on for both the library and test targets). Supports **macOS 12+** and **iOS 15+** (`platforms: [.macOS(.v12), .iOS(.v15)]`) and **Linux** (tested with Swift 6.2.4, 6.3.2 and 6.4 on Ubuntu 24.04). CI runs the tests on macOS, Linux and the iOS Simulator. tvOS/watchOS/visionOS aren't declared or tested. The pre-Swift-6 version of this package is preserved on the [`legacy`](../../tree/legacy) branch. @@ -57,7 +57,7 @@ dependencies: [ and add `"PerfectSQLite"` to your target's `dependencies` array. You need the Swift 6.2 toolchain (or newer). -- **macOS:** SQLite ships with the macOS 12+ SDK; nothing else to install. If the build fails on `PerfectCSQLite` (`no such module 'PerfectCSQLite'` or `unable to resolve module dependency: 'PerfectCSQLite'`), the SDK's `SQLite3` module wasn't found: check the selected toolchain and SDK (`xcode-select -p`, `xcrun --show-sdk-path`). +- **macOS / iOS:** SQLite ships with the macOS 12+ and iOS 15+ SDKs; nothing else to install. If the build fails on `PerfectCSQLite` (`no such module 'PerfectCSQLite'` or `unable to resolve module dependency: 'PerfectCSQLite'`), the SDK's `SQLite3` module wasn't found: check the selected toolchain and SDK (`xcode-select -p`, `xcrun --show-sdk-path`). - **Linux:** install the SQLite development package (`sqlite-devel` on Fedora/RHEL), e.g. on Debian/Ubuntu: ```bash diff --git a/Tests/PerfectSQLiteTests/PerfectSQLiteTests.swift b/Tests/PerfectSQLiteTests/PerfectSQLiteTests.swift index 505888e..66edfa2 100644 --- a/Tests/PerfectSQLiteTests/PerfectSQLiteTests.swift +++ b/Tests/PerfectSQLiteTests/PerfectSQLiteTests.swift @@ -52,14 +52,14 @@ func getDB(reset: Bool = true) throws -> Database { func getTestDB() throws -> Database { let db = try getDB() try db.create(TestTable1.self, policy: .dropTable) - try db.transaction { + _ = try db.transaction { try db.table(TestTable1.self).insert((1...5).map { num -> TestTable1 in let n = UInt8(num) let blob: [UInt8]? = (num % 2 != 0) ? nil : [UInt8](arrayLiteral: n+1, n+2, n+3, n+4, n+5) return TestTable1(id: num, name: "This is name bind \(num)", integer: num, double: Double(num), blob: blob) }) } - try db.transaction { + _ = try db.transaction { try db.table(TestTable2.self).insert((1...5).flatMap { parentId -> [TestTable2] in (1...5).map { num -> TestTable2 in let n = UInt8(num) @@ -326,7 +326,7 @@ func getDB(reset: Bool = true) throws -> Database { @Test func selfJoin() throws { struct Me: Codable { let id: Int; let parentId: Int; let mes: [Me]?; init(id i: Int, parentId p: Int) { id = i; parentId = p; mes = nil } } let db = try getTestDB() - try db.transaction { + _ = try db.transaction { try db.create(Me.self, policy: .dropTable) .insert([Me(id: 1, parentId: 0), Me(id: 2, parentId: 1), Me(id: 3, parentId: 1), Me(id: 4, parentId: 1), Me(id: 5, parentId: 1)]) diff --git a/Tests/PerfectSQLiteTests/ReconcileTableTests.swift b/Tests/PerfectSQLiteTests/ReconcileTableTests.swift index 6b2647b..d33cda1 100644 --- a/Tests/PerfectSQLiteTests/ReconcileTableTests.swift +++ b/Tests/PerfectSQLiteTests/ReconcileTableTests.swift @@ -181,7 +181,7 @@ extension PerfectSQLiteTests { #expect(try columnNames(db, of: TestTable1.tableName).contains("int")) #expect(try db.table(TestTable2.self).count() == 25) // A table nothing references can still be rebuilt in one. - try db.transaction { + _ = try db.transaction { try db.create(ReducedTestTable2.self, policy: [.reconcileTable, .shallow]) } let childTable = TestTable2.CRUDTableName @@ -206,7 +206,7 @@ extension PerfectSQLiteTests { foreignKeysOn = stmt.columnInt(position: 0) } #expect(foreignKeysOn == 1) - try db.transaction { + _ = try db.transaction { try db.table(TestTable1.self).where(\TestTable1.id == 3).delete() } #expect(try db.table(TestTable2.self).count() == 20) From cb9c514b35c40318f755871a489cb43de4b50177 Mon Sep 17 00:00:00 2001 From: Tim Taplin Date: Mon, 5 Oct 2026 12:42:56 -0600 Subject: [PATCH 2/2] CI: oldest and newest toolchains, pinned simulator Linux runs Swift 6.2 and 6.4, macOS runs Xcode 26.0.1 and 26.6, each in debug and release. The iOS job pins Xcode 26.6 and an iPhone 17. The macOS Swift 6.2 release jobs and the iOS job need Perfect-CRUD's crud-3-release-prep on main first: CRUD main crashes in Swift 6.2 release builds, and declares no iOS (Xcode 26 then builds it for iOS 12 and fails). Checked against that branch with Xcode 26.3: 49/49 in debug, release and the iOS Simulator. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 40 ++++++++++++++++++++++++++-------------- README.md | 2 +- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 08f152d..3f7257b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,14 +4,20 @@ on: push: pull_request: +permissions: + contents: read + jobs: linux: - name: Linux (Swift 6.4, ${{ matrix.configuration }}) + name: Linux (Swift ${{ matrix.swift }}, ${{ matrix.configuration }}) runs-on: ubuntu-24.04 - container: swift:6.4-noble + container: swift:${{ matrix.swift }}-noble + timeout-minutes: 30 strategy: fail-fast: false matrix: + # The oldest supported Swift (swift-tools-version 6.2) and the newest. + swift: ["6.2", "6.4"] configuration: [debug, release] steps: - uses: actions/checkout@v7 @@ -36,17 +42,22 @@ jobs: run: swift test $FLAGS macos: - name: macOS (${{ matrix.configuration }}) + name: macOS (Xcode ${{ matrix.xcode }}, ${{ matrix.configuration }}) runs-on: macos-26 + timeout-minutes: 30 strategy: fail-fast: false matrix: + # The oldest Xcode with Swift 6.2, and the runner's default. + xcode: ["26.0.1", "26.6"] configuration: [debug, release] steps: - uses: actions/checkout@v7 - - name: Toolchain - run: swift --version + - name: Select Xcode + run: | + sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode }}.app + swift --version - name: Build (warnings are errors) run: | @@ -62,19 +73,20 @@ jobs: ios: name: iOS Simulator runs-on: macos-26 + timeout-minutes: 30 steps: - uses: actions/checkout@v7 - - name: Pick a simulator + - name: Select Xcode run: | + sudo xcode-select -s /Applications/Xcode_26.6.app xcodebuild -version - # Simulator names change with each Xcode, so take the first available iPhone. - udid=$(xcrun simctl list devices available -j \ - | jq -r '[.devices | to_entries[] | select(.key | contains("iOS")) | .value[] | select(.name | startswith("iPhone"))][0].udid') - [ -n "$udid" ] && [ "$udid" != null ] || { echo "No iPhone simulator available"; exit 1; } - echo "SIMULATOR=$udid" >> "$GITHUB_ENV" - # No SWIFT_TREAT_WARNINGS_AS_ERRORS here: Xcode builds the Perfect-CRUD dependency with - # warnings suppressed, and the two flags conflict. The macOS and Linux jobs check warnings. + # The test target builds at the package's iOS 15 deployment target, so this also + # catches APIs that need a newer iOS. No SWIFT_TREAT_WARNINGS_AS_ERRORS: Xcode builds the + # Perfect-CRUD dependency with warnings suppressed, and the two flags conflict. The macOS + # and Linux jobs check warnings. - name: Test - run: xcodebuild test -scheme PerfectSQLite -destination "platform=iOS Simulator,id=$SIMULATOR" + run: | + xcodebuild test -scheme PerfectSQLite \ + -destination 'platform=iOS Simulator,name=iPhone 17,OS=latest' diff --git a/README.md b/README.md index e0510b3..c32d5be 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ This project provides a Swift wrapper around the SQLite 3 C library, plus a [Perfect-CRUD](https://github.com/PerfectlySoft/Perfect-CRUD) database driver built on top of it. -**Modernized for Swift 6.** Requires **swift-tools-version 6.2** and builds under full **Swift 6 language mode** (strict concurrency checking on for both the library and test targets). Supports **macOS 12+** and **iOS 15+** (`platforms: [.macOS(.v12), .iOS(.v15)]`) and **Linux** (tested with Swift 6.2.4, 6.3.2 and 6.4 on Ubuntu 24.04). CI runs the tests on macOS, Linux and the iOS Simulator. tvOS/watchOS/visionOS aren't declared or tested. +**Modernized for Swift 6.** Requires **swift-tools-version 6.2** and builds under full **Swift 6 language mode** (strict concurrency checking on for both the library and test targets). Supports **macOS 12+** and **iOS 15+** (`platforms: [.macOS(.v12), .iOS(.v15)]`) and **Linux** (tested with Swift 6.2.4, 6.3.2 and 6.4 on Ubuntu 24.04). CI runs the tests on Linux (Swift 6.2 and 6.4), macOS (Xcode 26.0.1 and 26.6) and the iOS Simulator. tvOS/watchOS/visionOS aren't declared or tested. The pre-Swift-6 version of this package is preserved on the [`legacy`](../../tree/legacy) branch.