diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ed08032..86f707d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,7 +1,8 @@ -# CuboKit CI: the test run (tools/test.sh = `swift test -c release`) and, on a tag, the CHANGELOG version check. +# CuboKit CI: the test run (tools/test.sh = `swift test -c release`) on every pull request; after merge the consumer check, +# the README consumer and an iOS Simulator build; on a tag, the CHANGELOG version check. # -# Actions must be enabled on the repository. The package needs macOS 15 or later and a Swift 6.2 or later toolchain -# (Package.swift: `.macOS(.v15)`, tools 6.2); `macos-latest` with its newest Xcode carries both. +# The package needs macOS 15 / iOS 18 or later and a Swift 6.2 or later toolchain (Package.swift: tools 6.2); the +# macos-26 image with Xcode 26.6, pinned below, carries both. # No real data in CI: RealDataTests skip by name when ~/Library/Caches/CuboKit/real-data is absent, so tools/test.sh is # green without it. No libhdf5 and no References/ (CuboKit has no package dependencies). # Never `swift build --build-tests -c release` (fails on this toolchain's Swift Build backend); tools/test.sh is the gate. @@ -18,20 +19,38 @@ concurrency: group: ci-${{ github.ref }} cancel-in-progress: true +env: + # Pinned like the apps: the macos-26 image's Xcode 26.6 (Swift 6.3, macOS 26 SDK). Bump on purpose. + DEVELOPER_DIR: /Applications/Xcode_26.6.app/Contents/Developer + jobs: test: - runs-on: macos-latest + runs-on: macos-26 timeout-minutes: 90 steps: - uses: actions/checkout@v7 - - name: Select the newest Xcode + - name: Toolchain on record run: | - xcode=$(ls -d /Applications/Xcode*.app | sort -V | tail -1) - sudo xcode-select -s "$xcode" + xcode-select -p swift --version - name: Gate (tools/test.sh, release) run: tools/test.sh + post-merge: + name: Consumer checks and iOS build (after merge) + # Not on pull requests: the free plan's macOS runners are scarce, so the extra builds run on main, tags and by hand. + if: github.event_name != 'pull_request' + runs-on: macos-26 + timeout-minutes: 90 + steps: + - uses: actions/checkout@v7 + - name: Consumer check (a separate package, the public API only) + run: tools/consumer-check/run.sh + - name: README consumer (a separate package compiling exactly the README's program, output checked) + run: tools/readme-consumer/run.sh + - name: Build the libraries for iOS (Simulator, not run) + run: xcodebuild -scheme CuboKit-Package -destination 'generic/platform=iOS Simulator' -quiet build + version: name: CHANGELOG version matches the tag runs-on: ubuntu-latest @@ -59,7 +78,7 @@ jobs: name: Lint (shared SwiftLint and SwiftFormat config) runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: SwiftLint run: docker run --rm -v "$PWD:/work" -w /work --entrypoint swiftlint ghcr.io/realm/swiftlint:0.65.1 lint --strict --quiet - name: SwiftFormat diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 7cba055..b5433fb 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -23,16 +23,19 @@ concurrency: group: docs-${{ github.ref }} cancel-in-progress: true +env: + # Pinned like the apps: the macos-26 image's Xcode 26.6 (Swift 6.3, macOS 26 SDK). Bump on purpose. + DEVELOPER_DIR: /Applications/Xcode_26.6.app/Contents/Developer + jobs: build: - runs-on: macos-latest + runs-on: macos-26 timeout-minutes: 60 steps: - uses: actions/checkout@v7 - - name: Select the newest Xcode + - name: Toolchain on record run: | - xcode=$(ls -d /Applications/Xcode*.app | sort -V | tail -1) - sudo xcode-select -s "$xcode" + xcode-select -p xcodebuild -version - name: Build the site run: tools/build-docs.sh .docs-site /CuboKit diff --git a/Tests/CuboVirtualTests/TruthTests.swift b/Tests/CuboVirtualTests/TruthTests.swift index 8349d08..68307a1 100644 --- a/Tests/CuboVirtualTests/TruthTests.swift +++ b/Tests/CuboVirtualTests/TruthTests.swift @@ -117,7 +117,7 @@ import CuboVirtual for (row, column) in [(1, 5), (3, 2), (4, 0), (0, 6), (2, 4)] { for path in paths { let image = try await VirtualImaging.image(of: source, geometry: .point(row: row, column: column), path: path) - #expect(image.values == (0..<6).map { Double($0 * 10000 + row * 100 + column) }, "(\(row), \(column)) \(path)") + #expect(image.values == (0..<6).map { (i: Int) -> Double in Double(i * 10000 + row * 100 + column) }, "(\(row), \(column)) \(path)") } } } diff --git a/tools/consumer-check/Sources/consumer-check/main.swift b/tools/consumer-check/Sources/consumer-check/main.swift index b39a0f1..5bcecba 100644 --- a/tools/consumer-check/Sources/consumer-check/main.swift +++ b/tools/consumer-check/Sources/consumer-check/main.swift @@ -475,7 +475,7 @@ check("CuboVirtualError.invalidInput", CuboVirtualError.invalidInput("x") == .in // HotPixelFilter.Replacer: the reusable in-place replacement plan (equals the one-frame `replacing`). do { let hpMask = (0..<20).map { $0 % 7 == 3 } - let hpFrame = (0..<20).map { Float(($0 * 37) % 11) + (($0 == 3) ? 50 : 0) } + let hpFrame: [Float] = (0..<20).map { (i: Int) -> Float in Float((i * 37) % 11) + (i == 3 ? 50 : 0) } var hpPlanned = hpFrame let replacer = try HotPixelFilter.Replacer(rows: 4, columns: 5, mask: hpMask) replacer.apply(to: &hpPlanned) @@ -659,7 +659,9 @@ do { let probe = try PtychographyArray(rows: d, columns: d, real: probeRe, imaginary: [Double](repeating: 0, count: d * d)) let object = try PtychographyArray(rows: 24, columns: 24, real: [Double](repeating: 1, count: 576), imaginary: [Double](repeating: 0, count: 576)) let amps = [Double](repeating: 1, count: count * d * d) - let positions = (0.. PtychographyPosition in + PtychographyPosition(row: Double(8 + 3 * i), column: Double(8 + 2 * i)) + } nonisolated(unsafe) var ptychoProgress: [Int] = [] let single = try SingleslicePtychography.reconstruct(amplitudes: amps, positions: positions, object: object, probe: probe, iterations: 2, precision: .single, progress: { done, _ in ptychoProgress.append(done) })