diff --git a/README.md b/README.md index 4552809..517a003 100644 --- a/README.md +++ b/README.md @@ -17,16 +17,21 @@ ### One-Click Installation -With Tophat, you can skip building branches locally. Tophat hosts a lightweight web server, allowing you to easily add Tophat links to your CI pipeline and launch apps right from GitHub. +With Tophat, you can skip building branches locally. Use Tophatʼs many features and APIs to easily create installation links to CI artifacts. Take your tooling a step further and offer contributors the ability to test pull requests without cloning anything! + +### Extensions + +With the [TophatKit](./TophatKit) SDK, you can easily extend Tophat to integrate with custom build and caching systems. This makes Tophat compatible with virtually any tooling setup you can imagine! ### Quick Launch -Quick Launch allows you to add your favourite apps right in the Tophat menu. Need the latest build? Click on the icon and go! Tophat will download the latest version, update the icon, and launch it on your device. +Quick Launch allows you to add your favourite apps right to the Tophat menu. Need the latest build? Click on the icon and go! Tophat will download the latest version, automatically update the icon, and launch it on your device.

Quick Launch

+ ### Device Pinning Have lots of devices and only use a couple at a time? Easily pin them to the top of the devices list for quick access. @@ -45,6 +50,79 @@ Customize Tophat to your needs with the Settings window. Adjust preferences, add ## Integrating Tophat +> :bulb: As of Tophat 2, key Tophat APIs have changed and the legacy APIs have been removed. If you still need to support your existing integrations, use Tophat 1 and refer to the [legacy integration guide](#integrating-tophat-legacy) below. + +Downloads with Tophat are powered by _artifact providers_. Some artifact providers are built-in to Tophat, while some can be installed using Tophat Extensions (see [_Extensions_](#extensions)). When triggering an install with Tophat, you will need to specify the following information: + +- The artifact providerʼs ID. +- The artifact providerʼs parameters. +- The platform (optional). +- The destination (optional). +- A list of launch arguments (optional). + +The format by which this information is specified varies slightly depending on whether you use URLs, Quick Launch, or `tophatctl`, but each API requires roughly the same information. + +You can view a list of all artifact provider IDs using `tophatctl providers`. + +Each request can install multiple artifacts. Within each request, these are called _recipes_, and are particularly useful when you want to have one link that supports both simulators and devices in the same link, where different builds are required for each. + +### URLs + +Tophat handles URLs using both the `tophat://` and `http://` schemes so that you can use them in any website or application. Where possible, prefer the `tophat://` scheme which does not navigate away from the current page. + +The examples below will use `tophat://`, but `tophat://` is interchangeable with `https://localhost:29070/` for use with GitHub, for example. + +#### Format + +Below is an example for a hypothetical artifact provider for Google Cloud Storage. In this case, `gcs` is the artifact provider ID: + +``` +tophat://install/gcs?bucket=&object= +``` + +The type of build downloaded will be interpreted by Tophat automatically, but the API provides a means to preheat the intended device ahead of time by specifying the platform and destination: + +``` +tophat://install/gcs?bucket=&object=&platform=ios&destination=device +``` + +If multiple artifacts are created for different destinations, parameters for both artifacts may be specified in the same URL by repeating query parameters. Tophat will then select the appropriate parameters for the selected device. + +``` +tophat://install/gcs?bucket=&object=&platform=ios&destination=device&bucket=&object=&platform=ios&destination=simulator +``` + +Launch arguments may be specified using the `arguments` query parameter: + +``` +tophat://install/gcs?bucket=&object=&arguments=one,two,three +``` + +> :memo: When creating an install link, prefer using “Install with Tophat” as the link text. + +### Built-In Providers + +You can download artifacts from public HTTP resources: + +``` +tophat://install/http?url= +``` + +A shell script allows you to have full control over how the artifact is downloaded. For an executable shell script located at `~/Library/Application Scripts/com.shopify.Tophat.TophatCoreExtension/filename` with executable flag set with `chmod +x filename`, the script can be invoked using: + +``` +tophat://install/shell?script=filename +``` + +The script is provided with two positional arguments: + +- `$1` is a full path to a **staging** directory where you may temporarily store files during download and unzip. +- `$2` is a full path to the **output** directory where exactly one artifact must be located when the script finishes. Tophat will look in this directory to install the application. + +## Integrating Tophat (Legacy) + +> This legacy guide is deprecated only applies to Tophat 1. + There are a number of ways to interact with Tophat so that you can integrate it into your project with ease. ### GitHub @@ -79,6 +157,8 @@ tophat://install/?universal=https://url/to/universal URL schemes and handling _via_ web server both use the same URL format. +## Using Tophat + ### Command Line Helper Tophat can be integrated with various tools and projects using `tophatctl`, Tophatʼs companion command line app. You can use `tophatctl` to perform the following tasks: diff --git a/TophatKit/README.md b/TophatKit/README.md new file mode 100644 index 0000000..57aa9ab --- /dev/null +++ b/TophatKit/README.md @@ -0,0 +1,122 @@ +# TophatKit +A Swift SDK for building extensions for [Tophat](https://github.com/Shopify/tophat). + +> :memo: TophatKit will eventually be available as a standalone package, but is temporarily hosted directly inside the Tophat repo. If you want to start building your own extensions right away, clone the `tophat` repo and reference the `TophatSDK` directory as a local Swift package. + +## Overview + +TophatKit makes it easy to quickly build custom integrations for Tophat. Currently, TophatKit lets you build your own _artifact providers_ so that you can use Swift to integrate Tophat with your CI or storage solution to retrieve artifacts. + +A Tophat extension is a Generic Extension target in Xcode and is hosted in a standalone application, similar to Safari extensions. Some of Tophatʼs [core features](../TophatExtensions/TophatCoreExtension) are defined in extensions as well. + +## Getting Started + +This guide assumes that you are familiar with Xcode and basic macOS application structure. + +Decide whether you want to host your extension directly in Tophat so that all Tophat users can have access, or whether you need a private extension that is exclusive to your organizationʼs systems. If you want to host in Tophat, open the Tophat Xcode project. If you want to create a private extension, create a new Xcode project and create a macOS application. + +1. Go to File → New → Target. +2. Select **Generic Extension**. +3. Choose a product name, and ensure that “Supports User Interface” is selected, even if you do not plan to support a settings interface. +4. Once the target is created, add the TophatSDK Swift package to your project and ensure that it is linked to your new Generic Extension target. +5. Open the `Info.plist` file for your extension and ensure that the `EXExtensionPointIdentifier` is set to `com.shopify.Tophat.extension`. + +### Basic Extension Structure + +Start by opening the `MyExtension.swift` file at the root of your extension target (the name will match the name of the product you chose) and replace the contents of the file with the following: + +```swift +// MyExtension.swift + +import TophatKit + +@main +struct MyExtension: TophatExtension { + static let title: LocalizedStringResource = "My Extension" +} +``` + +This is the default structure for an extension. The extension may then conform to one or more additional protocols to support additional functionality, such as `ArtifactProviding` and `SettingsProviding`. + +### Adding an Artifact Provider + +To add an artifact provider, start by creating a new Swift file to host the artifact provider in. Weʼll use Google Cloud Storage as an example: + +```swift +// GoogleCloudStorageArtifactProvider.swift + +import TophatKit + +struct GoogleCloudStorageArtifactProvider: ArtifactProvider { + static let id = "gcs" + static let title: LocalizedStringResource = "Google Cloud Storage" + + @Parameter(key: "bucket", title: "Bucket") + var bucket: String + + @Parameter(key: "object", title: "Object") + var object: String + + func retrieve() async throws -> some ArtifactProviderResult { + let downloadedFileURL = // Your logic for downloading the build. + + return .result(localURL: downloadedFileURL) + } + + func cleanUp(localURL: URL) async throws { + // Perform clean up. + } +} +``` + +Youʼll notice a few things here: +- The `id` is used to identify the artifact provider when using Tophat URLs or `tophatctl`. +- The `title` is what is displayed in various parts of the Tophat user interface. +- There are two `Parameter` values. An artifact provider receives zero or more parameters as inputs when it is called. Use the `@Parameter` property wrapper to define parameters for your artifact provider. +- The `retrieve()` function is the implementation of the artifact provider. Use the parameters to make the necessary requests to retrieve the artifact. +- The `cleanUp(localURL:)` function defines what needs to be done to delete the downloaded artifact produced by `retrieve()`. You must implement this function. + +Now that the artifact provider is created, register it in `MyExtension.swift`: + +```swift +// MyExtension.swift + +import TophatKit + +@main +struct MyExtension: TophatExtension, ArtifactProviding { + static let title: LocalizedStringResource = "My Extension" + + static var artifactProviders: some ArtifactProviders { + GoogleCloudStorageArtifactProvider() + } +} +``` + +Build and run and you should now have a functioning extension and artifact provider! + +### Making the Extension Configurable + +If the user needs to be able to configure the extension (e.g. to provide an API key), conform `MyExtension` to `SettingsProviding`: + +```swift +// GoogleCloudExtension.swift + +import SwiftUI +import TophatKit + +@main +struct MyExtension: TophatExtension, ArtifactProviding, SettingsProviding { + // ... + + static var settings: some View { + Text("Settings") + } +} +``` + +The `settings` property accepts a full SwiftUI view. The user can access the settings view by going to Tophat → Settings → Extensions, and clicking the info icon next to the extension. + +## Contributing + +See the [contribution guidelines](../CONTRIBUTING.md) for more information.