Skip to content

Commit

Permalink
Add Basic Windows & Linux Support (#184)
Browse files Browse the repository at this point in the history
* Seems to compile and sort of run tests

* Fix a few more issues

* Fix naming and unneeded import

* Get CI a little further

* Use platform specific snapshots

* More snapshots

* Try again with non-Darwin

* Turns out we just need a darwin and non-darwin snapshots dir

* Fix header look up for client creation

* update ci configuration

* Skip problematic test on Windows and Linux

* Use a supported version of Swift for the linux CI

* Go back to specific folders for each platform, it's just easier to deal with

* Attempt to disable conversion and skip broken test

* Cleaning up the PR a little

* Add missing test files

* Update tests

* Address some PR feedback

* Recomment out test

* Drop down to see if using a Semaphore works across platforms

* Allow threading in a local storage client

* Remove defaults from designated initializer and put defaults in platform specific convenience inits

* Give linux a convenience init so tests can use it

* Remove default local storage

* Fix mock generation

* Remove old tests

* Remove autocanonicalization problems by making Apikey consistent casing.

* Re-record all snapshots to prevent auto-canonicalization issues across toolchains

* Turn on autocrlf to see if that gets the test snapshots further

* fix line endings for snapshots

* fix initializer

* Remove unneeded testing workaround

* Update tests to use empty response where needed

* Fix empty implementation to work across platforms

* Fix test that breaks to only run if not Windows or Linux

* use correct operator

* Fix binary packaging

* Use URLs and remove convenience

* Fix cancellation

* fix example

* Fix actuallyCancel logic
  • Loading branch information
brianmichel authored Jan 11, 2024
1 parent 3b97d39 commit 37b68d2
Show file tree
Hide file tree
Showing 56 changed files with 534 additions and 149 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/Tests/**/__Snapshots__/**/*.txt eol=lf
34 changes: 30 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ concurrency:
cancel-in-progress: true

jobs:
library:
library-darwin:
runs-on: macos-13
name: Test Library
name: Test Library (Darwin)
steps:
- uses: actions/checkout@v3
- name: Select Xcode 15.0.1
Expand All @@ -25,7 +25,7 @@ jobs:
run: make test-library

library-evolution:
name: Library (evolution)
name: Library (evolution, Darwin)
runs-on: macos-13
steps:
- uses: actions/checkout@v4
Expand All @@ -34,6 +34,33 @@ jobs:
- name: Build for library evolution
run: make build-for-library-evolution

library-linux:
runs-on: ubuntu-latest
name: Test Library (Linux)
steps:
- uses: swift-actions/setup-swift@v1
with:
swift-version: "5.9"
- uses: actions/checkout@v3
- name: Run Tests
run: swift test

library-windows:
runs-on: windows-latest
name: Test Library (Windows)
steps:
# We use BCNY's repo since they have newer builds of Swift
# which have fixed libcurl in Foundation.
- uses: compnerd/gha-setup-swift@main
with:
release-tag-name: "20231203.0"
github-repo: "thebrowsercompany/swift-build"
release-asset-name: installer-amd64.exe
github-token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/checkout@v3
- name: Run Tests
run: swift test

examples:
runs-on: macos-13
name: Build Examples
Expand All @@ -45,4 +72,3 @@ jobs:
run: cp Examples/Examples/_Secrets.swift Examples/Examples/Secrets.swift
- name: Build examples
run: make build-examples

12 changes: 12 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"cSpell.words": [
"apikey",
"HTTPURL",
"pkce",
"postgrest",
"preconcurrency",
"Supabase",
"whitespaces",
"xctest"
]
}
3 changes: 2 additions & 1 deletion Examples/Examples/ExamplesApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ struct ExamplesApp: App {

let supabase = SupabaseClient(
supabaseURL: Secrets.supabaseURL,
supabaseKey: Secrets.supabaseAnonKey
supabaseKey: Secrets.supabaseAnonKey,
options: .init(auth: .init(storage: KeychainLocalStorage(service: "supabase.gotrue.swift", accessGroup: nil)))
)
5 changes: 3 additions & 2 deletions Examples/UserManagement/Supabase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Foundation
import Supabase

let supabase = SupabaseClient(
supabaseURL: "https://PROJECT_ID.supabase.co",
supabaseKey: "YOUR_SUPABASE_ANON_KEY"
supabaseURL: URL(string: "https://PROJECT_ID.supabase.co")!,
supabaseKey: "YOUR_SUPABASE_ANON_KEY",
options: .init(auth: .init(storage: KeychainLocalStorage(service: "supabase.gotrue.swift", accessGroup: nil)))
)
18 changes: 9 additions & 9 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
{
"pins" : [
{
"identity" : "keychainaccess",
"identity" : "swift-concurrency-extras",
"kind" : "remoteSourceControl",
"location" : "https://github.com/kishikawakatsumi/KeychainAccess",
"location" : "https://github.com/pointfreeco/swift-concurrency-extras",
"state" : {
"revision" : "84e546727d66f1adc5439debad16270d0fdd04e7",
"version" : "4.2.2"
"revision" : "bb5059bde9022d69ac516803f4f227d8ac967f71",
"version" : "1.1.0"
}
},
{
"identity" : "swift-concurrency-extras",
"identity" : "swift-crypto",
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swift-concurrency-extras",
"location" : "https://github.com/apple/swift-crypto.git",
"state" : {
"revision" : "bb5059bde9022d69ac516803f4f227d8ac967f71",
"version" : "1.1.0"
"revision" : "60f13f60c4d093691934dc6cfdf5f508ada1f894",
"version" : "2.6.0"
}
},
{
Expand Down Expand Up @@ -47,4 +47,4 @@
}
],
"version" : 2
}
}
36 changes: 25 additions & 11 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,28 @@
import Foundation
import PackageDescription

var dependencies: [Package.Dependency] = [
.package(url: "https://github.com/pointfreeco/swift-snapshot-testing", from: "1.8.1"),
.package(url: "https://github.com/pointfreeco/xctest-dynamic-overlay", from: "1.0.0"),
.package(url: "https://github.com/pointfreeco/swift-concurrency-extras", from: "1.0.0"),
.package(url: "https://github.com/apple/swift-crypto.git", "1.0.0" ..< "3.0.0"),
]

var goTrueDependencies: [Target.Dependency] = [
"_Helpers",
.product(name: "ConcurrencyExtras", package: "swift-concurrency-extras"),
.product(name: "Crypto", package: "swift-crypto"),
]

#if !os(Windows) && !os(Linux)
dependencies += [
.package(url: "https://github.com/kishikawakatsumi/KeychainAccess", from: "4.2.2"),
]
goTrueDependencies += [
.product(name: "KeychainAccess", package: "KeychainAccess"),
]
#endif

let package = Package(
name: "Supabase",
platforms: [
Expand All @@ -24,12 +46,7 @@ let package = Package(
targets: ["Supabase", "Functions", "PostgREST", "Auth", "Realtime", "Storage"]
),
],
dependencies: [
.package(url: "https://github.com/kishikawakatsumi/KeychainAccess", from: "4.2.2"),
.package(url: "https://github.com/pointfreeco/swift-snapshot-testing", from: "1.8.1"),
.package(url: "https://github.com/pointfreeco/xctest-dynamic-overlay", from: "1.0.0"),
.package(url: "https://github.com/pointfreeco/swift-concurrency-extras", from: "1.0.0"),
],
dependencies: dependencies,
targets: [
.target(
name: "_Helpers",
Expand All @@ -47,16 +64,13 @@ let package = Package(
),
.target(
name: "Auth",
dependencies: [
"_Helpers",
.product(name: "ConcurrencyExtras", package: "swift-concurrency-extras"),
.product(name: "KeychainAccess", package: "KeychainAccess"),
]
dependencies: goTrueDependencies
),
.testTarget(
name: "AuthTests",
dependencies: [
"Auth",
"_Helpers",
.product(name: "SnapshotTesting", package: "swift-snapshot-testing"),
.product(name: "XCTestDynamicOverlay", package: "xctest-dynamic-overlay"),
],
Expand Down
4 changes: 2 additions & 2 deletions Sources/Auth/AuthClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public actor AuthClient {
url: URL,
headers: [String: String] = [:],
flowType: AuthFlowType = Configuration.defaultFlowType,
localStorage: AuthLocalStorage = Configuration.defaultLocalStorage,
localStorage: AuthLocalStorage,
encoder: JSONEncoder = AuthClient.Configuration.jsonEncoder,
decoder: JSONDecoder = AuthClient.Configuration.jsonDecoder,
fetch: @escaping FetchHandler = { try await URLSession.shared.data(for: $0) }
Expand Down Expand Up @@ -101,7 +101,7 @@ public actor AuthClient {
url: URL,
headers: [String: String] = [:],
flowType: AuthFlowType = AuthClient.Configuration.defaultFlowType,
localStorage: AuthLocalStorage = AuthClient.Configuration.defaultLocalStorage,
localStorage: AuthLocalStorage,
encoder: JSONEncoder = AuthClient.Configuration.jsonEncoder,
decoder: JSONDecoder = AuthClient.Configuration.jsonDecoder,
fetch: @escaping FetchHandler = { try await URLSession.shared.data(for: $0) }
Expand Down
32 changes: 0 additions & 32 deletions Sources/Auth/AuthLocalStorage.swift

This file was deleted.

6 changes: 0 additions & 6 deletions Sources/Auth/Defaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,4 @@ extension AuthClient.Configuration {

/// The default ``AuthFlowType`` used when initializing a ``AuthClient`` instance.
public static let defaultFlowType: AuthFlowType = .pkce

/// The default ``AuthLocalStorage`` instance used by the ``AuthClient``.
public static let defaultLocalStorage: AuthLocalStorage = KeychainLocalStorage(
service: "supabase.gotrue.swift",
accessGroup: nil
)
}
30 changes: 30 additions & 0 deletions Sources/Auth/Internal/FixedWidthInteger+Random.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import Foundation

// Borrowed from the Vapor project, https://github.com/vapor/vapor/blob/main/Sources/Vapor/Utilities/Array%2BRandom.swift#L14
extension FixedWidthInteger {
internal static func random() -> Self {
return Self.random(in: .min ... .max)
}

internal static func random<T>(using generator: inout T) -> Self
where T : RandomNumberGenerator
{
return Self.random(in: .min ... .max, using: &generator)
}
}

extension Array where Element: FixedWidthInteger {
internal static func random(count: Int) -> [Element] {
var array: [Element] = .init(repeating: 0, count: count)
(0..<count).forEach { array[$0] = Element.random() }
return array
}

internal static func random<T>(count: Int, using generator: inout T) -> [Element]
where T: RandomNumberGenerator
{
var array: [Element] = .init(repeating: 0, count: count)
(0..<count).forEach { array[$0] = Element.random(using: &generator) }
return array
}
}
9 changes: 5 additions & 4 deletions Sources/Auth/Internal/PKCE.swift
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import CryptoKit
import Crypto
import Foundation

enum PKCE {
static func generateCodeVerifier() -> String {
var buffer = [UInt8](repeating: 0, count: 64)
_ = SecRandomCopyBytes(kSecRandomDefault, buffer.count, &buffer)
let buffer = [UInt8].random(count: 64)
return Data(buffer).pkceBase64EncodedString()
}

static func generateCodeChallenge(from string: String) -> String {
guard let data = string.data(using: .utf8) else {
preconditionFailure("provided string should be utf8 encoded.")
}
let hashed = SHA256.hash(data: data)

var hasher = SHA256()
hasher.update(data: data)
let hashed = hasher.finalize()
return Data(hashed).pkceBase64EncodedString()
}
}
Expand Down
1 change: 0 additions & 1 deletion Sources/Auth/Internal/SessionManager.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import Foundation
import KeychainAccess
@_spi(Internal) import _Helpers

struct SessionRefresher: Sendable {
Expand Down
7 changes: 7 additions & 0 deletions Sources/Auth/Storage/AuthLocalStorage.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Foundation

public protocol AuthLocalStorage: Sendable {
func store(key: String, value: Data) throws
func retrieve(key: String) throws -> Data?
func remove(key: String) throws
}
28 changes: 28 additions & 0 deletions Sources/Auth/Storage/KeychainLocalStorage.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#if !os(Windows) && !os(Linux)
import Foundation
@preconcurrency import KeychainAccess

public struct KeychainLocalStorage: AuthLocalStorage {
private let keychain: Keychain

public init(service: String, accessGroup: String?) {
if let accessGroup {
keychain = Keychain(service: service, accessGroup: accessGroup)
} else {
keychain = Keychain(service: service)
}
}

public func store(key: String, value: Data) throws {
try keychain.set(value, key: key)
}

public func retrieve(key: String) throws -> Data? {
try keychain.getData(key)
}

public func remove(key: String) throws {
try keychain.remove(key)
}
}
#endif
Loading

0 comments on commit 37b68d2

Please sign in to comment.