Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions .github/dependabot.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ updates:
interval: "weekly"
time: "06:00"
timezone: "America/Chicago"
cooldown:
default-days: 7
labels: []
commit-message:
prefix: "ci"
Expand Down
5 changes: 5 additions & 0 deletions .github/zizmor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
rules:
# `uses: $/...` isn't understood by actionlint yet, so keep using `./...`
# until https://github.com/rhysd/actionlint/issues/711 is resolved.
self-repository:
disable: true
2 changes: 1 addition & 1 deletion Coder-Desktop/.swiftformat
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
--selfrequired log,info,error,debug,critical,fault
--exclude **.pb.swift,**.grpc.swift
--condassignment always
--disable unusedArguments
--disable unusedArguments,wrapIfStatementBodies,wrapPropertyBodies
2 changes: 1 addition & 1 deletion Coder-Desktop/Coder-Desktop/About.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import SwiftUI

enum About {
public static let repo: String = "https://github.com/coder/coder-desktop-macos"
static let repo: String = "https://github.com/coder/coder-desktop-macos"
private static var credits: NSAttributedString {
let coder = NSMutableAttributedString(
string: "Coder.com",
Expand Down
6 changes: 3 additions & 3 deletions Coder-Desktop/Coder-Desktop/AppHelperXPCClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import NetworkExtension
import os
import VPNLib

// This is the client for the app to communicate with the privileged helper.
/// This is the client for the app to communicate with the privileged helper.
@objc final class HelperXPCClient: NSObject, @unchecked Sendable {
private var svc: CoderVPNService
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "HelperXPCClient")
Expand Down Expand Up @@ -43,7 +43,7 @@ import VPNLib
return connection
}

// Establishes a connection to the Helper, so it can send messages back.
/// Establishes a connection to the Helper, so it can send messages back.
func ping() async throws {
let conn = connect()
return try await withCheckedThrowingContinuation { continuation in
Expand Down Expand Up @@ -83,7 +83,7 @@ import VPNLib
}
}

// These methods are called by the Helper over XPC
/// These methods are called by the Helper over XPC
extension HelperXPCClient: AppXPCInterface {
func onPeerUpdate(_ diff: Data, reply: @escaping () -> Void) {
let reply = CompletionWrapper(reply)
Expand Down
22 changes: 11 additions & 11 deletions Coder-Desktop/Coder-Desktop/Coder_DesktopApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
NotificationCenter.default.removeObserver(self)
}

// This function MUST eventually call `NSApp.reply(toApplicationShouldTerminate: true)`
// or return `.terminateNow`
/// This function MUST eventually call `NSApp.reply(toApplicationShouldTerminate: true)`
/// or return `.terminateNow`
func applicationShouldTerminate(_: NSApplication) -> NSApplication.TerminateReply {
Task {
async let vpnTask: Void = {
Expand Down Expand Up @@ -214,15 +214,15 @@ class AppDelegate: NSObject, NSApplicationDelegate {
alert.addButton(withTitle: "OK")
let response = alert.runModal()
state.skipHiddenIconAlert = suppressCheckbox.state == .on
if #available(macOS 26, *), response == .alertFirstButtonReturn {
// We'll need to ensure this continues to work in future macOS versions
if !NSWorkspace.shared.open(
URL(string: "x-apple.systempreferences:com.apple.ControlCenter-Settings.extension?MenuBar")!
) {
NSWorkspace.shared.open(
URL(string: "x-apple.systempreferences:com.apple.ControlCenter-Settings.extension")!
)
}
if #available(macOS 26, *), response == .alertFirstButtonReturn,
// We'll need to ensure this continues to work in future macOS versions
!NSWorkspace.shared.open(
URL(string: "x-apple.systempreferences:com.apple.ControlCenter-Settings.extension?MenuBar")!
)
{
NSWorkspace.shared.open(
URL(string: "x-apple.systempreferences:com.apple.ControlCenter-Settings.extension")!
)
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions Coder-Desktop/Coder-Desktop/Notifications.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ extension AppDelegate: UNUserNotificationCenterDelegate {
)
}

// This function is required for notifications to appear as banners whilst the app is running.
// We're effectively forwarding the notification back to the OS
/// This function is required for notifications to appear as banners whilst the app is running.
/// We're effectively forwarding the notification back to the OS
nonisolated func userNotificationCenter(
_: UNUserNotificationCenter,
willPresent _: UNNotification
Expand Down
36 changes: 15 additions & 21 deletions Coder-Desktop/Coder-Desktop/State.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class AppState: ObservableObject {

let dangerousDisableCoderSignatureValidation: Bool

// Stored in UserDefaults
/// Stored in UserDefaults
@Published private(set) var hasSession: Bool {
didSet {
guard persistent else { return }
Expand All @@ -31,15 +31,15 @@ class AppState: ObservableObject {

static let defaultHostnameSuffix: String = "coder"

// Stored in Keychain
/// Stored in Keychain
@Published private(set) var sessionToken: String? {
didSet {
guard persistent else { return }
keychainSet(sessionToken, for: Keys.sessionToken)
}
}

public var client: Client?
var client: Client?

@Published var useLiteralHeaders: Bool = UserDefaults.standard.bool(forKey: Keys.useLiteralHeaders) {
didSet {
Expand All @@ -57,7 +57,7 @@ class AppState: ObservableObject {
}
}

// Defaults to `true`
/// Defaults to `true`
@Published var stopVPNOnQuit: Bool = UserDefaults.standard.optionalBool(forKey: Keys.stopVPNOnQuit) ?? true {
didSet {
guard persistent else { return }
Expand Down Expand Up @@ -110,13 +110,13 @@ class AppState: ObservableObject {

private let onChange: ((NETunnelProviderProtocol?) -> Void)?

// reconfigure must be called when any property used to configure the VPN changes
public func reconfigure() {
/// reconfigure must be called when any property used to configure the VPN changes
func reconfigure() {
if let onChange { onChange(tunnelProviderProtocol()) }
}

public init(onChange: ((NETunnelProviderProtocol?) -> Void)? = nil,
persistent: Bool = true)
init(onChange: ((NETunnelProviderProtocol?) -> Void)? = nil,
persistent: Bool = true)
{
self.persistent = persistent
self.onChange = onChange
Expand Down Expand Up @@ -151,7 +151,7 @@ class AppState: ObservableObject {
}
}

public func login(baseAccessURL: URL, sessionToken: String) {
func login(baseAccessURL: URL, sessionToken: String) {
hasSession = true
self.baseAccessURL = baseAccessURL
self.sessionToken = sessionToken
Expand All @@ -164,7 +164,7 @@ class AppState: ObservableObject {
reconfigure()
}

public func handleTokenExpiry() async {
func handleTokenExpiry() async {
if hasSession {
do {
_ = try await client!.user("me")
Expand All @@ -186,13 +186,13 @@ class AppState: ObservableObject {
@Published var dashboardURL: URL?

private var refreshTask: Task<String?, Never>?
public func refreshDeploymentConfig() async {
func refreshDeploymentConfig() async {
// Client is non-nil if there's a sesssion
if hasSession, let client {
refreshTask?.cancel()

refreshTask = Task {
let res = try? await retry(floor: .milliseconds(100), ceil: .seconds(10)) {
try? await retry(floor: .milliseconds(100), ceil: .seconds(10)) {
do {
let config = try await client.agentConnectionInfoGeneric()
return config.hostname_suffix
Expand All @@ -201,7 +201,6 @@ class AppState: ObservableObject {
throw error
}
}
return res
}

hostnameSuffix = await refreshTask?.value ?? Self.defaultHostnameSuffix
Expand All @@ -213,7 +212,7 @@ class AppState: ObservableObject {
}
}

public func clearSession() {
func clearSession() {
hasSession = false
sessionToken = nil
dashboardURL = nil
Expand Down Expand Up @@ -258,11 +257,6 @@ struct LiteralHeader: Hashable, Identifiable, Equatable, Codable {
var id: String {
"\(name):\(value)"
}

init(name: String, value: String) {
self.name = name
self.value = value
}
}

extension LiteralHeader {
Expand All @@ -272,8 +266,8 @@ extension LiteralHeader {
}

extension UserDefaults {
// Unlike the exisitng `bool(forKey:)` method which returns `false` for both
// missing values this method can return `nil`.
/// Unlike the exisitng `bool(forKey:)` method which returns `false` for both
/// missing values this method can return `nil`.
func optionalBool(forKey key: String) -> Bool? {
guard object(forKey: key) != nil else {
return nil
Expand Down
6 changes: 3 additions & 3 deletions Coder-Desktop/Coder-Desktop/UpdaterService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import Sparkle
import SwiftUI

final class UpdaterService: NSObject, ObservableObject {
// The auto-updater can be entirely disabled by setting the
// `disableUpdater` UserDefaults key to `true`. This is designed for use in
// MDM configurations, where the value can be set to `true` permanently.
/// The auto-updater can be entirely disabled by setting the
/// `disableUpdater` UserDefaults key to `true`. This is designed for use in
/// MDM configurations, where the value can be set to `true` permanently.
let disabled: Bool = UserDefaults.standard.bool(forKey: Keys.disableUpdater)

@Published var canCheckForUpdates = true
Expand Down
14 changes: 7 additions & 7 deletions Coder-Desktop/Coder-Desktop/VPN/MenuState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ struct Agent: Identifiable, Equatable, Comparable, Hashable {
self.primaryHost = primaryHost
}

// Agents are sorted by status, and then by name
/// Agents are sorted by status, and then by name
static func < (lhs: Agent, rhs: Agent) -> Bool {
if lhs.status != rhs.status {
return lhs.status < rhs.status
Expand Down Expand Up @@ -113,7 +113,7 @@ enum AgentStatus: Int, Equatable, Comparable {
case no_recent_handshake = 3
case off = 4

public var description: String {
var description: String {
switch self {
case .okay: "Connected"
case .connecting: "Connecting..."
Expand All @@ -123,7 +123,7 @@ enum AgentStatus: Int, Equatable, Comparable {
}
}

public var color: Color {
var color: Color {
switch self {
case .okay: .green
case .high_latency: .yellow
Expand All @@ -150,15 +150,15 @@ struct Workspace: Identifiable, Equatable, Comparable {
struct VPNMenuState {
var agents: [UUID: Agent] = [:]
var workspaces: [UUID: Workspace] = [:]
// Upserted agents that don't belong to any known workspace, have no FQDNs,
// or have any invalid UUIDs.
/// Upserted agents that don't belong to any known workspace, have no FQDNs,
/// or have any invalid UUIDs.
var invalidAgents: [Vpn_Agent] = []

public func findAgent(workspaceID: UUID, name: String) -> Agent? {
func findAgent(workspaceID: UUID, name: String) -> Agent? {
agents.values.first { $0.wsID == workspaceID && $0.name.caseInsensitiveCompare(name) == .orderedSame }
}

public func findWorkspace(name: String) -> Workspace? {
func findWorkspace(name: String) -> Workspace? {
workspaces.values.first { $0.name.caseInsensitiveCompare(name) == .orderedSame }
}

Expand Down
6 changes: 3 additions & 3 deletions Coder-Desktop/Coder-Desktop/VPN/NetworkExtension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ enum NetworkExtensionState: Equatable {
/// An actor that handles configuring, enabling, and disabling the VPN tunnel via the
/// NetworkExtension APIs.
extension CoderVPNService {
// Attempts to load the NetworkExtension configuration, returning true if successful.
/// Attempts to load the NetworkExtension configuration, returning true if successful.
func loadNetworkExtensionConfig() async -> Bool {
do {
let tm = try await getTunnelManager()
Expand Down Expand Up @@ -126,6 +126,6 @@ extension CoderVPNService {
}
}

// we're going to mark NETunnelProviderManager as Sendable since there are official APIs that return
// it async.
/// we're going to mark NETunnelProviderManager as Sendable since there are official APIs that return
/// it async.
extension NETunnelProviderManager: @unchecked @retroactive Sendable {}
12 changes: 6 additions & 6 deletions Coder-Desktop/Coder-Desktop/VPN/VPNService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ enum VPNServiceError: Error, Equatable {
case systemExtensionError(SystemExtensionState)
case networkExtensionError(NetworkExtensionState)

public var description: String {
var description: String {
switch self {
case let .internalError(description):
"Internal Error: \(description)"
Expand All @@ -48,7 +48,7 @@ enum VPNServiceError: Error, Equatable {
}
}

public var localizedDescription: String { description }
var localizedDescription: String { description }
}

@MainActor
Expand Down Expand Up @@ -108,9 +108,9 @@ final class CoderVPNService: NSObject, VPNService {
var onStart: (() -> Void)?
var onFailure: ((VPNServiceError) -> Void)?

// systemExtnDelegate holds a reference to the SystemExtensionDelegate so that it doesn't get
// garbage collected while the OSSystemExtensionRequest is in flight, since the OS framework
// only stores a weak reference to the delegate.
/// systemExtnDelegate holds a reference to the SystemExtensionDelegate so that it doesn't get
/// garbage collected while the OSSystemExtensionRequest is in flight, since the OS framework
/// only stores a weak reference to the delegate.
var systemExtnDelegate: SystemExtensionDelegate<CoderVPNService>?

var serverAddress: String?
Expand Down Expand Up @@ -200,7 +200,7 @@ final class CoderVPNService: NSObject, VPNService {
}

extension CoderVPNService {
public func vpnDidUpdate(_ connection: NETunnelProviderSession) {
func vpnDidUpdate(_ connection: NETunnelProviderSession) {
switch (tunnelState, connection.status) {
// Any -> Disconnected: Update UI w/ error if present
case (_, .disconnected):
Expand Down
11 changes: 6 additions & 5 deletions Coder-Desktop/Coder-Desktop/VPN/VPNSystemExtension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,10 @@ class SystemExtensionDelegate<AsyncDelegate: SystemExtensionAsyncRecorder>:
{
private var logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "vpn-installer")
private var asyncDelegate: AsyncDelegate
// The `didFinishWithResult` function is called for both activation,
// deactivation, and replacement requests. The API provides no way to
// differentiate them. https://developer.apple.com/forums/thread/684021
// This tracks the last request type made, to handle them accordingly.
/// The `didFinishWithResult` function is called for both activation,
/// deactivation, and replacement requests. The API provides no way to
/// differentiate them. https://developer.apple.com/forums/thread/684021
/// This tracks the last request type made, to handle them accordingly.
private var action: SystemExtensionDelegateAction = .none

init(asyncDelegate: AsyncDelegate) {
Expand Down Expand Up @@ -158,7 +158,8 @@ class SystemExtensionDelegate<AsyncDelegate: SystemExtensionAsyncRecorder>:
logger.error("System extension request failed: \(error.localizedDescription)")
Task { [asyncDelegate] in
await asyncDelegate.recordSystemExtensionState(
.failed(error.localizedDescription))
.failed(error.localizedDescription)
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ extension CircularProgressView {
}
}

// We note a constant >10% CPU usage when using a SwiftUI rotation animation that
// repeats forever, while this implementation, using Core Animation, uses <1% CPU.
/// We note a constant >10% CPU usage when using a SwiftUI rotation animation that
/// repeats forever, while this implementation, using Core Animation, uses <1% CPU.
struct IndeterminateSpinnerView: NSViewRepresentable {
var diameter: CGFloat
var strokeWidth: CGFloat
Expand Down
Loading