Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Switch from OrbStack to dns-sd #3

Merged
merged 34 commits into from
Jan 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
51f528b
feat: use mdns record and nginx port 80
cyredhat Jan 11, 2024
b540a12
refactor: remove orbstack
cyredhat Jan 11, 2024
7b8ccac
refactor: use single dns-sd process
cyredhat Jan 11, 2024
8c1c963
feat: log stdout
cyredhat Jan 11, 2024
2fedb08
feat: add dns-sd binary
cyredhat Jan 11, 2024
dfc13f5
feat: stub helper
suphon-t Jan 11, 2024
30fe515
feat: helper install flow
suphon-t Jan 12, 2024
b26bd77
feat: move cli management to helper
suphon-t Jan 12, 2024
e65364b
feat: move daemon to helper service
suphon-t Jan 12, 2024
fc36ca3
chore: show ProgressView while getting status
suphon-t Jan 12, 2024
e466305
Merge remote-tracking branch 'origin/pai/dns-sd' into suphon/integrat…
suphon-t Jan 12, 2024
6ee42dd
refactor: call dns-sd from go
suphon-t Jan 12, 2024
f5ebbf4
fix: startup status check
suphon-t Jan 12, 2024
b46222e
feat: compile with CGO_ENABLED
suphon-t Jan 12, 2024
bc2f52d
feat: make sure host ends with .local
suphon-t Jan 12, 2024
e947513
fix: don't fail on interrupt while processing
suphon-t Jan 12, 2024
cd1324c
fix: stop nginx with context
suphon-t Jan 12, 2024
f98d120
feat: terminate daemon before exiting helper
suphon-t Jan 12, 2024
9d10f39
feat: try killing nginx on start
suphon-t Jan 12, 2024
6869db6
feat: show route count
suphon-t Jan 12, 2024
2579eb1
feat: build on all branches
suphon-t Jan 12, 2024
6a9e28c
feat: log bundle on helper start
suphon-t Jan 12, 2024
f9735d3
refactor: migrate from SMAppService
suphon-t Jan 12, 2024
c59bae8
chore: ignore profraw
suphon-t Jan 12, 2024
70894da
fix: use created mapping as delete key
suphon-t Jan 12, 2024
9e0e317
fix: plsit modifier path
suphon-t Jan 12, 2024
3bea18b
chore: unused variable
suphon-t Jan 12, 2024
b7e37b3
fix: plist modifier deployment target
suphon-t Jan 12, 2024
d7708c9
fix: startup crashes
suphon-t Jan 12, 2024
5e27b98
chore: changeset
suphon-t Jan 15, 2024
7da08f6
chore: remove unused binary
suphon-t Jan 15, 2024
6b99035
Merge branch 'suphon/integrate-dns-sd' of github.com:softnetics/dotlo…
suphon-t Jan 15, 2024
060fe8f
fix: add default_server back
suphon-t Jan 15, 2024
9b5fd18
fix: ensure target starts with http or https
suphon-t Jan 15, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/old-cheetahs-watch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@softnetics/dotlocal": minor
---

Switch from OrbStack to dns-sd
4 changes: 2 additions & 2 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: build
on:
push:
branches:
- main
- '**'
tags:
- "v*.*.*"

Expand All @@ -18,7 +18,7 @@ permissions:
repository-projects: read
security-events: read
statuses: read

jobs:
build:
runs-on: macos-13
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,4 @@ fastlane/test_output
# https://github.com/johnno1962/injectionforxcode

iOSInjectionProject/
default.profraw
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"files.associations": {
"clientcommon.h": "c"
}
}
6 changes: 6 additions & 0 deletions Config/Config.xcconfig
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,9 @@
// https://help.apple.com/xcode/#/dev745c5c974

#include "Version.xcconfig"

APP_BUNDLE_IDENTIFIER = dev.suphon.DotLocal
HELPER_TOOL_BUNDLE_IDENTIFIER = $(APP_BUNDLE_IDENTIFIER).helper

APP_VERSION = $(MARKETING_VERSION)
HELPER_VERSION = $(MARKETING_VERSION)
570 changes: 516 additions & 54 deletions DotLocal.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
{
"pins" : [
{
"identity" : "authorized",
"kind" : "remoteSourceControl",
"location" : "https://github.com/trilemma-dev/Authorized.git",
"state" : {
"revision" : "e490b9d3f4a0e8b17a8b39b5a9750b8e0be7548a",
"version" : "1.0.0"
}
},
{
"identity" : "blessed",
"kind" : "remoteSourceControl",
"location" : "https://github.com/trilemma-dev/Blessed",
"state" : {
"revision" : "e7c730ea4bcd2df7b61f022dbd38c5cdc2c875de",
"version" : "0.6.0"
}
},
{
"identity" : "defaults",
"kind" : "remoteSourceControl",
Expand All @@ -9,6 +27,15 @@
"revision" : "d8a9f5105607c85b544558e7f5b51d6c360ba88b"
}
},
{
"identity" : "embeddedpropertylist",
"kind" : "remoteSourceControl",
"location" : "https://github.com/trilemma-dev/EmbeddedPropertyList.git",
"state" : {
"revision" : "21bd832e28a9a66ecdb7b4c21910bb0487a22fe5",
"version" : "2.0.2"
}
},
{
"identity" : "grpc-swift",
"kind" : "remoteSourceControl",
Expand All @@ -27,6 +54,24 @@
"revision" : "a04ec1c363be3627734f6dad757d82f5d4fa8fcc"
}
},
{
"identity" : "required",
"kind" : "remoteSourceControl",
"location" : "https://github.com/trilemma-dev/Required.git",
"state" : {
"revision" : "82a4fbd388346ca40b1bbe815014dc45a75d503c",
"version" : "0.1.1"
}
},
{
"identity" : "securexpc",
"kind" : "remoteSourceControl",
"location" : "https://github.com/trilemma-dev/SecureXPC",
"state" : {
"revision" : "d6e439e2b805de8be9b584fff97cf2f6a839a656",
"version" : "0.8.0"
}
},
{
"identity" : "swift-atomics",
"kind" : "remoteSourceControl",
Expand Down
18 changes: 18 additions & 0 deletions DotLocal/AppConfig.xcconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// AppConfig.xcconfig
// DotLocal
//
// Created by Suphon Thanakornpakapong on 11/1/2567 BE.
//

// Configuration settings file format documentation can be found at:
// https://help.apple.com/xcode/#/dev745c5c974

#include "Config/Config.xcconfig"

TARGET_DIRECTORY = DotLocal

PRODUCT_BUNDLE_IDENTIFIER = $(APP_BUNDLE_IDENTIFIER)
SWIFT_ACTIVE_COMPILATION_CONDITIONS = APP

INFOPLIST_FILE = $(TARGET_DIRECTORY)/Info.plist
20 changes: 15 additions & 5 deletions DotLocal/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
import Foundation
import AppKit
import Defaults
import SecureXPC

class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ notification: Notification) {
DaemonManager.shared.start()
override init() {
_ = HelperManager.shared
ClientManager.shared.checkInstalled()
}

Expand All @@ -27,8 +28,17 @@ class AppDelegate: NSObject, NSApplicationDelegate {
return true
}

func applicationWillTerminate(_ notification: Notification) {
DaemonManager.shared.stop()
DaemonManager.shared.wait()
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
print("applicationShouldTerminate called, stopping daemon and helper")
if HelperManager.shared.installationStatus.isReady {
Task {
await DaemonManager.shared.stop()
try? await HelperManager.shared.xpcClient.send(to: SharedConstants.exitRoute)
NSApplication.shared.terminate(nil)
}
return .terminateLater
} else {
return .terminateNow
}
}
}
12 changes: 6 additions & 6 deletions DotLocal/AppMenu.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ struct AppMenu: View {
switch daemonManager.state {
case .stopped:
Button("DotLocal is not running") {}.disabled(true)
case .starting:
case .starting, .unknown:
Button("DotLocal is starting") {}.disabled(true)
case .started:
case .started(let mappings):
Section("Routes") {
MappingListMenu()
MappingListMenu(mappings: mappings)
}
}
Divider()
Expand All @@ -39,14 +39,14 @@ struct AppMenu: View {
}

struct MappingListMenu: View {
@StateObject var vm = MappingListViewModel()
var mappings: [Mapping]
@Environment(\.openURL) var openURL

var body: some View {
if vm.mappings.isEmpty {
if mappings.isEmpty {
Button("No Routes", action: {}).disabled(true)
} else {
ForEach(vm.mappings) { mapping in
ForEach(mappings) { mapping in
let url = URL(string: "http://\(mapping.host)\(mapping.pathPrefix)")!
Button(action: { openURL(url) }, label: {
Text(getLabel(mapping: mapping))
Expand Down
18 changes: 13 additions & 5 deletions DotLocal/ClientManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,32 @@
//

import Foundation
import SecureXPC

class ClientManager: ObservableObject {
static let shared = ClientManager()

@Published var installed = false
private let clientUrl = Bundle.main.bundleURL.appendingPathComponent("Contents/Resources/bin/dotlocal")
private let target = "/usr/local/bin/dotlocal"

private init() {}

func installCli() async {
_ = await Sudo.run(path: clientUrl.path(percentEncoded: false), arguments: ["install"])
checkInstalled()
do {
try await HelperManager.shared.xpcClient.sendMessage(Bundle.main.bundleURL, to: SharedConstants.installClientRoute)
checkInstalled()
} catch {
print("error installing cli: \(error)")
}
}

func uninstallCli() async {
_ = await Sudo.run(path: clientUrl.path(percentEncoded: false), arguments: ["uninstall"])
checkInstalled()
do {
try await HelperManager.shared.xpcClient.send(to: SharedConstants.uninstallClientRoute)
checkInstalled()
} catch {
print("error uninstalling cli: \(error)")
}
}

func checkInstalled() {
Expand Down
77 changes: 65 additions & 12 deletions DotLocal/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,43 @@
//

import SwiftUI
import ServiceManagement
import Blessed
import Authorized
import SecureXPC

struct ContentView: View {
@StateObject var daemonManager = DaemonManager.shared
@StateObject var helperManager = HelperManager.shared

var body: some View {
let status = helperManager.installationStatus
VStack {
switch daemonManager.state {
case .stopped:
Text("DotLocal is not running")
case .starting:
ProgressView()
case .started:
MappingList()
if status.isReady {
VStack {
switch daemonManager.state {
case .stopped:
Text("DotLocal is not running")
case .starting, .unknown:
ProgressView()
case .started:
MappingList()
}
}.toolbar() {
StartStopButton(state: daemonManager.state, onStart: {
Task {
await daemonManager.start()
}
}, onStop: {
Task {
await daemonManager.stop()
}
})
}
} else {
RequiresHelperView()
}
}
.frame(maxWidth: /*@START_MENU_TOKEN@*/.infinity/*@END_MENU_TOKEN@*/, maxHeight: .infinity)
.toolbar() {
StartStopButton(state: daemonManager.state, onStart: { daemonManager.start() }, onStop: { daemonManager.stop() })
}
}
}

Expand All @@ -39,7 +57,7 @@ struct StartStopButton: View {
Button(action: onStart) {
Label("Start", systemImage: "play.fill")
}
case .starting:
case .starting, .unknown:
ProgressView().controlSize(.small)
case .started:
Button(action: onStop) {
Expand All @@ -49,6 +67,41 @@ struct StartStopButton: View {
}
}

struct RequiresHelperView: View {
@State private var didError = false
@State private var errorMessage = ""

var body: some View {
VStack(spacing: 8) {
Text("Helper Not Installed").font(.title).fontWeight(.bold)
Text("Please install the helper in order to use DotLocal")
Button(action: {
do {
try PrivilegedHelperManager.shared
.authorizeAndBless(message: nil)
} catch AuthorizationError.canceled {
// No user feedback needed, user canceled
} catch {
errorMessage = error.localizedDescription
didError = true
}
}, label: {
Text("Install Helper")
})
}
.foregroundStyle(.secondary)
.alert(
"Install failed",
isPresented: $didError,
presenting: errorMessage
) { _ in
Button("OK") {}
} message: { message in
Text(message)
}
}
}

#Preview {
ContentView()
}
Loading