From 83c127e31e2729ef35ee82710f739250084452c6 Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Mon, 16 Dec 2024 13:28:46 +1100 Subject: [PATCH 1/9] ci: add test, fmt, lint workflows --- .github/workflows/ci.yml | 54 +++++++++++ Coder Desktop/.swiftlint.yml | 3 + .../Coder Desktop.xcodeproj/project.pbxproj | 2 + Coder Desktop/Coder Desktop.xctestplan | 1 + Coder Desktop/Coder Desktop/Theme.swift | 1 + .../Coder Desktop/Views/Agents.swift | 2 +- .../Coder Desktop/Views/LoginForm.swift | 11 ++- Coder Desktop/Coder Desktop/Views/Util.swift | 2 +- .../Coder Desktop/Views/VPNMenu.swift | 2 +- .../Coder Desktop/Views/VPNState.swift | 2 +- .../Coder DesktopTests/AgentsTests.swift | 4 +- .../Coder DesktopTests/LoginFormTests.swift | 4 +- .../Coder DesktopTests/VPNMenuTests.swift | 2 +- .../Coder DesktopTests/VPNStateTests.swift | 4 +- .../Coder_DesktopUITests.swift | 3 +- .../Coder_DesktopUITestsLaunchTests.swift | 2 +- Coder Desktop/Proto/Receiver.swift | 6 +- Coder Desktop/Proto/Speaker.swift | 79 +++++++-------- Coder Desktop/ProtoTests/ProtoTests.swift | 95 ++++++++++--------- Coder Desktop/ProtoTests/SpeakerTests.swift | 45 ++++----- Coder Desktop/VPN/PacketTunnelProvider.swift | 13 ++- Makefile | 35 +++++++ 22 files changed, 238 insertions(+), 134 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..0f48417 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,54 @@ +name: ci + +on: + pull_request: + paths-ignore: + - "README.md" + push: + branches: + - main + paths-ignore: + - "README.md" + + +permissions: + contents: read + +jobs: + test: + name: test + runs-on: ${{ github.repository_owner == 'coder' && 'depot-macos-latest' || 'macos-latest'}} + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + fetch-depth: 1 + - name: Switch XCode Version + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '16.0.0' + - run: | + make test + format: + name: fmt + runs-on: ${{ github.repository_owner == 'coder' && 'depot-macos-latest' || 'macos-latest'}} + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + fetch-depth: 1 + - run: | + make fmt + lint: + name: lint + runs-on: ${{ github.repository_owner == 'coder' && 'depot-macos-latest' || 'macos-latest'}} + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + fetch-depth: 1 + - name: Install Swiftlint + run: | + brew install swiftlint + - run: | + make lint \ No newline at end of file diff --git a/Coder Desktop/.swiftlint.yml b/Coder Desktop/.swiftlint.yml index 49d9e03..1c2e5c4 100644 --- a/Coder Desktop/.swiftlint.yml +++ b/Coder Desktop/.swiftlint.yml @@ -1,7 +1,10 @@ disabled_rules: - todo - trailing_comma + - blanket_disable_command # Used by Protobuf + - opening_brace # Handled by SwiftFormat type_name: allowed_symbols: "_" identifier_name: allowed_symbols: "_" + min_length: 1 diff --git a/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj b/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj index b23a4a1..b3c2f19 100644 --- a/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj +++ b/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj @@ -721,6 +721,7 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 4399GN35BJ; GENERATE_INFOPLIST_FILE = YES; + MACOSX_DEPLOYMENT_TARGET = 14.6; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-DesktopUITests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -737,6 +738,7 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 4399GN35BJ; GENERATE_INFOPLIST_FILE = YES; + MACOSX_DEPLOYMENT_TARGET = 14.6; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-DesktopUITests"; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/Coder Desktop/Coder Desktop.xctestplan b/Coder Desktop/Coder Desktop.xctestplan index ab80bb8..d1197e5 100644 --- a/Coder Desktop/Coder Desktop.xctestplan +++ b/Coder Desktop/Coder Desktop.xctestplan @@ -25,6 +25,7 @@ } }, { + "enabled" : false, "parallelizable" : true, "target" : { "containerPath" : "container:Coder Desktop.xcodeproj", diff --git a/Coder Desktop/Coder Desktop/Theme.swift b/Coder Desktop/Coder Desktop/Theme.swift index 1303a4c..192cc36 100644 --- a/Coder Desktop/Coder Desktop/Theme.swift +++ b/Coder Desktop/Coder Desktop/Theme.swift @@ -8,5 +8,6 @@ enum Theme { static let rectCornerRadius: CGFloat = 4 } + static let defaultVisibleAgents = 5 } diff --git a/Coder Desktop/Coder Desktop/Views/Agents.swift b/Coder Desktop/Coder Desktop/Views/Agents.swift index 79c402f..42f12c9 100644 --- a/Coder Desktop/Coder Desktop/Views/Agents.swift +++ b/Coder Desktop/Coder Desktop/Views/Agents.swift @@ -6,7 +6,7 @@ struct Agents: View { @State private var viewAll = false private let defaultVisibleRows = 5 - internal let inspection = Inspection() + let inspection = Inspection() var body: some View { Group { diff --git a/Coder Desktop/Coder Desktop/Views/LoginForm.swift b/Coder Desktop/Coder Desktop/Views/LoginForm.swift index c4fe153..f10a88c 100644 --- a/Coder Desktop/Coder Desktop/Views/LoginForm.swift +++ b/Coder Desktop/Coder Desktop/Views/LoginForm.swift @@ -11,7 +11,7 @@ struct LoginForm: View { @State private var loading: Bool = false @FocusState private var focusedField: LoginField? - internal let inspection = Inspection() + let inspection = Inspection() var body: some View { VStack { @@ -58,7 +58,8 @@ struct LoginForm: View { .onReceive(inspection.notice) { self.inspection.visit(self, $0) } // ViewInspector } - internal func submit() async { + func submit() async { + loginError = nil guard sessionToken != "" else { return } @@ -67,7 +68,7 @@ struct LoginForm: View { return } loading = true - defer { loading = false} + defer { loading = false } let client = C(url: url, token: sessionToken) do throws(ClientError) { _ = try await client.user("me") @@ -134,8 +135,8 @@ struct LoginForm: View { Button("Sign In") { Task { await submit() } } - .buttonStyle(.borderedProminent) - .keyboardShortcut(.defaultAction) + .buttonStyle(.borderedProminent) + .keyboardShortcut(.defaultAction) }.padding(.top, 5) } } diff --git a/Coder Desktop/Coder Desktop/Views/Util.swift b/Coder Desktop/Coder Desktop/Views/Util.swift index 8f42891..96fb246 100644 --- a/Coder Desktop/Coder Desktop/Views/Util.swift +++ b/Coder Desktop/Coder Desktop/Views/Util.swift @@ -1,7 +1,7 @@ import Combine // This is required for inspecting stateful views -internal final class Inspection { +final class Inspection { let notice = PassthroughSubject() var callbacks = [UInt: (V) -> Void]() diff --git a/Coder Desktop/Coder Desktop/Views/VPNMenu.swift b/Coder Desktop/Coder Desktop/Views/VPNMenu.swift index 814c828..e3a6459 100644 --- a/Coder Desktop/Coder Desktop/Views/VPNMenu.swift +++ b/Coder Desktop/Coder Desktop/Views/VPNMenu.swift @@ -4,7 +4,7 @@ struct VPNMenu: View { @EnvironmentObject var vpn: VPN @EnvironmentObject var session: S - internal let inspection = Inspection() + let inspection = Inspection() var body: some View { // Main stack diff --git a/Coder Desktop/Coder Desktop/Views/VPNState.swift b/Coder Desktop/Coder Desktop/Views/VPNState.swift index 05f1c8b..1710203 100644 --- a/Coder Desktop/Coder Desktop/Views/VPNState.swift +++ b/Coder Desktop/Coder Desktop/Views/VPNState.swift @@ -3,7 +3,7 @@ import SwiftUI struct VPNState: View { @EnvironmentObject var vpn: VPN - internal let inspection = Inspection() + let inspection = Inspection() var body: some View { Group { diff --git a/Coder Desktop/Coder DesktopTests/AgentsTests.swift b/Coder Desktop/Coder DesktopTests/AgentsTests.swift index 2563362..8322fe2 100644 --- a/Coder Desktop/Coder DesktopTests/AgentsTests.swift +++ b/Coder Desktop/Coder DesktopTests/AgentsTests.swift @@ -1,7 +1,7 @@ @testable import Coder_Desktop +import SwiftUI import Testing import ViewInspector -import SwiftUI @Suite(.timeLimit(.minutes(1))) struct AgentsTests { @@ -47,7 +47,7 @@ struct AgentsTests { let forEach = try view.inspect().find(ViewType.ForEach.self) #expect(forEach.count == Theme.defaultVisibleAgents) - #expect(throws: Never.self) { try view.inspect().find(link: "a1.coder")} + #expect(throws: Never.self) { try view.inspect().find(link: "a1.coder") } } @Test diff --git a/Coder Desktop/Coder DesktopTests/LoginFormTests.swift b/Coder Desktop/Coder DesktopTests/LoginFormTests.swift index 3bdbf6a..b81f5c0 100644 --- a/Coder Desktop/Coder DesktopTests/LoginFormTests.swift +++ b/Coder Desktop/Coder DesktopTests/LoginFormTests.swift @@ -1,7 +1,7 @@ @testable import Coder_Desktop -import ViewInspector -import Testing import SwiftUI +import Testing +import ViewInspector @Suite(.timeLimit(.minutes(1))) struct LoginTests { diff --git a/Coder Desktop/Coder DesktopTests/VPNMenuTests.swift b/Coder Desktop/Coder DesktopTests/VPNMenuTests.swift index fb975e0..1c711ae 100644 --- a/Coder Desktop/Coder DesktopTests/VPNMenuTests.swift +++ b/Coder Desktop/Coder DesktopTests/VPNMenuTests.swift @@ -1,7 +1,7 @@ @testable import Coder_Desktop +import SwiftUI import Testing import ViewInspector -import SwiftUI @Suite(.timeLimit(.minutes(1))) struct VPNMenuTests { diff --git a/Coder Desktop/Coder DesktopTests/VPNStateTests.swift b/Coder Desktop/Coder DesktopTests/VPNStateTests.swift index c5a167e..07f17e5 100644 --- a/Coder Desktop/Coder DesktopTests/VPNStateTests.swift +++ b/Coder Desktop/Coder DesktopTests/VPNStateTests.swift @@ -1,7 +1,7 @@ @testable import Coder_Desktop -import ViewInspector -import Testing import SwiftUI +import Testing +import ViewInspector @Suite(.timeLimit(.minutes(1))) struct VPNStateTests { diff --git a/Coder Desktop/Coder DesktopUITests/Coder_DesktopUITests.swift b/Coder Desktop/Coder DesktopUITests/Coder_DesktopUITests.swift index 7ceceec..4b563d2 100644 --- a/Coder Desktop/Coder DesktopUITests/Coder_DesktopUITests.swift +++ b/Coder Desktop/Coder DesktopUITests/Coder_DesktopUITests.swift @@ -7,7 +7,8 @@ final class Coder_DesktopUITests: XCTestCase { // In UI tests it is usually best to stop immediately when a failure occurs. continueAfterFailure = false - // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + // In UI tests it’s important to set the initial state - such as + // interface orientation - required for your tests before they run. The setUp method is a good place to do this. } override func tearDownWithError() throws { diff --git a/Coder Desktop/Coder DesktopUITests/Coder_DesktopUITestsLaunchTests.swift b/Coder Desktop/Coder DesktopUITests/Coder_DesktopUITestsLaunchTests.swift index ad6b62c..27b4832 100644 --- a/Coder Desktop/Coder DesktopUITests/Coder_DesktopUITestsLaunchTests.swift +++ b/Coder Desktop/Coder DesktopUITests/Coder_DesktopUITestsLaunchTests.swift @@ -1,7 +1,7 @@ import XCTest final class Coder_DesktopUITestsLaunchTests: XCTestCase { - override class var runsForEachTargetApplicationUIConfiguration: Bool { + override static var runsForEachTargetApplicationUIConfiguration: Bool { true } diff --git a/Coder Desktop/Proto/Receiver.swift b/Coder Desktop/Proto/Receiver.swift index 89ea0d3..74a85cb 100644 --- a/Coder Desktop/Proto/Receiver.swift +++ b/Coder Desktop/Proto/Receiver.swift @@ -1,6 +1,6 @@ import Foundation -import SwiftProtobuf import os +import SwiftProtobuf /// An actor that reads data from a `DispatchIO` channel, and deserializes it into VPN protocol messages. actor Receiver { @@ -19,7 +19,7 @@ actor Receiver { private func readLen() async throws -> UInt32 { let lenD: Data = try await withCheckedThrowingContinuation { continuation in var lenData = Data() - dispatch.read(offset: 0, length: 4, queue: queue) {done, data, error in + dispatch.read(offset: 0, length: 4, queue: queue) { done, data, error in guard error == 0 else { let errStrPtr = strerror(error) let errStr = String(validatingUTF8: errStrPtr!)! @@ -39,7 +39,7 @@ actor Receiver { private func readMsg(_ length: UInt32) async throws -> RecvMsg { let msgData: Data = try await withCheckedThrowingContinuation { continuation in var msgData = Data() - dispatch.read(offset: 0, length: Int(length), queue: queue) {done, data, error in + dispatch.read(offset: 0, length: Int(length), queue: queue) { done, data, error in guard error == 0 else { let errStrPtr = strerror(error) let errStr = String(validatingUTF8: errStrPtr!)! diff --git a/Coder Desktop/Proto/Speaker.swift b/Coder Desktop/Proto/Speaker.swift index 8c2edd3..fdb334b 100644 --- a/Coder Desktop/Proto/Speaker.swift +++ b/Coder Desktop/Proto/Speaker.swift @@ -1,15 +1,15 @@ import Foundation -import SwiftProtobuf import os +import SwiftProtobuf -let newLine = 0x0a +let newLine = 0x0A let headerPreamble = "codervpn" /// A message that has the `rpc` property for recording participation in a unary RPC. protocol RPCMessage { - var rpc: Vpn_RPC {get set} + var rpc: Vpn_RPC { get set } /// Returns true if `rpc` has been explicitly set. - var hasRpc: Bool {get} + var hasRpc: Bool { get } } extension Vpn_TunnelMessage: RPCMessage {} @@ -26,7 +26,7 @@ struct ProtoVersion: CustomStringConvertible, Equatable, Codable { let major: Int let minor: Int - var description: String {"\(major).\(minor)"} + var description: String { "\(major).\(minor)" } init(_ major: Int, _ minor: Int) { self.major = major @@ -34,7 +34,7 @@ struct ProtoVersion: CustomStringConvertible, Equatable, Codable { } init(parse str: String) throws { - let parts = str.split(separator: ".").map({Int($0)}) + let parts = str.split(separator: ".").map { Int($0) } if parts.count != 2 { throw HandshakeError.invalidVersion(str) } @@ -65,23 +65,24 @@ class Speaker { init(writeFD: FileHandle, readFD: FileHandle) { self.writeFD = writeFD self.readFD = readFD - self.sender = Sender(writeFD: writeFD) - self.dispatch = DispatchIO( + sender = Sender(writeFD: writeFD) + dispatch = DispatchIO( type: .stream, fileDescriptor: readFD.fileDescriptor, queue: queue, - cleanupHandler: {_ in - do { - try readFD.close() - } catch { - // TODO + cleanupHandler: { _ in + do { + try readFD.close() + } catch { + // TODO: + } } - }) - self.receiver = Receiver(dispatch: self.dispatch, queue: self.queue) + ) + receiver = Receiver(dispatch: dispatch, queue: queue) if SendMsg.self == Vpn_TunnelMessage.self { - self.role = .tunnel + role = .tunnel } else { - self.role = .manager + role = .manager } } @@ -94,22 +95,22 @@ class Speaker { /// Reads and handles protocol messages. func readLoop() async throws { - for try await msg in try await self.receiver.messages() { + for try await msg in try await receiver.messages() { guard msg.hasRpc else { - self.handleMessage(msg) + handleMessage(msg) continue } guard msg.rpc.msgID == 0 else { - let req = RPCRequest(req: msg, sender: self.sender) - self.handleRPC(req) + let req = RPCRequest(req: msg, sender: sender) + handleRPC(req) continue } guard msg.rpc.responseTo == 0 else { - self.logger.debug("got RPC reply for msgID \(msg.rpc.responseTo)") + logger.debug("got RPC reply for msgID \(msg.rpc.responseTo)") do throws(RPCError) { try await self.secretary.route(reply: msg) } catch { - self.logger.error( + logger.error( "couldn't route RPC reply for \(msg.rpc.responseTo): \(error)") } continue @@ -120,13 +121,13 @@ class Speaker { /// Handles a single non-RPC message. It is expected that subclasses override this method with their own handlers. func handleMessage(_ msg: RecvMsg) { // just log - self.logger.debug("got non-RPC message \(msg.textFormatString())") + logger.debug("got non-RPC message \(msg.textFormatString())") } /// Handle a single RPC request. It is expected that subclasses override this method with their own handlers. func handleRPC(_ req: RPCRequest) { // just log - self.logger.debug("got RPC message \(req.msg.textFormatString())") + logger.debug("got RPC message \(req.msg.textFormatString())") } /// Send a unary RPC message and handle the response @@ -152,7 +153,7 @@ class Speaker { func closeWrite() { do { - try self.writeFD.close() + try writeFD.close() } catch { logger.error("failed to close write file handle: \(error)") } @@ -160,7 +161,7 @@ class Speaker { func closeRead() { do { - try self.readFD.close() + try readFD.close() } catch { logger.error("failed to close read file handle: \(error)") } @@ -171,16 +172,16 @@ class Speaker { class Handshaker { private let writeFD: FileHandle private let dispatch: DispatchIO - private var theirData: Data = Data() + private var theirData: Data = .init() private let versions: [ProtoVersion] private let role: ProtoRole private var continuation: CheckedContinuation? private let queue: DispatchQueue - init (writeFD: FileHandle, dispatch: DispatchIO, queue: DispatchQueue, - role: ProtoRole, - versions: [ProtoVersion] = [.init(1, 0)] - ) { + init(writeFD: FileHandle, dispatch: DispatchIO, queue: DispatchQueue, + role: ProtoRole, + versions: [ProtoVersion] = [.init(1, 0)]) + { self.writeFD = writeFD self.dispatch = dispatch self.role = role @@ -198,7 +199,7 @@ class Handshaker { handleRead(false, nil, 0) } - let vStr = versions.map({$0.description}).joined(separator: ",") + let vStr = versions.map { $0.description }.joined(separator: ",") let ours = String(format: "\(headerPreamble) \(role) \(vStr)\n") try writeFD.write(contentsOf: ours.data(using: .utf8)!) @@ -222,12 +223,12 @@ class Handshaker { continuation?.resume(throwing: HandshakeError.readError(errStr)) return } - if let ddd = data, !ddd.isEmpty { - guard ddd[0] != newLine else { + if let d = data, !d.isEmpty { + guard d[0] != newLine else { continuation?.resume(returning: theirData) return } - theirData.append(contentsOf: ddd) + theirData.append(contentsOf: d) } // read another byte, one at a time, so we don't read beyond the header. @@ -243,7 +244,7 @@ class Handshaker { throw HandshakeError.invalidHeader("expected \(headerPreamble) but got \(parts[0])") } var expectedRole = ProtoRole.manager - if self.role == .manager { + if role == .manager { expectedRole = .tunnel } guard parts[1] == expectedRole.rawValue else { @@ -251,7 +252,7 @@ class Handshaker { } let theirVersions = try parts[2] .split(separator: ",") - .map({try ProtoVersion(parse: String($0))}) + .map { try ProtoVersion(parse: String($0)) } return try pickVersion(ours: versions, theirs: theirVersions) } } @@ -281,7 +282,7 @@ struct RPCRequest { private let sender: Sender public init(req: RecvMsg, sender: Sender) { - self.msg = req + msg = req self.sender = sender } diff --git a/Coder Desktop/ProtoTests/ProtoTests.swift b/Coder Desktop/ProtoTests/ProtoTests.swift index b49d4ad..1df9ae0 100644 --- a/Coder Desktop/ProtoTests/ProtoTests.swift +++ b/Coder Desktop/ProtoTests/ProtoTests.swift @@ -1,19 +1,19 @@ -import Testing -import Foundation @testable import Coder_Desktop +import Foundation +import Testing @Suite(.timeLimit(.minutes(1))) struct SenderReceiverTests { let pipe = Pipe() let dispatch: DispatchIO let queue: DispatchQueue = .global(qos: .utility) - + init() { - self.dispatch = DispatchIO( + dispatch = DispatchIO( type: .stream, fileDescriptor: pipe.fileHandleForReading.fileDescriptor, queue: queue, - cleanupHandler: {error in print("cleanupHandler: \(error)")} + cleanupHandler: { error in print("cleanupHandler: \(error)") } ) } @@ -34,14 +34,14 @@ struct SenderReceiverTests { } #expect(count == 1) } - + @Test func sendMany() async throws { let s = Sender(writeFD: pipe.fileHandleForWriting) let r = Receiver(dispatch: dispatch, queue: queue) var msg = Vpn_ManagerMessage() msg.networkSettings.errorMessage = "test error" Task { - for _ in 0..<10 { + for _ in 0 ..< 10 { try await s.send(msg) } try await s.close() @@ -62,28 +62,30 @@ struct HandshakerTests { let dispatchT: DispatchIO let dispatchM: DispatchIO let queue: DispatchQueue = .global(qos: .utility) - + init() { - self.dispatchT = DispatchIO( + dispatchT = DispatchIO( type: .stream, fileDescriptor: pipeMT.fileHandleForReading.fileDescriptor, queue: queue, - cleanupHandler: {error in print("cleanupHandler: \(error)")} + cleanupHandler: { error in print("cleanupHandler: \(error)") } ) - self.dispatchM = DispatchIO( + dispatchM = DispatchIO( type: .stream, fileDescriptor: pipeTM.fileHandleForReading.fileDescriptor, queue: queue, - cleanupHandler: {error in print("cleanupHandler: \(error)")} + cleanupHandler: { error in print("cleanupHandler: \(error)") } ) } - + @Test("Default versions") func mainline() async throws { let uutTun = Handshaker( - writeFD: pipeTM.fileHandleForWriting, dispatch: dispatchT, queue: queue, role: .tunnel) + writeFD: pipeTM.fileHandleForWriting, dispatch: dispatchT, queue: queue, role: .tunnel + ) let uutMgr = Handshaker( - writeFD: pipeMT.fileHandleForWriting, dispatch: dispatchM, queue: queue, role: .manager) + writeFD: pipeMT.fileHandleForWriting, dispatch: dispatchM, queue: queue, role: .manager + ) let taskTun = Task { try await uutTun.handshake() } @@ -95,41 +97,45 @@ struct HandshakerTests { let versionMgr = try await taskMgr.value #expect(versionMgr == ProtoVersion(1, 0)) } - - - struct versionCase : CustomStringConvertible { + + struct VersionCase: CustomStringConvertible { let tun: [ProtoVersion] let mgr: [ProtoVersion] let result: ProtoVersion - + var description: String { return "\(tun) vs \(mgr) -> \(result)" } } - + @Test("explicit versions", arguments: [ - versionCase( + VersionCase( tun: [ProtoVersion(1, 0)], mgr: [ProtoVersion(1, 1)], - result: ProtoVersion(1,0)), - versionCase( + result: ProtoVersion(1, 0) + ), + VersionCase( tun: [ProtoVersion(1, 1)], mgr: [ProtoVersion(1, 7)], - result: ProtoVersion(1,1)), - versionCase( + result: ProtoVersion(1, 1) + ), + VersionCase( tun: [ProtoVersion(1, 7), ProtoVersion(2, 1)], mgr: [ProtoVersion(1, 7)], - result: ProtoVersion(1, 7)), - versionCase( + result: ProtoVersion(1, 7) + ), + VersionCase( tun: [ProtoVersion(1, 7)], mgr: [ProtoVersion(1, 7), ProtoVersion(2, 1)], - result: ProtoVersion(1, 7)), - versionCase( + result: ProtoVersion(1, 7) + ), + VersionCase( tun: [ProtoVersion(1, 3), ProtoVersion(2, 1)], mgr: [ProtoVersion(1, 7)], - result: ProtoVersion(1, 3)), + result: ProtoVersion(1, 3) + ), ]) - func explictVersions(tc: versionCase) async throws { + func explictVersions(tc: VersionCase) async throws { let uutTun = Handshaker( writeFD: pipeTM.fileHandleForWriting, dispatch: dispatchT, queue: queue, role: .tunnel, versions: tc.tun @@ -149,16 +155,16 @@ struct HandshakerTests { let versionMgr = try await taskMgr.value #expect(versionMgr == tc.result) } - + @Test func incompatible() async throws { let uutTun = Handshaker( writeFD: pipeTM.fileHandleForWriting, dispatch: dispatchT, queue: queue, role: .tunnel, - versions: [ProtoVersion(1,8)] + versions: [ProtoVersion(1, 8)] ) let uutMgr = Handshaker( writeFD: pipeMT.fileHandleForWriting, dispatch: dispatchM, queue: queue, role: .manager, - versions: [ProtoVersion(2,8)] + versions: [ProtoVersion(2, 8)] ) let taskTun = Task { try await uutTun.handshake() @@ -182,19 +188,19 @@ struct OneSidedHandshakerTests { let queue: DispatchQueue = .global(qos: .utility) let dispatchT: DispatchIO let uut: Handshaker - + init() { - self.dispatchT = DispatchIO( + dispatchT = DispatchIO( type: .stream, fileDescriptor: pipeMT.fileHandleForReading.fileDescriptor, queue: queue, - cleanupHandler: {error in print("cleanupHandler: \(error)")} + cleanupHandler: { error in print("cleanupHandler: \(error)") } ) - self.uut = Handshaker( + uut = Handshaker( writeFD: pipeTM.fileHandleForWriting, dispatch: dispatchT, queue: queue, role: .tunnel ) } - + @Test() func badPreamble() async throws { let taskTun = Task { @@ -207,7 +213,7 @@ struct OneSidedHandshakerTests { try await taskTun.value } } - + @Test(.timeLimit(.minutes(1))) func badRole() async throws { let taskTun = Task { @@ -220,7 +226,7 @@ struct OneSidedHandshakerTests { try await taskTun.value } } - + @Test(.timeLimit(.minutes(1))) func badVersion() async throws { let taskTun = Task { @@ -233,7 +239,7 @@ struct OneSidedHandshakerTests { try await taskTun.value } } - + @Test(.timeLimit(.minutes(1))) func mainline() async throws { let taskTun = Task { @@ -245,9 +251,8 @@ struct OneSidedHandshakerTests { pipeMT.fileHandleForWriting.write(Data("codervpn manager 1.0\n".utf8)) let tunHdr = try pipeTM.fileHandleForReading.readToEnd() #expect(tunHdr == Data("codervpn tunnel 1.0\n".utf8)) - + let v = try await taskTun.value - #expect(v == ProtoVersion(1,0)) + #expect(v == ProtoVersion(1, 0)) } } - diff --git a/Coder Desktop/ProtoTests/SpeakerTests.swift b/Coder Desktop/ProtoTests/SpeakerTests.swift index c4199ff..08829ff 100644 --- a/Coder Desktop/ProtoTests/SpeakerTests.swift +++ b/Coder Desktop/ProtoTests/SpeakerTests.swift @@ -1,6 +1,6 @@ -import Testing -import Foundation @testable import Coder_Desktop +import Foundation +import Testing /// A concrete, test class for the abstract Speaker, which overrides the handlers to send things to /// continuations we set in the test. @@ -9,7 +9,7 @@ class TestTunnel: Speaker { override func handleMessage(_ msg: Vpn_ManagerMessage) { msgHandler?.resume(returning: msg) } - + var rpcHandler: CheckedContinuation, Error>? override func handleRPC(_ req: RPCRequest) { rpcHandler?.resume(returning: req) @@ -25,36 +25,37 @@ struct SpeakerTests { let dispatch: DispatchIO let receiver: Receiver let handshaker: Handshaker - + init() { let queue = DispatchQueue.global(qos: .utility) - self.uut = TestTunnel( + uut = TestTunnel( writeFD: pipeTM.fileHandleForWriting, readFD: pipeMT.fileHandleForReading ) - self.dispatch = DispatchIO( + dispatch = DispatchIO( type: .stream, fileDescriptor: pipeTM.fileHandleForReading.fileDescriptor, queue: queue, - cleanupHandler: {error in print("cleanupHandler: \(error)")} + cleanupHandler: { error in print("cleanupHandler: \(error)") } ) - self.sender = Sender(writeFD: pipeMT.fileHandleForWriting) - self.receiver = Receiver(dispatch: dispatch, queue: queue) - self.handshaker = Handshaker( + sender = Sender(writeFD: pipeMT.fileHandleForWriting) + receiver = Receiver(dispatch: dispatch, queue: queue) + handshaker = Handshaker( writeFD: pipeMT.fileHandleForWriting, - dispatch: self.dispatch, queue: queue, - role: .manager) + dispatch: dispatch, queue: queue, + role: .manager + ) } - + @Test func handshake() async throws { async let v = handshaker.handshake() try await uut.handshake() #expect(try await v == ProtoVersion(1, 0)) } - + @Test func handleSingleMessage() async throws { async let readDone: () = try uut.readLoop() - + let got = try await withCheckedThrowingContinuation { continuation in uut.msgHandler = continuation Task { @@ -69,10 +70,10 @@ struct SpeakerTests { try await sender.close() try await readDone } - + @Test func handleRPC() async throws { async let readDone: () = try uut.readLoop() - + let got = try await withCheckedThrowingContinuation { continuation in uut.rpcHandler = continuation Task { @@ -92,7 +93,7 @@ struct SpeakerTests { reply.rpc.responseTo = 33 try await got.sendReply(reply) uut.closeWrite() - + var count = 0 await #expect(throws: Never.self) { for try await reply in try await receiver.messages() { @@ -104,11 +105,11 @@ struct SpeakerTests { try await sender.close() try await readDone } - + @Test func sendRPCs() async throws { async let readDone: () = try uut.readLoop() - - async let managerDone = Task { + + async let managerDone = Task { var count = 0 for try await req in try await receiver.messages() { #expect(req.msg == .networkSettings(Vpn_NetworkSettingsRequest())) @@ -122,7 +123,7 @@ struct SpeakerTests { } #expect(count == 2) } - for i in 0..<2 { + for i in 0 ..< 2 { var req = Vpn_TunnelMessage() req.networkSettings = Vpn_NetworkSettingsRequest() let got = try await uut.unaryRPC(req) diff --git a/Coder Desktop/VPN/PacketTunnelProvider.swift b/Coder Desktop/VPN/PacketTunnelProvider.swift index c8a87cf..d60e8f9 100644 --- a/Coder Desktop/VPN/PacketTunnelProvider.swift +++ b/Coder Desktop/VPN/PacketTunnelProvider.swift @@ -1,28 +1,27 @@ import NetworkExtension class PacketTunnelProvider: NEPacketTunnelProvider { - - override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) { + override func startTunnel(options _: [String: NSObject]?, completionHandler _: @escaping (Error?) -> Void) { // Add code here to start the process of connecting the tunnel. } - - override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) { + + override func stopTunnel(with _: NEProviderStopReason, completionHandler: @escaping () -> Void) { // Add code here to start the process of stopping the tunnel. completionHandler() } - + override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) { // Add code here to handle the message. if let handler = completionHandler { handler(messageData) } } - + override func sleep(completionHandler: @escaping () -> Void) { // Add code here to get ready to sleep. completionHandler() } - + override func wake() { // Add code here to wake up. } diff --git a/Makefile b/Makefile index 736c77f..1f22f45 100644 --- a/Makefile +++ b/Makefile @@ -1,2 +1,37 @@ +ifdef CI +LINTFLAGS := --reporter github-actions-logging +FMTFLAGS := --lint --reporter github-actions-log +else +LINTFLAGS := +FMTFLAGS := +endif + +PROJECT := "Coder Desktop/Coder Desktop.xcodeproj" +SCHEME := "Coder Desktop" + +fmt: + swiftformat \ + --exclude '**.pb.swift' \ + $(FMTFLAGS) . + +test: + xcodebuild test \ + -project $(PROJECT) \ + -scheme $(SCHEME) \ + -testPlan $(SCHEME) \ + -skipPackagePluginValidation \ + CODE_SIGNING_REQUIRED=NO \ + CODE_SIGNING_ALLOWED=NO \ + | LC_ALL="en_US.UTF-8" xcpretty + +lint: + swiftlint \ + --strict \ + --quiet $(LINTFLAGS) + +clean: + xcodebuild clean \ + -project $(PROJECT) + proto: protoc --swift_out=. 'Coder Desktop/Proto/vpn.proto' From 087e35f02551254b77f1f8bc1dcadcf1f82f851b Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Mon, 16 Dec 2024 17:42:39 +1100 Subject: [PATCH 2/9] makefile indent --- Makefile | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index 1f22f45..e4f0cfb 100644 --- a/Makefile +++ b/Makefile @@ -11,27 +11,27 @@ SCHEME := "Coder Desktop" fmt: swiftformat \ - --exclude '**.pb.swift' \ - $(FMTFLAGS) . + --exclude '**.pb.swift' \ + $(FMTFLAGS) . test: xcodebuild test \ - -project $(PROJECT) \ - -scheme $(SCHEME) \ - -testPlan $(SCHEME) \ - -skipPackagePluginValidation \ - CODE_SIGNING_REQUIRED=NO \ - CODE_SIGNING_ALLOWED=NO \ - | LC_ALL="en_US.UTF-8" xcpretty + -project $(PROJECT) \ + -scheme $(SCHEME) \ + -testPlan $(SCHEME) \ + -skipPackagePluginValidation \ + CODE_SIGNING_REQUIRED=NO \ + CODE_SIGNING_ALLOWED=NO \ + | LC_ALL="en_US.UTF-8" xcpretty lint: swiftlint \ - --strict \ - --quiet $(LINTFLAGS) + --strict \ + --quiet $(LINTFLAGS) clean: xcodebuild clean \ - -project $(PROJECT) + -project $(PROJECT) proto: protoc --swift_out=. 'Coder Desktop/Proto/vpn.proto' From e0c3139f39eee38a959ddaed97a27bdd068711cf Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Mon, 16 Dec 2024 22:08:50 +1100 Subject: [PATCH 3/9] fmt --- .../Coder Desktop.xcodeproj/project.pbxproj | 82 +++++++++++-------- .../Preview Content/PreviewClient.swift | 2 +- Coder Desktop/Coder Desktop/SDK/Client.swift | 15 ++-- .../Coder Desktop/Views/LoginForm.swift | 22 ++--- Coder Desktop/Coder DesktopTests/Util.swift | 6 +- 5 files changed, 68 insertions(+), 59 deletions(-) diff --git a/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj b/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj index b3c2f19..adc4876 100644 --- a/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj +++ b/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj @@ -9,12 +9,14 @@ /* Begin PBXBuildFile section */ 961679332CFF117300B2B6DF /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 961679322CFF117300B2B6DF /* NetworkExtension.framework */; }; 9616793D2CFF117300B2B6DF /* com.coder.Coder-Desktop.VPN.systemextension in Embed System Extensions */ = {isa = PBXBuildFile; fileRef = 961679302CFF117300B2B6DF /* com.coder.Coder-Desktop.VPN.systemextension */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 961679532CFF207900B2B6DF /* SwiftProtobuf in Frameworks */ = {isa = PBXBuildFile; productRef = 961679522CFF207900B2B6DF /* SwiftProtobuf */; }; + 961679552CFF207900B2B6DF /* SwiftProtobufPluginLibrary in Frameworks */ = {isa = PBXBuildFile; productRef = 961679542CFF207900B2B6DF /* SwiftProtobufPluginLibrary */; }; + AA071D2C2D1041A7008D0B72 /* SwiftProtobuf in Frameworks */ = {isa = PBXBuildFile; productRef = 961679E22D03144900B2B6DF /* SwiftProtobuf */; }; + AA071D2D2D1041A7008D0B72 /* SwiftProtobufPluginLibrary in Frameworks */ = {isa = PBXBuildFile; productRef = 961679E42D03144C00B2B6DF /* SwiftProtobufPluginLibrary */; }; AA8BC3392D0060A900E1ABAA /* ViewInspector in Frameworks */ = {isa = PBXBuildFile; productRef = AA8BC3382D0060A900E1ABAA /* ViewInspector */; }; AA8BC33F2D0061F200E1ABAA /* FluidMenuBarExtra in Frameworks */ = {isa = PBXBuildFile; productRef = AA8BC33E2D0061F200E1ABAA /* FluidMenuBarExtra */; }; AA8BC4CF2D00A4B700E1ABAA /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = AA8BC4CE2D00A4B700E1ABAA /* KeychainAccess */; }; AAD720D02D0816B200F6304D /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = AAD720CF2D0816B200F6304D /* Alamofire */; }; - 961679532CFF207900B2B6DF /* SwiftProtobuf in Frameworks */ = {isa = PBXBuildFile; productRef = 961679522CFF207900B2B6DF /* SwiftProtobuf */; }; - 961679552CFF207900B2B6DF /* SwiftProtobufPluginLibrary in Frameworks */ = {isa = PBXBuildFile; productRef = 961679542CFF207900B2B6DF /* SwiftProtobufPluginLibrary */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -86,6 +88,13 @@ ); target = 9616792F2CFF117300B2B6DF /* VPN */; }; + AA071D842D1041E9008D0B72 /* Exceptions for "Proto" folder in "Coder Desktop" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + vpn.proto, + ); + target = 961678FB2CFF100D00B2B6DF /* Coder Desktop */; + }; /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ @@ -115,6 +124,7 @@ 961679432CFF149000B2B6DF /* Proto */ = { isa = PBXFileSystemSynchronizedRootGroup; exceptions = ( + AA071D842D1041E9008D0B72 /* Exceptions for "Proto" folder in "Coder Desktop" target */, 961679472CFF14EA00B2B6DF /* Exceptions for "Proto" folder in "VPN" target */, ); path = Proto; @@ -159,9 +169,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 961679E52D03144C00B2B6DF /* SwiftProtobufPluginLibrary in Frameworks */, + AA071D2D2D1041A7008D0B72 /* SwiftProtobufPluginLibrary in Frameworks */, + AA071D2C2D1041A7008D0B72 /* SwiftProtobuf in Frameworks */, 961679332CFF117300B2B6DF /* NetworkExtension.framework in Frameworks */, - 961679E32D03144900B2B6DF /* SwiftProtobuf in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -490,15 +500,15 @@ target = 9616792F2CFF117300B2B6DF /* VPN */; targetProxy = 9616793B2CFF117300B2B6DF /* PBXContainerItemProxy */; }; - AA8BC33C2D0060E700E1ABAA /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - productRef = AA8BC33B2D0060E700E1ABAA /* SwiftLintBuildToolPlugin */; - }; 961679DE2D030E1D00B2B6DF /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 961678FB2CFF100D00B2B6DF /* Coder Desktop */; targetProxy = 961679DD2D030E1D00B2B6DF /* PBXContainerItemProxy */; }; + AA8BC33C2D0060E700E1ABAA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + productRef = AA8BC33B2D0060E700E1ABAA /* SwiftLintBuildToolPlugin */; + }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ @@ -894,6 +904,14 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ + 961679512CFF207900B2B6DF /* XCRemoteSwiftPackageReference "swift-protobuf" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/apple/swift-protobuf.git"; + requirement = { + kind = exactVersion; + version = 1.28.2; + }; + }; AA8BC3372D00609700E1ABAA /* XCRemoteSwiftPackageReference "ViewInspector" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/nalexn/ViewInspector"; @@ -934,17 +952,29 @@ minimumVersion = 5.10.2; }; }; - 961679512CFF207900B2B6DF /* XCRemoteSwiftPackageReference "swift-protobuf" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/apple/swift-protobuf.git"; - requirement = { - kind = exactVersion; - version = 1.28.2; - }; - }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 961679522CFF207900B2B6DF /* SwiftProtobuf */ = { + isa = XCSwiftPackageProductDependency; + package = 961679512CFF207900B2B6DF /* XCRemoteSwiftPackageReference "swift-protobuf" */; + productName = SwiftProtobuf; + }; + 961679542CFF207900B2B6DF /* SwiftProtobufPluginLibrary */ = { + isa = XCSwiftPackageProductDependency; + package = 961679512CFF207900B2B6DF /* XCRemoteSwiftPackageReference "swift-protobuf" */; + productName = SwiftProtobufPluginLibrary; + }; + 961679E22D03144900B2B6DF /* SwiftProtobuf */ = { + isa = XCSwiftPackageProductDependency; + package = 961679512CFF207900B2B6DF /* XCRemoteSwiftPackageReference "swift-protobuf" */; + productName = SwiftProtobuf; + }; + 961679E42D03144C00B2B6DF /* SwiftProtobufPluginLibrary */ = { + isa = XCSwiftPackageProductDependency; + package = 961679512CFF207900B2B6DF /* XCRemoteSwiftPackageReference "swift-protobuf" */; + productName = SwiftProtobufPluginLibrary; + }; AA8BC3382D0060A900E1ABAA /* ViewInspector */ = { isa = XCSwiftPackageProductDependency; package = AA8BC3372D00609700E1ABAA /* XCRemoteSwiftPackageReference "ViewInspector" */; @@ -970,26 +1000,6 @@ package = AAD720CE2D0816B200F6304D /* XCRemoteSwiftPackageReference "Alamofire" */; productName = Alamofire; }; - 961679522CFF207900B2B6DF /* SwiftProtobuf */ = { - isa = XCSwiftPackageProductDependency; - package = 961679512CFF207900B2B6DF /* XCRemoteSwiftPackageReference "swift-protobuf" */; - productName = SwiftProtobuf; - }; - 961679542CFF207900B2B6DF /* SwiftProtobufPluginLibrary */ = { - isa = XCSwiftPackageProductDependency; - package = 961679512CFF207900B2B6DF /* XCRemoteSwiftPackageReference "swift-protobuf" */; - productName = SwiftProtobufPluginLibrary; - }; - 961679E22D03144900B2B6DF /* SwiftProtobuf */ = { - isa = XCSwiftPackageProductDependency; - package = 961679512CFF207900B2B6DF /* XCRemoteSwiftPackageReference "swift-protobuf" */; - productName = SwiftProtobuf; - }; - 961679E42D03144C00B2B6DF /* SwiftProtobufPluginLibrary */ = { - isa = XCSwiftPackageProductDependency; - package = 961679512CFF207900B2B6DF /* XCRemoteSwiftPackageReference "swift-protobuf" */; - productName = SwiftProtobufPluginLibrary; - }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 961678F42CFF100D00B2B6DF /* Project object */; diff --git a/Coder Desktop/Coder Desktop/Preview Content/PreviewClient.swift b/Coder Desktop/Coder Desktop/Preview Content/PreviewClient.swift index 336df0c..692dc27 100644 --- a/Coder Desktop/Coder Desktop/Preview Content/PreviewClient.swift +++ b/Coder Desktop/Coder Desktop/Preview Content/PreviewClient.swift @@ -1,5 +1,5 @@ -import SwiftUI import Alamofire +import SwiftUI struct PreviewClient: Client { init(url _: URL, token _: String? = nil) {} diff --git a/Coder Desktop/Coder Desktop/SDK/Client.swift b/Coder Desktop/Coder Desktop/SDK/Client.swift index 8f5e2c0..d3a1fdd 100644 --- a/Coder Desktop/Coder Desktop/SDK/Client.swift +++ b/Coder Desktop/Coder Desktop/SDK/Client.swift @@ -36,9 +36,9 @@ struct CoderClient: Client { headers: headers ).serializingData().response switch out.result { - case .success(let data): + case let .success(data): return HTTPResponse(resp: out.response!, data: data, req: out.request) - case .failure(let error): + case let .failure(error): throw ClientError.reqError(error) } } @@ -55,9 +55,9 @@ struct CoderClient: Client { headers: headers ).serializingData().response switch out.result { - case .success(let data): + case let .success(data): return HTTPResponse(resp: out.response!, data: data, req: out.request) - case .failure(let error): + case let .failure(error): throw ClientError.reqError(error) } } @@ -80,7 +80,6 @@ struct CoderClient: Client { enum Headers { static let sessionToken = "Coder-Session-Token" } - } struct HTTPResponse { @@ -130,11 +129,11 @@ enum ClientError: Error { var description: String { switch self { - case .apiError(let error): + case let .apiError(error): return error.description - case .reqError(let error): + case let .reqError(error): return error.localizedDescription - case .unexpectedResponse(let data): + case let .unexpectedResponse(data): return "Unexpected response: \(data)" } } diff --git a/Coder Desktop/Coder Desktop/Views/LoginForm.swift b/Coder Desktop/Coder Desktop/Views/LoginForm.swift index f10a88c..7ed7d9e 100644 --- a/Coder Desktop/Coder Desktop/Views/LoginForm.swift +++ b/Coder Desktop/Coder Desktop/Views/LoginForm.swift @@ -40,18 +40,18 @@ struct LoginForm: View { baseAccessURL = session.baseAccessURL?.absoluteString ?? baseAccessURL sessionToken = "" }.padding(.vertical, 35) - .alert("Error", isPresented: Binding( - get: { loginError != nil }, - set: { isPresented in - if !isPresented { - loginError = nil - } + .alert("Error", isPresented: Binding( + get: { loginError != nil }, + set: { isPresented in + if !isPresented { + loginError = nil } - )) { - Button("OK", role: .cancel) {}.keyboardShortcut(.defaultAction) - } message: { - Text(loginError?.description ?? "") } + )) { + Button("OK", role: .cancel) {}.keyboardShortcut(.defaultAction) + } message: { + Text(loginError?.description ?? "") + } }.padding() .frame(width: 450, height: 220) .disabled(loading) @@ -171,7 +171,7 @@ enum LoginError { switch self { case .invalidURL: return "Invalid URL" - case .failedAuth(let err): + case let .failedAuth(err): return "Could not authenticate with Coder deployment:\n\(err.description)" } } diff --git a/Coder Desktop/Coder DesktopTests/Util.swift b/Coder Desktop/Coder DesktopTests/Util.swift index f902dce..b6adcb4 100644 --- a/Coder Desktop/Coder DesktopTests/Util.swift +++ b/Coder Desktop/Coder DesktopTests/Util.swift @@ -67,10 +67,10 @@ struct MockClient: Client { } struct MockErrorClient: Client { - init(url: URL, token: String?) {} - func user(_ ident: String) async throws(ClientError) -> Coder_Desktop.User { + init(url _: URL, token _: String?) {} + func user(_: String) async throws(ClientError) -> Coder_Desktop.User { throw ClientError.reqError(.explicitlyCancelled) } } -extension Inspection: @unchecked Sendable, @retroactive InspectionEmissary { } +extension Inspection: @unchecked Sendable, @retroactive InspectionEmissary {} From 0dd1827c5d958685e7c9eb1bce4539d402b4b6a1 Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Mon, 16 Dec 2024 22:17:02 +1100 Subject: [PATCH 4/9] include proto tests --- Coder Desktop/Coder Desktop.xctestplan | 8 +++++++- Coder Desktop/ProtoTests/ProtoTests.swift | 15 +++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/Coder Desktop/Coder Desktop.xctestplan b/Coder Desktop/Coder Desktop.xctestplan index d1197e5..444d6b9 100644 --- a/Coder Desktop/Coder Desktop.xctestplan +++ b/Coder Desktop/Coder Desktop.xctestplan @@ -17,7 +17,13 @@ }, "testTargets" : [ { - "parallelizable" : true, + "target" : { + "containerPath" : "container:Coder Desktop.xcodeproj", + "identifier" : "961679D82D030E1D00B2B6DF", + "name" : "ProtoTests" + } + }, + { "target" : { "containerPath" : "container:Coder Desktop.xcodeproj", "identifier" : "9616790E2CFF100E00B2B6DF", diff --git a/Coder Desktop/ProtoTests/ProtoTests.swift b/Coder Desktop/ProtoTests/ProtoTests.swift index 1df9ae0..5a71cae 100644 --- a/Coder Desktop/ProtoTests/ProtoTests.swift +++ b/Coder Desktop/ProtoTests/ProtoTests.swift @@ -156,8 +156,7 @@ struct HandshakerTests { #expect(versionMgr == tc.result) } - @Test - func incompatible() async throws { + @Test func incompatible() async throws { let uutTun = Handshaker( writeFD: pipeTM.fileHandleForWriting, dispatch: dispatchT, queue: queue, role: .tunnel, versions: [ProtoVersion(1, 8)] @@ -201,8 +200,7 @@ struct OneSidedHandshakerTests { ) } - @Test() - func badPreamble() async throws { + @Test func badPreamble() async throws { let taskTun = Task { try await uut.handshake() } @@ -214,8 +212,7 @@ struct OneSidedHandshakerTests { } } - @Test(.timeLimit(.minutes(1))) - func badRole() async throws { + @Test func badRole() async throws { let taskTun = Task { try await uut.handshake() } @@ -227,8 +224,7 @@ struct OneSidedHandshakerTests { } } - @Test(.timeLimit(.minutes(1))) - func badVersion() async throws { + @Test func badVersion() async throws { let taskTun = Task { try await uut.handshake() } @@ -240,8 +236,7 @@ struct OneSidedHandshakerTests { } } - @Test(.timeLimit(.minutes(1))) - func mainline() async throws { + @Test func mainline() async throws { let taskTun = Task { let v = try await uut.handshake() // close our pipe so that `readToEnd()` below succeeds. From 43dd6d45f02a48e01d3da4aa9997c91fe53cf1f5 Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Mon, 16 Dec 2024 22:51:00 +1100 Subject: [PATCH 5/9] upgrade all to swift 6 --- .../Coder Desktop.xcodeproj/project.pbxproj | 36 +++++++++++++------ .../xcschemes/Coder Desktop.xcscheme | 2 +- .../xcschemes/ProtoTests.xcscheme | 11 +----- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj b/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj index adc4876..e5ae28b 100644 --- a/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj +++ b/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj @@ -355,7 +355,7 @@ attributes = { BuildIndependentTargetsInParallel = 1; LastSwiftUpdateCheck = 1610; - LastUpgradeCheck = 1610; + LastUpgradeCheck = 1620; TargetAttributes = { 961678FB2CFF100D00B2B6DF = { CreatedOnToolsVersion = 16.1; @@ -546,6 +546,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -572,6 +573,7 @@ SDKROOT = macosx; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 6.0; }; name = Debug; }; @@ -609,6 +611,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -627,6 +630,7 @@ MTL_FAST_MATH = YES; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_VERSION = 6.0; }; name = Release; }; @@ -639,6 +643,7 @@ CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = "\"Coder Desktop/Preview Content\""; DEVELOPMENT_TEAM = 4399GN35BJ; ENABLE_HARDENED_RUNTIME = YES; @@ -655,7 +660,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; }; name = Debug; }; @@ -668,6 +673,7 @@ CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = "\"Coder Desktop/Preview Content\""; DEVELOPMENT_TEAM = 4399GN35BJ; ENABLE_HARDENED_RUNTIME = YES; @@ -684,7 +690,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; }; name = Release; }; @@ -694,6 +700,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 4399GN35BJ; GENERATE_INFOPLIST_FILE = YES; MACOSX_DEPLOYMENT_TARGET = 14.0; @@ -701,7 +708,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-DesktopTests"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Coder Desktop.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Coder Desktop"; }; name = Debug; @@ -712,6 +719,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 4399GN35BJ; GENERATE_INFOPLIST_FILE = YES; MACOSX_DEPLOYMENT_TARGET = 14.0; @@ -719,7 +727,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-DesktopTests"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Coder Desktop.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Coder Desktop"; }; name = Release; @@ -729,6 +737,7 @@ buildSettings = { CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 4399GN35BJ; GENERATE_INFOPLIST_FILE = YES; MACOSX_DEPLOYMENT_TARGET = 14.6; @@ -736,7 +745,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-DesktopUITests"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TEST_TARGET_NAME = "Coder Desktop"; }; name = Debug; @@ -746,6 +755,7 @@ buildSettings = { CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 4399GN35BJ; GENERATE_INFOPLIST_FILE = YES; MACOSX_DEPLOYMENT_TARGET = 14.6; @@ -753,7 +763,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-DesktopUITests"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TEST_TARGET_NAME = "Coder Desktop"; }; name = Release; @@ -764,6 +774,7 @@ CODE_SIGN_ENTITLEMENTS = VPN/VPN.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 4399GN35BJ; ENABLE_HARDENED_RUNTIME = YES; GENERATE_INFOPLIST_FILE = YES; @@ -780,7 +791,7 @@ PRODUCT_NAME = "$(inherited)"; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; }; name = Debug; }; @@ -790,6 +801,7 @@ CODE_SIGN_ENTITLEMENTS = VPN/VPN.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 4399GN35BJ; ENABLE_HARDENED_RUNTIME = YES; GENERATE_INFOPLIST_FILE = YES; @@ -806,7 +818,7 @@ PRODUCT_NAME = "$(inherited)"; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; }; name = Release; }; @@ -816,13 +828,14 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 4399GN35BJ; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop.ProtoTests"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Coder Desktop.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Coder Desktop"; }; name = Debug; @@ -833,13 +846,14 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 4399GN35BJ; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop.ProtoTests"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Coder Desktop.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Coder Desktop"; }; name = Release; diff --git a/Coder Desktop/Coder Desktop.xcodeproj/xcshareddata/xcschemes/Coder Desktop.xcscheme b/Coder Desktop/Coder Desktop.xcodeproj/xcshareddata/xcschemes/Coder Desktop.xcscheme index 7671268..7439c84 100644 --- a/Coder Desktop/Coder Desktop.xcodeproj/xcshareddata/xcschemes/Coder Desktop.xcscheme +++ b/Coder Desktop/Coder Desktop.xcodeproj/xcshareddata/xcschemes/Coder Desktop.xcscheme @@ -1,6 +1,6 @@ - - - - From f8840b56ed5acafd05242d3b2fdb61b672cba88a Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Tue, 17 Dec 2024 14:51:07 +1100 Subject: [PATCH 6/9] temporarily back to swift 5 --- .../Coder Desktop.xcodeproj/project.pbxproj | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj b/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj index e5ae28b..ad092e3 100644 --- a/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj +++ b/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj @@ -573,7 +573,7 @@ SDKROOT = macosx; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 6.0; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -660,7 +660,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 6.0; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -690,7 +690,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 6.0; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -708,7 +708,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-DesktopTests"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 6.0; + SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Coder Desktop.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Coder Desktop"; }; name = Debug; @@ -727,7 +727,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-DesktopTests"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 6.0; + SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Coder Desktop.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Coder Desktop"; }; name = Release; @@ -745,7 +745,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-DesktopUITests"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 6.0; + SWIFT_VERSION = 5.0; TEST_TARGET_NAME = "Coder Desktop"; }; name = Debug; @@ -763,7 +763,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-DesktopUITests"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 6.0; + SWIFT_VERSION = 5.0; TEST_TARGET_NAME = "Coder Desktop"; }; name = Release; @@ -791,7 +791,7 @@ PRODUCT_NAME = "$(inherited)"; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 6.0; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -818,7 +818,7 @@ PRODUCT_NAME = "$(inherited)"; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 6.0; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -835,7 +835,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop.ProtoTests"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 6.0; + SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Coder Desktop.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Coder Desktop"; }; name = Debug; @@ -853,7 +853,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop.ProtoTests"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 6.0; + SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Coder Desktop.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Coder Desktop"; }; name = Release; From 6cf9394e2ef502cd9ec41d9163c3a5811e16b9c2 Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Tue, 17 Dec 2024 14:52:13 +1100 Subject: [PATCH 7/9] temporarily back to swift 5 part 2 --- Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj b/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj index ad092e3..7e9cfce 100644 --- a/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj +++ b/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj @@ -630,7 +630,7 @@ MTL_FAST_MATH = YES; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_VERSION = 6.0; + SWIFT_VERSION = 5.0; }; name = Release; }; From c1e187de44c58111b16adf9f23e29721769a5b91 Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Tue, 17 Dec 2024 17:25:36 +1100 Subject: [PATCH 8/9] Coder Desktop swift 6 conformance --- Coder Desktop/Coder Desktop/About.swift | 1 + .../Coder Desktop/Coder_DesktopApp.swift | 1 + .../Preview Content/PreviewVPN.swift | 29 +++++++------------ Coder Desktop/Coder Desktop/SDK/Client.swift | 2 +- Coder Desktop/Coder Desktop/VPNService.swift | 1 + .../Coder DesktopTests/AgentsTests.swift | 5 +--- .../Coder DesktopTests/LoginFormTests.swift | 7 +---- Coder Desktop/Coder DesktopTests/Util.swift | 3 +- .../Coder DesktopTests/VPNMenuTests.swift | 6 +--- .../Coder DesktopTests/VPNStateTests.swift | 6 +--- 10 files changed, 19 insertions(+), 42 deletions(-) diff --git a/Coder Desktop/Coder Desktop/About.swift b/Coder Desktop/Coder Desktop/About.swift index 7f040a1..4bff9d6 100644 --- a/Coder Desktop/Coder Desktop/About.swift +++ b/Coder Desktop/Coder Desktop/About.swift @@ -30,6 +30,7 @@ enum About { return coder } + @MainActor static func open() { #if compiler(>=5.9) && canImport(AppKit) if #available(macOS 14, *) { diff --git a/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift b/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift index 26e5ab4..408722b 100644 --- a/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift +++ b/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift @@ -17,6 +17,7 @@ struct DesktopApp: App { } } +@MainActor class AppDelegate: NSObject, NSApplicationDelegate { private var menuBarExtra: FluidMenuBarExtra? let vpn: PreviewVPN diff --git a/Coder Desktop/Coder Desktop/Preview Content/PreviewVPN.swift b/Coder Desktop/Coder Desktop/Preview Content/PreviewVPN.swift index 2599db4..0e08bf9 100644 --- a/Coder Desktop/Coder Desktop/Preview Content/PreviewVPN.swift +++ b/Coder Desktop/Coder Desktop/Preview Content/PreviewVPN.swift @@ -1,6 +1,7 @@ import SwiftUI -class PreviewVPN: Coder_Desktop.VPNService { +@MainActor +final class PreviewVPN: Coder_Desktop.VPNService { @Published var state: Coder_Desktop.VPNServiceState = .disabled @Published var agents: [Coder_Desktop.Agent] = [ Agent(id: UUID(), name: "dogfood2", status: .error, copyableDNS: "asdf.coder", workspaceName: "dogfood2"), @@ -22,36 +23,26 @@ class PreviewVPN: Coder_Desktop.VPNService { self.shouldFail = shouldFail } - private func setState(_ newState: Coder_Desktop.VPNServiceState) async { - await MainActor.run { - self.state = newState - } - } - func start() async { - await setState(.connecting) + state = .connecting do { - try await Task.sleep(for: .seconds(1)) + try await Task.sleep(for: .seconds(10)) } catch { - await setState(.failed(.exampleError)) + state = .failed(.exampleError) return } - if shouldFail { - await setState(.failed(.exampleError)) - } else { - await setState(.connected) - } + state = shouldFail ? .failed(.exampleError) : .connected } func stop() async { guard state == .connected else { return } - await setState(.disconnecting) + state = .disconnecting do { - try await Task.sleep(for: .seconds(1)) + try await Task.sleep(for: .seconds(10)) } catch { - await setState(.failed(.exampleError)) + state = .failed(.exampleError) return } - await setState(.disabled) + state = .disabled } } diff --git a/Coder Desktop/Coder Desktop/SDK/Client.swift b/Coder Desktop/Coder Desktop/SDK/Client.swift index d3a1fdd..39a4023 100644 --- a/Coder Desktop/Coder Desktop/SDK/Client.swift +++ b/Coder Desktop/Coder Desktop/SDK/Client.swift @@ -22,7 +22,7 @@ struct CoderClient: Client { return enc }() - func request( + func request( _ path: String, method: HTTPMethod, body: T? = nil diff --git a/Coder Desktop/Coder Desktop/VPNService.swift b/Coder Desktop/Coder Desktop/VPNService.swift index 3f535c7..f19b4b9 100644 --- a/Coder Desktop/Coder Desktop/VPNService.swift +++ b/Coder Desktop/Coder Desktop/VPNService.swift @@ -1,5 +1,6 @@ import SwiftUI +@MainActor protocol VPNService: ObservableObject { var state: VPNServiceState { get } var agents: [Agent] { get } diff --git a/Coder Desktop/Coder DesktopTests/AgentsTests.swift b/Coder Desktop/Coder DesktopTests/AgentsTests.swift index 8322fe2..300d3e8 100644 --- a/Coder Desktop/Coder DesktopTests/AgentsTests.swift +++ b/Coder Desktop/Coder DesktopTests/AgentsTests.swift @@ -3,6 +3,7 @@ import SwiftUI import Testing import ViewInspector +@MainActor @Suite(.timeLimit(.minutes(1))) struct AgentsTests { let vpn: MockVPNService @@ -30,7 +31,6 @@ struct AgentsTests { } @Test - @MainActor func agentsWhenVPNOff() throws { vpn.state = .disabled @@ -40,7 +40,6 @@ struct AgentsTests { } @Test - @MainActor func agentsWhenVPNOn() throws { vpn.state = .connected vpn.agents = createMockAgents(count: Theme.defaultVisibleAgents + 2) @@ -51,7 +50,6 @@ struct AgentsTests { } @Test - @MainActor func showAllToggle() async throws { vpn.state = .connected vpn.agents = createMockAgents(count: 7) @@ -78,7 +76,6 @@ struct AgentsTests { } @Test - @MainActor func noToggleFewAgents() throws { vpn.state = .connected vpn.agents = createMockAgents(count: 3) diff --git a/Coder Desktop/Coder DesktopTests/LoginFormTests.swift b/Coder Desktop/Coder DesktopTests/LoginFormTests.swift index b81f5c0..ae77b5c 100644 --- a/Coder Desktop/Coder DesktopTests/LoginFormTests.swift +++ b/Coder Desktop/Coder DesktopTests/LoginFormTests.swift @@ -3,6 +3,7 @@ import SwiftUI import Testing import ViewInspector +@MainActor @Suite(.timeLimit(.minutes(1))) struct LoginTests { let session: MockSession @@ -16,7 +17,6 @@ struct LoginTests { } @Test - @MainActor func testInitialView() async throws { try await ViewHosting.host(view) { try await sut.inspection.inspect { view in @@ -28,7 +28,6 @@ struct LoginTests { } @Test - @MainActor func testInvalidServerURL() async throws { try await ViewHosting.host(view) { try await sut.inspection.inspect { view in @@ -40,7 +39,6 @@ struct LoginTests { } @Test - @MainActor func testValidServerURL() async throws { try await ViewHosting.host(view) { try await sut.inspection.inspect { view in @@ -55,7 +53,6 @@ struct LoginTests { } @Test - @MainActor func testBackButton() async throws { try await ViewHosting.host(view) { try await sut.inspection.inspect { view in @@ -70,7 +67,6 @@ struct LoginTests { } @Test - @MainActor func testFailedAuthentication() async throws { let login = LoginForm() @@ -87,7 +83,6 @@ struct LoginTests { } @Test - @MainActor func testSuccessfulLogin() async throws { try await ViewHosting.host(view) { try await sut.inspection.inspect { view in diff --git a/Coder Desktop/Coder DesktopTests/Util.swift b/Coder Desktop/Coder DesktopTests/Util.swift index b6adcb4..1e8ccc6 100644 --- a/Coder Desktop/Coder DesktopTests/Util.swift +++ b/Coder Desktop/Coder DesktopTests/Util.swift @@ -3,6 +3,7 @@ import Combine import SwiftUI import ViewInspector +@MainActor class MockVPNService: VPNService, ObservableObject { @Published var state: Coder_Desktop.VPNServiceState = .disabled @Published var baseAccessURL: URL = .init(string: "https://dev.coder.com")! @@ -10,13 +11,11 @@ class MockVPNService: VPNService, ObservableObject { var onStart: (() async -> Void)? var onStop: (() async -> Void)? - @MainActor func start() async { state = .connecting await onStart?() } - @MainActor func stop() async { state = .disconnecting await onStop?() diff --git a/Coder Desktop/Coder DesktopTests/VPNMenuTests.swift b/Coder Desktop/Coder DesktopTests/VPNMenuTests.swift index 1c711ae..2292801 100644 --- a/Coder Desktop/Coder DesktopTests/VPNMenuTests.swift +++ b/Coder Desktop/Coder DesktopTests/VPNMenuTests.swift @@ -3,6 +3,7 @@ import SwiftUI import Testing import ViewInspector +@MainActor @Suite(.timeLimit(.minutes(1))) struct VPNMenuTests { let vpn: MockVPNService @@ -18,7 +19,6 @@ struct VPNMenuTests { } @Test - @MainActor func testVPNLoggedOut() async throws { session.hasSession = false @@ -33,7 +33,6 @@ struct VPNMenuTests { } @Test - @MainActor func testStartStopCalled() async throws { try await ViewHosting.host(view) { try await sut.inspection.inspect { view in @@ -58,7 +57,6 @@ struct VPNMenuTests { } @Test - @MainActor func testVPNDisabledWhileConnecting() async throws { vpn.state = .disabled @@ -79,7 +77,6 @@ struct VPNMenuTests { } @Test - @MainActor func testVPNDisabledWhileDisconnecting() async throws { vpn.state = .disabled @@ -106,7 +103,6 @@ struct VPNMenuTests { } @Test - @MainActor func testOffWhenFailed() async throws { try await ViewHosting.host(view) { try await sut.inspection.inspect { view in diff --git a/Coder Desktop/Coder DesktopTests/VPNStateTests.swift b/Coder Desktop/Coder DesktopTests/VPNStateTests.swift index 07f17e5..89c241d 100644 --- a/Coder Desktop/Coder DesktopTests/VPNStateTests.swift +++ b/Coder Desktop/Coder DesktopTests/VPNStateTests.swift @@ -3,6 +3,7 @@ import SwiftUI import Testing import ViewInspector +@MainActor @Suite(.timeLimit(.minutes(1))) struct VPNStateTests { let vpn: MockVPNService @@ -16,7 +17,6 @@ struct VPNStateTests { } @Test - @MainActor func testDisabledState() async throws { vpn.state = .disabled @@ -30,7 +30,6 @@ struct VPNStateTests { } @Test - @MainActor func testConnectingState() async throws { vpn.state = .connecting @@ -43,7 +42,6 @@ struct VPNStateTests { } @Test - @MainActor func testDisconnectingState() async throws { vpn.state = .disconnecting @@ -56,7 +54,6 @@ struct VPNStateTests { } @Test - @MainActor func testFailedState() async throws { vpn.state = .failed(.exampleError) @@ -69,7 +66,6 @@ struct VPNStateTests { } @Test - @MainActor func testDefaultState() async throws { vpn.state = .connected From 94b7faedec681de4c7e171a00aa77dd3b3cf5a85 Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Wed, 18 Dec 2024 14:35:43 +1100 Subject: [PATCH 9/9] bump swift 6 compiling targets --- .../Coder Desktop.xcodeproj/project.pbxproj | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj b/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj index 7e9cfce..223b83f 100644 --- a/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj +++ b/Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj @@ -573,7 +573,7 @@ SDKROOT = macosx; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; }; name = Debug; }; @@ -630,7 +630,7 @@ MTL_FAST_MATH = YES; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; }; name = Release; }; @@ -708,7 +708,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-DesktopTests"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Coder Desktop.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Coder Desktop"; }; name = Debug; @@ -727,7 +727,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-DesktopTests"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Coder Desktop.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Coder Desktop"; }; name = Release; @@ -745,7 +745,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-DesktopUITests"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TEST_TARGET_NAME = "Coder Desktop"; }; name = Debug; @@ -763,7 +763,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-DesktopUITests"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; TEST_TARGET_NAME = "Coder Desktop"; }; name = Release; @@ -791,7 +791,7 @@ PRODUCT_NAME = "$(inherited)"; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; }; name = Debug; }; @@ -818,7 +818,7 @@ PRODUCT_NAME = "$(inherited)"; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; }; name = Release; };