Skip to content

Commit c35ad00

Browse files
committed
update auth, versioning in pipelines
1 parent fe061c4 commit c35ad00

File tree

9 files changed

+59
-25
lines changed

9 files changed

+59
-25
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414

1515
- name: Build app
1616
run: |
17-
xcodebuild -project Daycal.xcodeproj -scheme Daycal -configuration Release -derivedDataPath build
17+
xcodebuild -project Daycal.xcodeproj -scheme Daycal -configuration Release -derivedDataPath build CURRENT_PROJECT_VERSION=${{ github.run_number }}
1818
1919
- name: Package app
2020
run: |

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616

1717
- name: Build app
1818
run: |
19-
xcodebuild -project Daycal.xcodeproj -scheme Daycal -configuration Release -derivedDataPath build
19+
xcodebuild -project Daycal.xcodeproj -scheme Daycal -configuration Release -derivedDataPath build CURRENT_PROJECT_VERSION=${{ github.run_number }}
2020
2121
- name: Create DMG
2222
run: |

Daycal.xcodeproj/project.pbxproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,10 @@
113113
/* End PBXSourcesBuildPhase section */
114114

115115
/* Begin XCBuildConfiguration section */
116-
231A49875AB7488A8723C653CBA2D987 /* Debug */ = {isa = XCBuildConfiguration; buildSettings = {CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = "$(SRCROOT)/Daycal/Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = com.jackmal.daycal; MACOSX_DEPLOYMENT_TARGET = 13.0; PRODUCT_NAME = Daycal; SWIFT_VERSION = 5.9;}; name = Debug; };
117-
1972127CC949410898FE23EFB31DC5D9 /* Release */ = {isa = XCBuildConfiguration; buildSettings = {CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = "$(SRCROOT)/Daycal/Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = com.jackmal.daycal; MACOSX_DEPLOYMENT_TARGET = 13.0; PRODUCT_NAME = Daycal; SWIFT_VERSION = 5.9;}; name = Release; };
118-
D31D27B8C16440C19203EC9B4CF566EB /* Debug */ = {isa = XCBuildConfiguration; buildSettings = {CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = "$(SRCROOT)/Daycal/Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = com.jackmal.daycal; MACOSX_DEPLOYMENT_TARGET = 13.0; PRODUCT_NAME = Daycal; SWIFT_VERSION = 5.9;}; name = Debug; };
119-
7FD36384F2094D4095CAE7D6ECC018A3 /* Release */ = {isa = XCBuildConfiguration; buildSettings = {CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = "$(SRCROOT)/Daycal/Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = com.jackmal.daycal; MACOSX_DEPLOYMENT_TARGET = 13.0; PRODUCT_NAME = Daycal; SWIFT_VERSION = 5.9;}; name = Release; };
116+
231A49875AB7488A8723C653CBA2D987 /* Debug */ = {isa = XCBuildConfiguration; buildSettings = {CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = "$(SRCROOT)/Daycal/Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = com.jackmal.daycal; MACOSX_DEPLOYMENT_TARGET = 13.0; PRODUCT_NAME = Daycal; SWIFT_VERSION = 5.9; CURRENT_PROJECT_VERSION = 1;}; name = Debug; };
117+
1972127CC949410898FE23EFB31DC5D9 /* Release */ = {isa = XCBuildConfiguration; buildSettings = {CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = "$(SRCROOT)/Daycal/Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = com.jackmal.daycal; MACOSX_DEPLOYMENT_TARGET = 13.0; PRODUCT_NAME = Daycal; SWIFT_VERSION = 5.9; CURRENT_PROJECT_VERSION = 1;}; name = Release; };
118+
D31D27B8C16440C19203EC9B4CF566EB /* Debug */ = {isa = XCBuildConfiguration; buildSettings = {CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = "$(SRCROOT)/Daycal/Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = com.jackmal.daycal; MACOSX_DEPLOYMENT_TARGET = 13.0; PRODUCT_NAME = Daycal; SWIFT_VERSION = 5.9; CURRENT_PROJECT_VERSION = 1;}; name = Debug; };
119+
7FD36384F2094D4095CAE7D6ECC018A3 /* Release */ = {isa = XCBuildConfiguration; buildSettings = {CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = "$(SRCROOT)/Daycal/Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = com.jackmal.daycal; MACOSX_DEPLOYMENT_TARGET = 13.0; PRODUCT_NAME = Daycal; SWIFT_VERSION = 5.9; CURRENT_PROJECT_VERSION = 1;}; name = Release; };
120120
/* End XCBuildConfiguration section */
121121

122122
/* Begin XCConfigurationList section */

Daycal/Info.plist

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
<string>Daycal</string>
77
<key>CFBundleIdentifier</key>
88
<string>com.jackmal.daycal</string>
9+
<key>CFBundleVersion</key>
10+
<string>$(CURRENT_PROJECT_VERSION)</string>
911
<key>CFBundleName</key>
1012
<string>Daycal</string>
1113
<key>CFBundlePackageType</key>

Sources/Daycal/AuthRedirectHandler.swift

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
import Foundation
22

33
final class AuthRedirectHandler {
4+
struct AuthResponse {
5+
let code: String
6+
let state: String?
7+
}
8+
49
static let shared = AuthRedirectHandler()
510

6-
private var completion: ((Result<String, Error>) -> Void)?
11+
private var completion: ((Result<AuthResponse, Error>) -> Void)?
712

813
private init() {}
914

10-
func start(completion: @escaping (Result<String, Error>) -> Void) {
15+
func start(completion: @escaping (Result<AuthResponse, Error>) -> Void) {
1116
self.completion = completion
1217
}
1318

@@ -20,7 +25,8 @@ final class AuthRedirectHandler {
2025
return
2126
}
2227

23-
completion?(.success(code))
28+
let state = items.first(where: { $0.name == "state" })?.value
29+
completion?(.success(AuthResponse(code: code, state: state)))
2430
completion = nil
2531
}
2632
}

Sources/Daycal/CalendarStore.swift

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,21 @@ final class CalendarStore: ObservableObject {
2222

2323
init() {
2424
Task {
25-
if tokenStore.hasToken {
26-
authState = .signedIn
27-
await refreshEvents()
28-
scheduleAutoRefresh()
29-
} else {
25+
await restoreSession()
26+
}
27+
}
28+
29+
private func restoreSession() async {
30+
do {
31+
_ = try await tokenStore.validToken()
32+
authState = .signedIn
33+
await refreshEvents()
34+
scheduleAutoRefresh()
35+
} catch {
36+
if let authError = error as? CalendarAuthError, authError == .missingToken {
3037
authState = .signedOut
38+
} else {
39+
authState = .error(error.localizedDescription)
3140
}
3241
}
3342
}
@@ -44,7 +53,11 @@ final class CalendarStore: ObservableObject {
4453
scheduleAutoRefresh()
4554
} catch {
4655
if Task.isCancelled { return }
47-
authState = .error(error.localizedDescription)
56+
if let authError = error as? CalendarAuthError, authError == .missingToken {
57+
authState = .signedOut
58+
} else {
59+
authState = .error(error.localizedDescription)
60+
}
4861
}
4962
}
5063
}
@@ -60,11 +73,6 @@ final class CalendarStore: ObservableObject {
6073
}
6174

6275
func refreshEvents() async {
63-
guard tokenStore.hasValidToken else {
64-
authState = .signedOut
65-
return
66-
}
67-
6876
isLoading = true
6977
defer { isLoading = false }
7078

Sources/Daycal/EventsMenuView.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,13 @@ struct EventsMenuView: View {
9595
}
9696
case .error(let message):
9797
Text("Error: \(message)")
98-
Button("Try Again") {
99-
calendarStore.signIn()
98+
HStack(spacing: 8) {
99+
Button("Try Again") {
100+
calendarStore.signIn()
101+
}
102+
Button("Quit") {
103+
NSApplication.shared.terminate(nil)
104+
}
100105
}
101106
}
102107
}

Sources/Daycal/GoogleAuthService.swift

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ enum CalendarAuthError: Error, LocalizedError {
66
case invalidRedirect
77
case tokenExchangeFailed
88
case cancelled
9+
case stateMismatch
910

1011
var errorDescription: String? {
1112
switch self {
@@ -17,10 +18,13 @@ enum CalendarAuthError: Error, LocalizedError {
1718
return "Could not exchange authorization code."
1819
case .cancelled:
1920
return "Sign-in was cancelled."
21+
case .stateMismatch:
22+
return "Sign-in response did not match the request."
2023
}
2124
}
2225
}
2326

27+
2428
final class GoogleAuthService: NSObject {
2529
private var authContinuation: CheckedContinuation<OAuthToken, Error>?
2630
private let redirectServer = RedirectServer()
@@ -67,10 +71,15 @@ final class GoogleAuthService: NSObject {
6771
authContinuation = continuation
6872
AuthRedirectHandler.shared.start { result in
6973
switch result {
70-
case .success(let code):
74+
case .success(let response):
75+
guard response.state == state else {
76+
server.stop()
77+
continuation.resume(throwing: CalendarAuthError.stateMismatch)
78+
return
79+
}
7180
Task {
7281
do {
73-
let token = try await GoogleAuthService.exchangeCodeForToken(code: code)
82+
let token = try await GoogleAuthService.exchangeCodeForToken(code: response.code)
7483
server.stop()
7584
continuation.resume(returning: token)
7685
} catch {
@@ -85,7 +94,7 @@ final class GoogleAuthService: NSObject {
8594
}
8695

8796
Task {
88-
try? await Task.sleep(nanoseconds: 30_000_000_000)
97+
try? await Task.sleep(nanoseconds: 60_000_000_000)
8998
guard server.isRunning else { return }
9099
server.stop()
91100
continuation.resume(throwing: CalendarAuthError.cancelled)

Sources/Daycal/TokenStore.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ final class TokenStore {
4343
}
4444

4545
if token.isExpired {
46+
guard !token.refreshToken.isEmpty else {
47+
clear()
48+
throw CalendarAuthError.missingToken
49+
}
4650
let refreshed = try await GoogleAuthService().refresh(token: token)
4751
save(token: refreshed)
4852
return refreshed

0 commit comments

Comments
 (0)