Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Basic Windows & Linux Support #184

Merged
merged 41 commits into from
Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
be0e50a
Seems to compile and sort of run tests
brianmichel Dec 2, 2023
4807c7e
Fix a few more issues
brianmichel Dec 3, 2023
e36d3b6
Fix naming and unneeded import
brianmichel Dec 3, 2023
ae2d272
Get CI a little further
brianmichel Dec 3, 2023
85064a8
Use platform specific snapshots
brianmichel Dec 4, 2023
4ae07d7
More snapshots
brianmichel Dec 4, 2023
448d0f2
Try again with non-Darwin
brianmichel Dec 4, 2023
24499b7
Turns out we just need a darwin and non-darwin snapshots dir
brianmichel Dec 10, 2023
c01a57c
Fix header look up for client creation
brianmichel Dec 10, 2023
aca8534
update ci configuration
brianmichel Dec 10, 2023
c7bb863
Skip problematic test on Windows and Linux
brianmichel Dec 10, 2023
83b93ad
Use a supported version of Swift for the linux CI
brianmichel Dec 10, 2023
ce5ed7f
Go back to specific folders for each platform, it's just easier to de…
brianmichel Dec 10, 2023
672b583
Attempt to disable conversion and skip broken test
brianmichel Dec 10, 2023
00b5592
Cleaning up the PR a little
brianmichel Dec 10, 2023
a8883c6
Add missing test files
brianmichel Dec 10, 2023
fdaad65
Update tests
brianmichel Dec 10, 2023
638091d
Address some PR feedback
brianmichel Dec 17, 2023
bfc9620
Recomment out test
brianmichel Dec 17, 2023
1e382ec
Drop down to see if using a Semaphore works across platforms
brianmichel Dec 17, 2023
8921a7c
Allow threading in a local storage client
brianmichel Dec 17, 2023
13cd94d
Remove defaults from designated initializer and put defaults in platf…
brianmichel Dec 17, 2023
bcf8ccf
Give linux a convenience init so tests can use it
brianmichel Dec 17, 2023
73abf4d
Remove default local storage
brianmichel Dec 29, 2023
e938502
Fix mock generation
brianmichel Dec 29, 2023
e027681
Remove old tests
brianmichel Dec 29, 2023
d6cb996
Remove autocanonicalization problems by making Apikey consistent casing.
brianmichel Dec 29, 2023
609ca57
Re-record all snapshots to prevent auto-canonicalization issues acros…
brianmichel Dec 29, 2023
3970e3d
Turn on autocrlf to see if that gets the test snapshots further
brianmichel Jan 6, 2024
60d9d88
fix line endings for snapshots
brianmichel Jan 7, 2024
ecf5835
fix initializer
brianmichel Jan 7, 2024
44a2aaf
Remove unneeded testing workaround
brianmichel Jan 7, 2024
300f82f
Update tests to use empty response where needed
brianmichel Jan 7, 2024
08abb80
Fix empty implementation to work across platforms
brianmichel Jan 7, 2024
d093cef
Fix test that breaks to only run if not Windows or Linux
brianmichel Jan 7, 2024
2a96bc6
use correct operator
brianmichel Jan 7, 2024
e28cb4e
Fix binary packaging
brianmichel Jan 7, 2024
af527e6
Use URLs and remove convenience
brianmichel Jan 9, 2024
03e7b74
Fix cancellation
brianmichel Jan 9, 2024
f20c806
fix example
brianmichel Jan 9, 2024
0f9be90
Fix actuallyCancel logic
brianmichel Jan 9, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
brianmichel marked this conversation as resolved.
Show resolved Hide resolved
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