Skip to content

Commit

Permalink
Deprecate support for API endpoint artifact retrieval (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
lfroms authored Aug 26, 2024
1 parent 912c369 commit 2a3d302
Show file tree
Hide file tree
Showing 20 changed files with 80 additions and 427 deletions.
23 changes: 0 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,6 @@ Or, for universal builds that work on all device types, use the `universal` quer
http://localhost:29070/install/<ios|android>?universal=https://url/to/virtual
```

For builds that are available using a Tophat API (see [_API Endpoints_](#api-endpoints)):

```
http://localhost:29070/install?api=https://url/to/endpoint
```

You can also specify arguments to pass to the application on launch using the `launchArguments` query string. For example:

```
Expand Down Expand Up @@ -98,23 +92,6 @@ For more details on how to use `tophatctl`, run the following command after inst
tophatctl --help
```

### API Endpoints

For more advanced integrations, Tophat supports requesting artifacts using REST. To support Tophat, a web service must expose an endpoint that returns a response in the following format:

```json
{
"name": "<App Name>",
"platform": "<ios|android>",
"virtual": "<URL>",
"physical": "<URL>"
}
```

The endpoint must support basic HTTP authentication with username `TOPHAT_APP_TOKEN` and any token as the password. The token must be stored as plain text in a local `~/.tophatrc` file with no newlines and no spaces. Tophat reads this value and automatically specifies it as part of the `Authorization` header when making the request.

Client side configuration for API-based application retrieval is supported _via_ the Settings window or `tophatctl`.

### File Associations

Tophat also adds file associations to `*.ipa`, `*.apk`, and `*.zip` files so you can open artifacts from your device.
Expand Down
12 changes: 0 additions & 12 deletions Tophat.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,6 @@
80564B5229834137002DC136 /* TaskStatusReporterDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80564B5129834137002DC136 /* TaskStatusReporterDelegate.swift */; };
80564B542983414D002DC136 /* AlertOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80564B532983414D002DC136 /* AlertOptions.swift */; };
80564B5629834203002DC136 /* FileTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80564B5529834203002DC136 /* FileTypes.swift */; };
805B0EDE29C3A3DE00F051BE /* ArtifactProviderResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 805B0EDD29C3A3DE00F051BE /* ArtifactProviderResponse.swift */; };
805B0EE029C3A41100F051BE /* ArtifactProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 805B0EDF29C3A41100F051BE /* ArtifactProvider.swift */; };
805B0EE229C3B02800F051BE /* ArtifactProviderError+LocalizedError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 805B0EE129C3B02800F051BE /* ArtifactProviderError+LocalizedError.swift */; };
805FC43229E9BE0A00A78208 /* ArtifactDownloaderError+LocalizedError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 805FC43129E9BE0A00A78208 /* ArtifactDownloaderError+LocalizedError.swift */; };
80629BF12939818C0077960E /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80629BE92939818C0077960E /* SettingsView.swift */; };
80629BF22939818C0077960E /* PinnedApplicationRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80629BEC2939818C0077960E /* PinnedApplicationRow.swift */; };
Expand Down Expand Up @@ -277,9 +274,6 @@
80564B5129834137002DC136 /* TaskStatusReporterDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskStatusReporterDelegate.swift; sourceTree = "<group>"; };
80564B532983414D002DC136 /* AlertOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertOptions.swift; sourceTree = "<group>"; };
80564B5529834203002DC136 /* FileTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileTypes.swift; sourceTree = "<group>"; };
805B0EDD29C3A3DE00F051BE /* ArtifactProviderResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArtifactProviderResponse.swift; sourceTree = "<group>"; };
805B0EDF29C3A41100F051BE /* ArtifactProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArtifactProvider.swift; sourceTree = "<group>"; };
805B0EE129C3B02800F051BE /* ArtifactProviderError+LocalizedError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ArtifactProviderError+LocalizedError.swift"; sourceTree = "<group>"; };
805FC43129E9BE0A00A78208 /* ArtifactDownloaderError+LocalizedError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ArtifactDownloaderError+LocalizedError.swift"; sourceTree = "<group>"; };
80629BE92939818C0077960E /* SettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
80629BEC2939818C0077960E /* PinnedApplicationRow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PinnedApplicationRow.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -469,7 +463,6 @@
80301625292C17560016F25E /* AppleApplication.swift */,
8030162B292C1B490016F25E /* ApplicationError.swift */,
8029A080298AF1E90002C579 /* ApplicationIcon.swift */,
805B0EDD29C3A3DE00F051BE /* ArtifactProviderResponse.swift */,
80EB5D56297096FB0011DE5F /* InstallStatusMetadata.swift */,
80EB5D50296F68CD0011DE5F /* LaunchContext.swift */,
80318D1D2927EC54002A5FD9 /* LaunchRequest.swift */,
Expand All @@ -494,7 +487,6 @@
80EB5D4C296F64F50011DE5F /* ApplicationError+LocalizedError.swift */,
80EB5D52296F6A380011DE5F /* Array+JoinedWithSpaces.swift */,
805FC43129E9BE0A00A78208 /* ArtifactDownloaderError+LocalizedError.swift */,
805B0EE129C3B02800F051BE /* ArtifactProviderError+LocalizedError.swift */,
802671462947C33C001A804D /* Bundle+Extensions.swift */,
804FF6582914239800147652 /* Collection+Filter.swift */,
80EB5D4A296F64D70011DE5F /* DeviceError+LocalizedError.swift */,
Expand Down Expand Up @@ -713,7 +705,6 @@
8090E2552967741F003106B9 /* Status Reporting */,
80EB5D43296F59000011DE5F /* Tasks */,
80FF03F029089975008509E0 /* ArtifactDownloader.swift */,
805B0EDF29C3A41100F051BE /* ArtifactProvider.swift */,
8030161F292874B70016F25E /* ArtifactUnpacker.swift */,
80629BFB293981B10077960E /* CodableAppStorage.swift */,
809BD034290C3A5200FD4043 /* DeviceManager.swift */,
Expand Down Expand Up @@ -940,7 +931,6 @@
buildActionMask = 2147483647;
files = (
80629BF12939818C0077960E /* SettingsView.swift in Sources */,
805B0EE229C3B02800F051BE /* ArtifactProviderError+LocalizedError.swift in Sources */,
809C8573297B0625004CE6A2 /* LaunchFromLocationMenuItem.swift in Sources */,
80F74E2A2909FAC80040F026 /* MainMenu.swift in Sources */,
80A66D742981BD2200ECBCB6 /* UtilityPathPreferences.swift in Sources */,
Expand Down Expand Up @@ -1025,7 +1015,6 @@
80629BF42939818C0077960E /* AddPinnedApplicationSheet.swift in Sources */,
804ECB7C2975C15300DE78F4 /* DevicePicker.swift in Sources */,
805FC43229E9BE0A00A78208 /* ArtifactDownloaderError+LocalizedError.swift in Sources */,
805B0EE029C3A41100F051BE /* ArtifactProvider.swift in Sources */,
80629C22293A8D270077960E /* ProvisioningProfile.swift in Sources */,
80D71F1C2984CE720006E1BF /* XcodeOnboardingItem.swift in Sources */,
802671452947C297001A804D /* MenuHeader.swift in Sources */,
Expand All @@ -1044,7 +1033,6 @@
80301620292874B70016F25E /* ArtifactUnpacker.swift in Sources */,
80EB5D4F296F658E0011DE5F /* LaunchRequestBuilderError+LocalizedError.swift in Sources */,
80A66D6D2981BC9900ECBCB6 /* ErrorNotifier.swift in Sources */,
805B0EDE29C3A3DE00F051BE /* ArtifactProviderResponse.swift in Sources */,
80FF03EF29087473008509E0 /* InstallCoordinator.swift in Sources */,
B6AA44DD296F78670017321C /* GeneralTab.swift in Sources */,
80D71F202984CE9F0006E1BF /* GoogleCloudStorageOnboardingItem.swift in Sources */,
Expand Down
42 changes: 0 additions & 42 deletions Tophat/Extensions/ArtifactProviderError+LocalizedError.swift

This file was deleted.

17 changes: 0 additions & 17 deletions Tophat/Models/ArtifactProviderResponse.swift

This file was deleted.

4 changes: 1 addition & 3 deletions Tophat/Models/PinnedApplication.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ struct PinnedApplication: Identifiable, Codable {
let name: String
let platform: Platform
let artifacts: [Artifact]
let artifactProviderURL: URL?
var icon: ApplicationIcon? = nil

/// Creates a new pinned application.
Expand All @@ -27,12 +26,11 @@ struct PinnedApplication: Identifiable, Codable {
/// - name: The name of the pinned application.
/// - platform: The platform of the pinned application.
/// - artifacts: The set of artifacts at which this pinned application can be found.
init(id: String? = nil, name: String, platform: Platform, artifacts: [Artifact] = [], artifactProviderURL: URL? = nil) {
init(id: String? = nil, name: String, platform: Platform, artifacts: [Artifact] = []) {
self.id = id ?? UUID().uuidString
self.name = name
self.platform = platform
self.artifacts = artifacts
self.artifactProviderURL = artifactProviderURL
}
}

Expand Down
11 changes: 0 additions & 11 deletions Tophat/TophatApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -205,17 +205,6 @@ private final class AppDelegate: NSObject, NSApplicationDelegate {
}
}
.store(in: &cancellables)

Publishers.MergeMany(
self.urlHandler.onLaunchArtifactProviderURL,
self.notificationHandler.onLaunchArtifactProviderURL
)
.sink { [weak self] (artifactProviderURL, launchArguments) in
Task.detached(priority: .userInitiated) { [weak self] in
await self?.launchApp(artifactProviderURL: artifactProviderURL, context: LaunchContext(arguments: launchArguments))
}
}
.store(in: &cancellables)
}

private func handle(urls: [URL]) {
Expand Down
62 changes: 0 additions & 62 deletions Tophat/Utilities/ArtifactProvider.swift

This file was deleted.

33 changes: 0 additions & 33 deletions Tophat/Utilities/InstallCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,39 +51,6 @@ final class InstallCoordinator {
}
}

/// Downloads, installs, and launches an artifact from an artifact provider endpoint.
///
/// If an appropriate device is found for the artifact set in advance, the device is booted in parallel
/// with the download process to improve completion time.
///
/// - Parameters:
/// - artifactProviderURL: The URL of the API that returns artifacts.
/// - context: Additional metadata for the operation.
func launch(artifactProviderURL: URL, context: LaunchContext? = nil) async throws {
await preflightInstallation(context: context)

let response: ArtifactProviderResponse

do {
response = try await ArtifactProvider(url: artifactProviderURL).fetchArtifacts()
} catch let error {
notifyError(error: error)
throw error
}

do {
let launchRequest = try launchRequestBuilder.createRequest(for: response)
try await launch(
artifactURL: launchRequest.launchable.url,
device: launchRequest.device,
context: context ?? LaunchContext(appName: response.name)
)
} catch let error {
notifyError(error: error, platform: response.platform)
throw error
}
}

/// Downloads, installs, and launches an artifact from a local or remote URL.
///
/// The device to boot is not known ahead of time—it will be booted after the application is downloaded
Expand Down
8 changes: 0 additions & 8 deletions Tophat/Utilities/LaunchAppAction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,6 @@ struct LaunchAppAction {
}
}

func callAsFunction(artifactProviderURL: URL, context: LaunchContext? = nil) async {
do {
try await installCoordinator.launch(artifactProviderURL: artifactProviderURL, context: context)
} catch {
ErrorNotifier().notify(error: error)
}
}

func callAsFunction(artifactSet: ArtifactSet, on platform: Platform, context: LaunchContext? = nil) async {
do {
try await installCoordinator.launch(artifactSet: artifactSet, on: platform, context: context)
Expand Down
23 changes: 0 additions & 23 deletions Tophat/Utilities/LaunchRequestBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,6 @@ final class LaunchRequestBuilder {
return LaunchRequest(launchable: artifact, device: device)
}

func createRequest(for artifactProviderResponse: ArtifactProviderResponse) throws -> LaunchRequest {
return try createRequest(
for: ArtifactSet(artifactProviderResponse: artifactProviderResponse),
platform: artifactProviderResponse.platform
)
}

func createRequest(for application: Application) throws -> LaunchRequest {
let platform = application.platform
let targets = application.targets
Expand Down Expand Up @@ -75,19 +68,3 @@ enum LaunchRequestBuilderError: Error {
case failedToFindCompatibleDevice(platform: Platform, availableTargets: Set<DeviceType>)
case failedToFindCompatibleArtifact(device: Device, availableTargets: Set<DeviceType>)
}

private extension ArtifactSet {
init(artifactProviderResponse response: ArtifactProviderResponse) {
var artifacts: [Artifact] = []

if let virtualURL = response.virtual {
artifacts.append(.init(url: virtualURL, targets: [.virtual]))
}

if let physicalURL = response.physical {
artifacts.append(.init(url: physicalURL, targets: [.physical]))
}

self.init(artifacts: artifacts)
}
}
10 changes: 2 additions & 8 deletions Tophat/Utilities/NotificationHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ final class NotificationHandler {

let onLaunchArtifactSet = PassthroughSubject<(ArtifactSet, Platform, [String]), Never>()
let onLaunchArtifactURL = PassthroughSubject<(URL, [String]), Never>()
let onLaunchArtifactProviderURL = PassthroughSubject<(URL, [String]), Never>()

private let notifier = TophatInterProcessNotifier()
private var cancellables: Set<AnyCancellable> = []
Expand All @@ -40,11 +39,7 @@ final class NotificationHandler {
notifier
.publisher(for: TophatInstallGenericNotification.self)
.sink { [weak self] payload in
if payload.isAPI {
self?.onLaunchArtifactProviderURL.send((payload.url, payload.launchArguments))
} else {
self?.onLaunchArtifactURL.send((payload.url, payload.launchArguments))
}
self?.onLaunchArtifactURL.send((payload.url, payload.launchArguments))
}
.store(in: &cancellables)

Expand All @@ -55,8 +50,7 @@ final class NotificationHandler {
id: payload.id,
name: payload.name,
platform: payload.platform,
artifacts: payload.artifacts,
artifactProviderURL: payload.artifactProviderURL
artifacts: payload.artifacts
)

self?.delegate?.notificationHandler(didReceiveRequestToAddPinnedApplication: pinnedApplication)
Expand Down
Loading

0 comments on commit 2a3d302

Please sign in to comment.