mirror of
https://github.com/maxgoedjen/secretive.git
synced 2025-07-01 17:53:36 +00:00
Compare commits
13 Commits
projected_
...
xpc
Author | SHA1 | Date | |
---|---|---|---|
93b51f9e9b | |||
ba1c4c1563 | |||
b41c036ea0 | |||
adabe801d3 | |||
19f9494492 | |||
c50d2feaf9 | |||
03d3cc9177 | |||
141cc03b60 | |||
07559bd7ef | |||
cb206a18c2 | |||
6cb3ff80d9 | |||
05c5aca9b6 | |||
5894bbca00 |
49
.github/workflows/nightly.yml
vendored
Normal file
49
.github/workflows/nightly.yml
vendored
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
name: Nightly
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 8 * * *"
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: macos-11.0
|
||||||
|
timeout-minutes: 10
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Setup Signing
|
||||||
|
env:
|
||||||
|
SIGNING_DATA: ${{ secrets.SIGNING_DATA }}
|
||||||
|
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
|
||||||
|
HOST_PROFILE_DATA: ${{ secrets.HOST_PROFILE_DATA }}
|
||||||
|
AGENT_PROFILE_DATA: ${{ secrets.AGENT_PROFILE_DATA }}
|
||||||
|
APPLE_API_KEY_DATA: ${{ secrets.APPLE_API_KEY_DATA }}
|
||||||
|
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||||
|
run: ./.github/scripts/signing.sh
|
||||||
|
- name: Set Environment
|
||||||
|
run: sudo xcrun xcode-select -s /Applications/Xcode_13.2.1.app
|
||||||
|
- name: Update Build Number
|
||||||
|
env:
|
||||||
|
RUN_ID: ${{ github.run_id }}
|
||||||
|
run: |
|
||||||
|
sed -i '' -e "s/GITHUB_CI_VERSION/0.0.0/g" Sources/Config/Config.xcconfig
|
||||||
|
sed -i '' -e "s/GITHUB_BUILD_NUMBER/1.$RUN_ID/g" Sources/Config/Config.xcconfig
|
||||||
|
sed -i '' -e "s/GITHUB_BUILD_URL/https:\/\/github.com\/maxgoedjen\/secretive\/actions\/runs\/$RUN_ID/g" Sources/Secretive/Credits.rtf
|
||||||
|
- name: Build
|
||||||
|
run: xcrun xcodebuild -project Sources/Secretive.xcodeproj -scheme Secretive -configuration Release -archivePath Archive.xcarchive archive
|
||||||
|
- name: Create ZIPs
|
||||||
|
run: |
|
||||||
|
ditto -c -k --sequesterRsrc --keepParent Archive.xcarchive/Products/Applications/Secretive.app ./Secretive.zip
|
||||||
|
ditto -c -k --sequesterRsrc --keepParent Archive.xcarchive ./Archive.zip
|
||||||
|
- name: Notarize
|
||||||
|
env:
|
||||||
|
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||||
|
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
|
||||||
|
run: xcrun notarytool submit --key ~/.private_keys/AuthKey_$APPLE_API_KEY_ID.p8 --key-id $APPLE_API_KEY_ID --issuer $APPLE_API_ISSUER Secretive.zip
|
||||||
|
- name: Document SHAs
|
||||||
|
run: |
|
||||||
|
shasum -a 512 Secretive.zip
|
||||||
|
shasum -a 512 Archive.zip
|
||||||
|
- name: Upload App to Artifacts
|
||||||
|
uses: actions/upload-artifact@v1
|
||||||
|
with:
|
||||||
|
name: Secretive.zip
|
||||||
|
path: Secretive.zip
|
@ -51,6 +51,30 @@ Add this to `~/Library/LaunchAgents/com.maxgoedjen.Secretive.SecretAgent.plist`
|
|||||||
|
|
||||||
Log out and log in again before launching Cyberduck.
|
Log out and log in again before launching Cyberduck.
|
||||||
|
|
||||||
|
## GitKraken
|
||||||
|
|
||||||
|
Add this to `~/Library/LaunchAgents/com.maxgoedjen.Secretive.SecretAgent.plist`
|
||||||
|
|
||||||
|
```
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>Label</key>
|
||||||
|
<string>link-ssh-auth-sock</string>
|
||||||
|
<key>ProgramArguments</key>
|
||||||
|
<array>
|
||||||
|
<string>/bin/sh</string>
|
||||||
|
<string>-c</string>
|
||||||
|
<string>/bin/ln -sf $HOME/Library/Containers/com.maxgoedjen.Secretive.SecretAgent/Data/socket.ssh $SSH_AUTH_SOCK</string>
|
||||||
|
</array>
|
||||||
|
<key>RunAtLoad</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
```
|
||||||
|
|
||||||
|
Log out and log in again before launching Gitkraken. Then enable "Use local SSH agent in GitKraken Preferences (Located under Preferences -> SSH)
|
||||||
|
|
||||||
# The app I use isn't listed here!
|
# The app I use isn't listed here!
|
||||||
|
|
||||||
|
6
FAQ.md
6
FAQ.md
@ -38,7 +38,11 @@ Awesome! Just bear in mind that because an app only has access to the keychain i
|
|||||||
|
|
||||||
### What's this network request to GitHub?
|
### What's this network request to GitHub?
|
||||||
|
|
||||||
Secretive checks in with GitHub's releases API to check if there's a new version of Secretive available. You can audit the source code for this feature [here](https://github.com/maxgoedjen/secretive/blob/main/Brief/Updater.swift).
|
Secretive checks in with GitHub's releases API to check if there's a new version of Secretive available. You can audit the source code for this feature [here](https://github.com/maxgoedjen/secretive/blob/main/Sources/Packages/Sources/Brief/Updater.swift).
|
||||||
|
|
||||||
|
### How do I uninstall Secretive?
|
||||||
|
|
||||||
|
Drag Secretive.app to the trash and remove `~/Library/Containers/com.maxgoedjen.Secretive.SecretAgent`. `SecretAgent` may continue running until you quit it or reboot.
|
||||||
|
|
||||||
### I have a security issue
|
### I have a security issue
|
||||||
|
|
||||||
|
@ -11,5 +11,5 @@ Brief is a collection of protocols and concrete implmentation describing updates
|
|||||||
|
|
||||||
### Updater
|
### Updater
|
||||||
|
|
||||||
- ``UpdaterProtocol``
|
- ``UpdateCheckerProtocol``
|
||||||
- ``Updater``
|
- ``UpdateChecker``
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
/// A concrete implementation of ``UpdaterProtocol`` which considers the current release and OS version.
|
/// A concrete implementation of ``UpdateCheckerProtocol`` which considers the current release and OS version.
|
||||||
public class Updater: ObservableObject, UpdaterProtocol {
|
public class UpdateChecker: ObservableObject, UpdateCheckerProtocol {
|
||||||
|
|
||||||
@Published public var update: Release?
|
@Published public var update: Release?
|
||||||
public let testBuild: Bool
|
public let testBuild: Bool
|
||||||
@ -53,7 +53,7 @@ public class Updater: ObservableObject, UpdaterProtocol {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Updater {
|
extension UpdateChecker {
|
||||||
|
|
||||||
/// Evaluates the available downloadable releases, and selects the newest non-prerelease release that the user is able to run.
|
/// Evaluates the available downloadable releases, and selects the newest non-prerelease release that the user is able to run.
|
||||||
/// - Parameter releases: An array of ``Release`` objects.
|
/// - Parameter releases: An array of ``Release`` objects.
|
||||||
@ -88,7 +88,7 @@ extension Updater {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Updater {
|
extension UpdateChecker {
|
||||||
|
|
||||||
enum Constants {
|
enum Constants {
|
||||||
static let updateURL = URL(string: "https://api.github.com/repos/maxgoedjen/secretive/releases")!
|
static let updateURL = URL(string: "https://api.github.com/repos/maxgoedjen/secretive/releases")!
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
/// A protocol for retreiving the latest available version of an app.
|
/// A protocol for retreiving the latest available version of an app.
|
||||||
public protocol UpdaterProtocol: ObservableObject {
|
public protocol UpdateCheckerProtocol: ObservableObject {
|
||||||
|
|
||||||
/// The latest update
|
/// The latest update
|
||||||
var update: Release? { get }
|
var update: Release? { get }
|
||||||
|
@ -30,20 +30,23 @@ extension Agent {
|
|||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - reader: A ``FileHandleReader`` to read the content of the request.
|
/// - reader: A ``FileHandleReader`` to read the content of the request.
|
||||||
/// - writer: A ``FileHandleWriter`` to write the response to.
|
/// - writer: A ``FileHandleWriter`` to write the response to.
|
||||||
public func handle(reader: FileHandleReader, writer: FileHandleWriter) {
|
/// - Return value:
|
||||||
|
/// - Boolean if data could be read
|
||||||
|
public func handle(reader: FileHandleReader, writer: FileHandleWriter) -> Bool {
|
||||||
Logger().debug("Agent handling new data")
|
Logger().debug("Agent handling new data")
|
||||||
let data = Data(reader.availableData)
|
let data = Data(reader.availableData)
|
||||||
guard data.count > 4 else { return }
|
guard data.count > 4 else { return false}
|
||||||
let requestTypeInt = data[4]
|
let requestTypeInt = data[4]
|
||||||
guard let requestType = SSHAgent.RequestType(rawValue: requestTypeInt) else {
|
guard let requestType = SSHAgent.RequestType(rawValue: requestTypeInt) else {
|
||||||
writer.write(OpenSSHKeyWriter().lengthAndData(of: SSHAgent.ResponseType.agentFailure.data))
|
writer.write(OpenSSHKeyWriter().lengthAndData(of: SSHAgent.ResponseType.agentFailure.data))
|
||||||
Logger().debug("Agent returned \(SSHAgent.ResponseType.agentFailure.debugDescription)")
|
Logger().debug("Agent returned \(SSHAgent.ResponseType.agentFailure.debugDescription)")
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
Logger().debug("Agent handling request of type \(requestType.debugDescription)")
|
Logger().debug("Agent handling request of type \(requestType.debugDescription)")
|
||||||
let subData = Data(data[5...])
|
let subData = Data(data[5...])
|
||||||
let response = handle(requestType: requestType, data: subData, reader: reader)
|
let response = handle(requestType: requestType, data: subData, reader: reader)
|
||||||
writer.write(response)
|
writer.write(response)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func handle(requestType: SSHAgent.RequestType, data: Data, reader: FileHandleReader) -> Data {
|
func handle(requestType: SSHAgent.RequestType, data: Data, reader: FileHandleReader) -> Data {
|
||||||
|
@ -6,8 +6,12 @@ public class SocketController {
|
|||||||
|
|
||||||
/// The active FileHandle.
|
/// The active FileHandle.
|
||||||
private var fileHandle: FileHandle?
|
private var fileHandle: FileHandle?
|
||||||
|
/// The active SocketPort.
|
||||||
|
private var port: SocketPort?
|
||||||
/// A handler that will be notified when a new read/write handle is available.
|
/// A handler that will be notified when a new read/write handle is available.
|
||||||
public var handler: ((FileHandleReader, FileHandleWriter) -> Void)?
|
/// False if no data could be read
|
||||||
|
public var handler: ((FileHandleReader, FileHandleWriter) -> Bool)?
|
||||||
|
|
||||||
|
|
||||||
/// Initializes a socket controller with a specified path.
|
/// Initializes a socket controller with a specified path.
|
||||||
/// - Parameter path: The path to use as a socket.
|
/// - Parameter path: The path to use as a socket.
|
||||||
@ -19,6 +23,7 @@ public class SocketController {
|
|||||||
let exists = FileManager.default.fileExists(atPath: path)
|
let exists = FileManager.default.fileExists(atPath: path)
|
||||||
assert(!exists)
|
assert(!exists)
|
||||||
Logger().debug("Socket controller path is clear")
|
Logger().debug("Socket controller path is clear")
|
||||||
|
port = socketPort(at: path)
|
||||||
configureSocket(at: path)
|
configureSocket(at: path)
|
||||||
Logger().debug("Socket listening at \(path)")
|
Logger().debug("Socket listening at \(path)")
|
||||||
}
|
}
|
||||||
@ -26,7 +31,7 @@ public class SocketController {
|
|||||||
/// Configures the socket and a corresponding FileHandle.
|
/// Configures the socket and a corresponding FileHandle.
|
||||||
/// - Parameter path: The path to use as a socket.
|
/// - Parameter path: The path to use as a socket.
|
||||||
func configureSocket(at path: String) {
|
func configureSocket(at path: String) {
|
||||||
let port = socketPort(at: path)
|
guard let port = port else { return }
|
||||||
fileHandle = FileHandle(fileDescriptor: port.socket, closeOnDealloc: true)
|
fileHandle = FileHandle(fileDescriptor: port.socket, closeOnDealloc: true)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(handleConnectionAccept(notification:)), name: .NSFileHandleConnectionAccepted, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(handleConnectionAccept(notification:)), name: .NSFileHandleConnectionAccepted, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(handleConnectionDataAvailable(notification:)), name: .NSFileHandleDataAvailable, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(handleConnectionDataAvailable(notification:)), name: .NSFileHandleDataAvailable, object: nil)
|
||||||
@ -62,7 +67,7 @@ public class SocketController {
|
|||||||
@objc func handleConnectionAccept(notification: Notification) {
|
@objc func handleConnectionAccept(notification: Notification) {
|
||||||
Logger().debug("Socket controller accepted connection")
|
Logger().debug("Socket controller accepted connection")
|
||||||
guard let new = notification.userInfo?[NSFileHandleNotificationFileHandleItem] as? FileHandle else { return }
|
guard let new = notification.userInfo?[NSFileHandleNotificationFileHandleItem] as? FileHandle else { return }
|
||||||
handler?(new, new)
|
_ = handler?(new, new)
|
||||||
new.waitForDataInBackgroundAndNotify()
|
new.waitForDataInBackgroundAndNotify()
|
||||||
fileHandle?.acceptConnectionInBackgroundAndNotify(forModes: [RunLoop.current.currentMode!])
|
fileHandle?.acceptConnectionInBackgroundAndNotify(forModes: [RunLoop.current.currentMode!])
|
||||||
}
|
}
|
||||||
@ -73,7 +78,12 @@ public class SocketController {
|
|||||||
Logger().debug("Socket controller has new data available")
|
Logger().debug("Socket controller has new data available")
|
||||||
guard let new = notification.object as? FileHandle else { return }
|
guard let new = notification.object as? FileHandle else { return }
|
||||||
Logger().debug("Socket controller received new file handle")
|
Logger().debug("Socket controller received new file handle")
|
||||||
handler?(new, new)
|
if((handler?(new, new)) == true) {
|
||||||
|
Logger().debug("Socket controller handled data, wait for more data")
|
||||||
|
new.waitForDataInBackgroundAndNotify()
|
||||||
|
} else {
|
||||||
|
Logger().debug("Socket controller called with empty data, socked closed")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
import Foundation
|
||||||
|
import OSLog
|
||||||
|
|
||||||
|
/// Controller responsible for writing public keys to disk, so that they're easily accessible by scripts.
|
||||||
|
public class PublicKeyFileStoreController {
|
||||||
|
|
||||||
|
private let logger = Logger()
|
||||||
|
private let directory: String
|
||||||
|
|
||||||
|
/// Initializes a PublicKeyFileStoreController.
|
||||||
|
public init(homeDirectory: String) {
|
||||||
|
directory = homeDirectory.appending("/PublicKeys")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes out the keys specified to disk.
|
||||||
|
/// - Parameter secrets: The Secrets to generate keys for.
|
||||||
|
/// - Parameter clear: Whether or not the directory should be erased before writing keys.
|
||||||
|
public func generatePublicKeys(for secrets: [AnySecret], clear: Bool = false) throws {
|
||||||
|
logger.log("Writing public keys to disk")
|
||||||
|
if clear {
|
||||||
|
try? FileManager.default.removeItem(at: URL(fileURLWithPath: directory))
|
||||||
|
}
|
||||||
|
try? FileManager.default.createDirectory(at: URL(fileURLWithPath: directory), withIntermediateDirectories: false, attributes: nil)
|
||||||
|
let keyWriter = OpenSSHKeyWriter()
|
||||||
|
for secret in secrets {
|
||||||
|
let path = path(for: secret)
|
||||||
|
guard let data = keyWriter.openSSHString(secret: secret).data(using: .utf8) else { continue }
|
||||||
|
FileManager.default.createFile(atPath: path, contents: data, attributes: nil)
|
||||||
|
}
|
||||||
|
logger.log("Finished writing public keys")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The path for a Secret's public key.
|
||||||
|
/// - Parameter secret: The Secret to return the path for.
|
||||||
|
/// - Returns: The path to the Secret's public key.
|
||||||
|
/// - Warning: This method returning a path does not imply that a key has been written to disk already. This method only describes where it will be written to.
|
||||||
|
public func path<SecretType: Secret>(for secret: SecretType) -> String {
|
||||||
|
directory.appending("/").appending("\(secret.name.replacingOccurrences(of: " ", with: "-")).pub")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -4,8 +4,6 @@ import CryptoTokenKit
|
|||||||
import LocalAuthentication
|
import LocalAuthentication
|
||||||
import SecretKit
|
import SecretKit
|
||||||
|
|
||||||
// TODO: Might need to split this up into "sub-stores?"
|
|
||||||
// ie, each token has its own Store.
|
|
||||||
extension SmartCard {
|
extension SmartCard {
|
||||||
|
|
||||||
/// An implementation of Store backed by a Smart Card.
|
/// An implementation of Store backed by a Smart Card.
|
||||||
|
@ -51,7 +51,7 @@ class ReleaseParsingTests: XCTestCase {
|
|||||||
func testGreatestSelectedIfOldPatchIsPublishedLater() {
|
func testGreatestSelectedIfOldPatchIsPublishedLater() {
|
||||||
// If 2.x.x series has been published, and a patch for 1.x.x is issued
|
// If 2.x.x series has been published, and a patch for 1.x.x is issued
|
||||||
// 2.x.x should still be selected if user can run it.
|
// 2.x.x should still be selected if user can run it.
|
||||||
let updater = Updater(checkOnLaunch: false, osVersion: SemVer("2.2.3"), currentVersion: SemVer("1.0.0"))
|
let updater = UpdateChecker(checkOnLaunch: false, osVersion: SemVer("2.2.3"), currentVersion: SemVer("1.0.0"))
|
||||||
let two = Release(name: "2.0.0", prerelease: false, html_url: URL(string: "https://example.com")!, body: "2.0 available! Minimum macOS Version: 2.2.3")
|
let two = Release(name: "2.0.0", prerelease: false, html_url: URL(string: "https://example.com")!, body: "2.0 available! Minimum macOS Version: 2.2.3")
|
||||||
let releases = [
|
let releases = [
|
||||||
Release(name: "1.0.0", prerelease: false, html_url: URL(string: "https://example.com")!, body: "Initial release Minimum macOS Version: 1.2.3"),
|
Release(name: "1.0.0", prerelease: false, html_url: URL(string: "https://example.com")!, body: "Initial release Minimum macOS Version: 1.2.3"),
|
||||||
@ -72,7 +72,7 @@ class ReleaseParsingTests: XCTestCase {
|
|||||||
func testLatestVersionIsRunnable() {
|
func testLatestVersionIsRunnable() {
|
||||||
// If the 2.x.x series has been published but the user can't run it
|
// If the 2.x.x series has been published but the user can't run it
|
||||||
// the last version the user can run should be selected.
|
// the last version the user can run should be selected.
|
||||||
let updater = Updater(checkOnLaunch: false, osVersion: SemVer("1.2.3"), currentVersion: SemVer("1.0.0"))
|
let updater = UpdateChecker(checkOnLaunch: false, osVersion: SemVer("1.2.3"), currentVersion: SemVer("1.0.0"))
|
||||||
let oneOhTwo = Release(name: "1.0.2", prerelease: false, html_url: URL(string: "https://example.com")!, body: "Emergency patch! Minimum macOS Version: 1.2.3")
|
let oneOhTwo = Release(name: "1.0.2", prerelease: false, html_url: URL(string: "https://example.com")!, body: "Emergency patch! Minimum macOS Version: 1.2.3")
|
||||||
let releases = [
|
let releases = [
|
||||||
Release(name: "1.0.0", prerelease: false, html_url: URL(string: "https://example.com")!, body: "Initial release Minimum macOS Version: 1.2.3"),
|
Release(name: "1.0.0", prerelease: false, html_url: URL(string: "https://example.com")!, body: "Initial release Minimum macOS Version: 1.2.3"),
|
||||||
|
@ -16,8 +16,9 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
list.add(store: SmartCard.Store())
|
list.add(store: SmartCard.Store())
|
||||||
return list
|
return list
|
||||||
}()
|
}()
|
||||||
private let updater = Updater(checkOnLaunch: false)
|
private let updater = UpdateChecker(checkOnLaunch: false)
|
||||||
private let notifier = Notifier()
|
private let notifier = Notifier()
|
||||||
|
private let publicKeyFileStoreController = PublicKeyFileStoreController(homeDirectory: NSHomeDirectory())
|
||||||
private lazy var agent: Agent = {
|
private lazy var agent: Agent = {
|
||||||
Agent(storeList: storeList, witness: notifier)
|
Agent(storeList: storeList, witness: notifier)
|
||||||
}()
|
}()
|
||||||
@ -32,6 +33,10 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.socketController.handler = self.agent.handle(reader:writer:)
|
self.socketController.handler = self.agent.handle(reader:writer:)
|
||||||
}
|
}
|
||||||
|
DistributedNotificationCenter.default().addObserver(forName: .secretStoreUpdated, object: nil, queue: .main) { [self] _ in
|
||||||
|
try? publicKeyFileStoreController.generatePublicKeys(for: storeList.stores.flatMap({ $0.secrets }), clear: true)
|
||||||
|
}
|
||||||
|
try? publicKeyFileStoreController.generatePublicKeys(for: storeList.stores.flatMap({ $0.secrets }), clear: true)
|
||||||
notifier.prompt()
|
notifier.prompt()
|
||||||
updateSink = updater.$update.sink { update in
|
updateSink = updater.$update.sink { update in
|
||||||
guard let update = update else { return }
|
guard let update = update else { return }
|
||||||
@ -39,6 +44,5 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,8 +18,7 @@ class Notifier {
|
|||||||
let rawDurations = [
|
let rawDurations = [
|
||||||
Measurement(value: 1, unit: UnitDuration.minutes),
|
Measurement(value: 1, unit: UnitDuration.minutes),
|
||||||
Measurement(value: 5, unit: UnitDuration.minutes),
|
Measurement(value: 5, unit: UnitDuration.minutes),
|
||||||
Measurement(value: 1, unit: UnitDuration.hours),
|
Measurement(value: 1, unit: UnitDuration.hours)
|
||||||
Measurement(value: 24, unit: UnitDuration.hours)
|
|
||||||
]
|
]
|
||||||
|
|
||||||
let doNotPersistAction = UNNotificationAction(identifier: Constants.doNotPersistActionIdentitifier, title: "Do Not Unlock", options: [])
|
let doNotPersistAction = UNNotificationAction(identifier: Constants.doNotPersistActionIdentitifier, title: "Do Not Unlock", options: [])
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
5003EF612780081600DF2006 /* SmartCardSecretKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF602780081600DF2006 /* SmartCardSecretKit */; };
|
5003EF612780081600DF2006 /* SmartCardSecretKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF602780081600DF2006 /* SmartCardSecretKit */; };
|
||||||
5003EF632780081B00DF2006 /* SecureEnclaveSecretKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF622780081B00DF2006 /* SecureEnclaveSecretKit */; };
|
5003EF632780081B00DF2006 /* SecureEnclaveSecretKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF622780081B00DF2006 /* SecureEnclaveSecretKit */; };
|
||||||
5003EF652780081B00DF2006 /* SmartCardSecretKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF642780081B00DF2006 /* SmartCardSecretKit */; };
|
5003EF652780081B00DF2006 /* SmartCardSecretKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5003EF642780081B00DF2006 /* SmartCardSecretKit */; };
|
||||||
|
500ED3D427B7934A00A6DC28 /* UpdaterProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5088065927B4A5E40090BD57 /* UpdaterProtocol.swift */; };
|
||||||
|
500ED3DA27B797EE00A6DC28 /* Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = 5081F6D027B790DD0094B82D /* Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
501421622781262300BBAA70 /* Brief in Frameworks */ = {isa = PBXBuildFile; productRef = 501421612781262300BBAA70 /* Brief */; };
|
501421622781262300BBAA70 /* Brief in Frameworks */ = {isa = PBXBuildFile; productRef = 501421612781262300BBAA70 /* Brief */; };
|
||||||
501421652781268000BBAA70 /* SecretAgent.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = 50A3B78A24026B7500D209EA /* SecretAgent.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
501421652781268000BBAA70 /* SecretAgent.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = 50A3B78A24026B7500D209EA /* SecretAgent.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
50153E20250AFCB200525160 /* UpdateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50153E1F250AFCB200525160 /* UpdateView.swift */; };
|
50153E20250AFCB200525160 /* UpdateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50153E1F250AFCB200525160 /* UpdateView.swift */; };
|
||||||
@ -37,6 +39,13 @@
|
|||||||
506772C72424784600034DED /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 506772C62424784600034DED /* Credits.rtf */; };
|
506772C72424784600034DED /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 506772C62424784600034DED /* Credits.rtf */; };
|
||||||
506772C92425BB8500034DED /* NoStoresView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 506772C82425BB8500034DED /* NoStoresView.swift */; };
|
506772C92425BB8500034DED /* NoStoresView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 506772C82425BB8500034DED /* NoStoresView.swift */; };
|
||||||
5079BA0F250F29BF00EA86F4 /* StoreListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5079BA0E250F29BF00EA86F4 /* StoreListView.swift */; };
|
5079BA0F250F29BF00EA86F4 /* StoreListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5079BA0E250F29BF00EA86F4 /* StoreListView.swift */; };
|
||||||
|
5081F6D327B790DD0094B82D /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5081F6D227B790DD0094B82D /* main.swift */; };
|
||||||
|
5081F6D727B790DE0094B82D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5081F6D627B790DE0094B82D /* Assets.xcassets */; };
|
||||||
|
5081F6E027B791110094B82D /* UpdaterProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5088065927B4A5E40090BD57 /* UpdaterProtocol.swift */; };
|
||||||
|
5081F6E127B791110094B82D /* Updater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5088065B27B4A6240090BD57 /* Updater.swift */; };
|
||||||
|
5081F6E327B791620094B82D /* Brief in Frameworks */ = {isa = PBXBuildFile; productRef = 5081F6E227B791620094B82D /* Brief */; };
|
||||||
|
5081F70027B792150094B82D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5081F6FF27B792150094B82D /* Main.storyboard */; };
|
||||||
|
5088068F27B4A6FF0090BD57 /* UpdaterCommunicationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5088068E27B4A6FF0090BD57 /* UpdaterCommunicationController.swift */; };
|
||||||
508A58AA241E06B40069DC07 /* PreviewUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508A58A9241E06B40069DC07 /* PreviewUpdater.swift */; };
|
508A58AA241E06B40069DC07 /* PreviewUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508A58A9241E06B40069DC07 /* PreviewUpdater.swift */; };
|
||||||
508A58B3241ED2180069DC07 /* AgentStatusChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */; };
|
508A58B3241ED2180069DC07 /* AgentStatusChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */; };
|
||||||
508A58B5241ED48F0069DC07 /* PreviewAgentStatusChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508A58B4241ED48F0069DC07 /* PreviewAgentStatusChecker.swift */; };
|
508A58B5241ED48F0069DC07 /* PreviewAgentStatusChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508A58B4241ED48F0069DC07 /* PreviewAgentStatusChecker.swift */; };
|
||||||
@ -53,6 +62,20 @@
|
|||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
500ED3D627B796C800A6DC28 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = 50617D7723FCE48D0099B055 /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = 5081F6CF27B790DD0094B82D;
|
||||||
|
remoteInfo = SecretiveUpdater;
|
||||||
|
};
|
||||||
|
500ED3D827B7978700A6DC28 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = 50617D7723FCE48D0099B055 /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = 5081F6CF27B790DD0094B82D;
|
||||||
|
remoteInfo = SecretiveUpdater;
|
||||||
|
};
|
||||||
50142166278126B500BBAA70 /* PBXContainerItemProxy */ = {
|
50142166278126B500BBAA70 /* PBXContainerItemProxy */ = {
|
||||||
isa = PBXContainerItemProxy;
|
isa = PBXContainerItemProxy;
|
||||||
containerPortal = 50617D7723FCE48D0099B055 /* Project object */;
|
containerPortal = 50617D7723FCE48D0099B055 /* Project object */;
|
||||||
@ -96,6 +119,7 @@
|
|||||||
dstPath = Contents/Library/LoginItems;
|
dstPath = Contents/Library/LoginItems;
|
||||||
dstSubfolderSpec = 1;
|
dstSubfolderSpec = 1;
|
||||||
files = (
|
files = (
|
||||||
|
500ED3DA27B797EE00A6DC28 /* Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater.app in CopyFiles */,
|
||||||
501421652781268000BBAA70 /* SecretAgent.app in CopyFiles */,
|
501421652781268000BBAA70 /* SecretAgent.app in CopyFiles */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@ -129,6 +153,14 @@
|
|||||||
506772C62424784600034DED /* Credits.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = Credits.rtf; sourceTree = "<group>"; };
|
506772C62424784600034DED /* Credits.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = Credits.rtf; sourceTree = "<group>"; };
|
||||||
506772C82425BB8500034DED /* NoStoresView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoStoresView.swift; sourceTree = "<group>"; };
|
506772C82425BB8500034DED /* NoStoresView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoStoresView.swift; sourceTree = "<group>"; };
|
||||||
5079BA0E250F29BF00EA86F4 /* StoreListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreListView.swift; sourceTree = "<group>"; };
|
5079BA0E250F29BF00EA86F4 /* StoreListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreListView.swift; sourceTree = "<group>"; };
|
||||||
|
5081F6D027B790DD0094B82D /* Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
5081F6D227B790DD0094B82D /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
|
||||||
|
5081F6D627B790DE0094B82D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
|
5081F6DB27B790DE0094B82D /* SecretiveUpdater.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SecretiveUpdater.entitlements; sourceTree = "<group>"; };
|
||||||
|
5081F6FF27B792150094B82D /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = "<group>"; };
|
||||||
|
5088065927B4A5E40090BD57 /* UpdaterProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdaterProtocol.swift; sourceTree = "<group>"; };
|
||||||
|
5088065B27B4A6240090BD57 /* Updater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Updater.swift; sourceTree = "<group>"; };
|
||||||
|
5088068E27B4A6FF0090BD57 /* UpdaterCommunicationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 5; lastKnownFileType = sourcecode.swift; path = UpdaterCommunicationController.swift; sourceTree = "<group>"; };
|
||||||
508A58A9241E06B40069DC07 /* PreviewUpdater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewUpdater.swift; sourceTree = "<group>"; };
|
508A58A9241E06B40069DC07 /* PreviewUpdater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewUpdater.swift; sourceTree = "<group>"; };
|
||||||
508A58AB241E121B0069DC07 /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = "<group>"; };
|
508A58AB241E121B0069DC07 /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = "<group>"; };
|
||||||
508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgentStatusChecker.swift; sourceTree = "<group>"; };
|
508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgentStatusChecker.swift; sourceTree = "<group>"; };
|
||||||
@ -168,6 +200,14 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
5081F6CD27B790DD0094B82D /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
5081F6E327B791620094B82D /* Brief in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
50A3B78724026B7500D209EA /* Frameworks */ = {
|
50A3B78724026B7500D209EA /* Frameworks */ = {
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@ -199,6 +239,7 @@
|
|||||||
50617D9723FCE48E0099B055 /* SecretiveTests */,
|
50617D9723FCE48E0099B055 /* SecretiveTests */,
|
||||||
50A3B78B24026B7500D209EA /* SecretAgent */,
|
50A3B78B24026B7500D209EA /* SecretAgent */,
|
||||||
508A58AF241E144C0069DC07 /* Config */,
|
508A58AF241E144C0069DC07 /* Config */,
|
||||||
|
5081F6D127B790DD0094B82D /* SecretiveUpdater */,
|
||||||
50617D8023FCE48E0099B055 /* Products */,
|
50617D8023FCE48E0099B055 /* Products */,
|
||||||
5099A08B240243730062B6F2 /* Frameworks */,
|
5099A08B240243730062B6F2 /* Frameworks */,
|
||||||
);
|
);
|
||||||
@ -210,6 +251,7 @@
|
|||||||
50617D7F23FCE48E0099B055 /* Secretive.app */,
|
50617D7F23FCE48E0099B055 /* Secretive.app */,
|
||||||
50617D9423FCE48E0099B055 /* SecretiveTests.xctest */,
|
50617D9423FCE48E0099B055 /* SecretiveTests.xctest */,
|
||||||
50A3B78A24026B7500D209EA /* SecretAgent.app */,
|
50A3B78A24026B7500D209EA /* SecretAgent.app */,
|
||||||
|
5081F6D027B790DD0094B82D /* Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater.app */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -251,6 +293,19 @@
|
|||||||
path = SecretiveTests;
|
path = SecretiveTests;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
5081F6D127B790DD0094B82D /* SecretiveUpdater */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
5081F6D227B790DD0094B82D /* main.swift */,
|
||||||
|
5088065927B4A5E40090BD57 /* UpdaterProtocol.swift */,
|
||||||
|
5088065B27B4A6240090BD57 /* Updater.swift */,
|
||||||
|
5081F6FF27B792150094B82D /* Main.storyboard */,
|
||||||
|
5081F6D627B790DE0094B82D /* Assets.xcassets */,
|
||||||
|
5081F6DB27B790DE0094B82D /* SecretiveUpdater.entitlements */,
|
||||||
|
);
|
||||||
|
path = SecretiveUpdater;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
508A58AF241E144C0069DC07 /* Config */ = {
|
508A58AF241E144C0069DC07 /* Config */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -282,11 +337,12 @@
|
|||||||
508A58B1241ED1EA0069DC07 /* Controllers */ = {
|
508A58B1241ED1EA0069DC07 /* Controllers */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
5088068E27B4A6FF0090BD57 /* UpdaterCommunicationController.swift */,
|
||||||
508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */,
|
508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */,
|
||||||
5091D2BB25183B830049FD9B /* ApplicationDirectoryController.swift */,
|
5091D2BB25183B830049FD9B /* ApplicationDirectoryController.swift */,
|
||||||
50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */,
|
50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */,
|
||||||
50571E0424393D1500F76F6C /* LaunchAgentController.swift */,
|
|
||||||
5066A6F6251829B1004B5A36 /* ShellConfigurationController.swift */,
|
5066A6F6251829B1004B5A36 /* ShellConfigurationController.swift */,
|
||||||
|
50571E0424393D1500F76F6C /* LaunchAgentController.swift */,
|
||||||
);
|
);
|
||||||
path = Controllers;
|
path = Controllers;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -338,6 +394,8 @@
|
|||||||
);
|
);
|
||||||
dependencies = (
|
dependencies = (
|
||||||
50142167278126B500BBAA70 /* PBXTargetDependency */,
|
50142167278126B500BBAA70 /* PBXTargetDependency */,
|
||||||
|
500ED3D727B796C800A6DC28 /* PBXTargetDependency */,
|
||||||
|
500ED3D927B7978700A6DC28 /* PBXTargetDependency */,
|
||||||
);
|
);
|
||||||
name = Secretive;
|
name = Secretive;
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
@ -368,6 +426,26 @@
|
|||||||
productReference = 50617D9423FCE48E0099B055 /* SecretiveTests.xctest */;
|
productReference = 50617D9423FCE48E0099B055 /* SecretiveTests.xctest */;
|
||||||
productType = "com.apple.product-type.bundle.unit-test";
|
productType = "com.apple.product-type.bundle.unit-test";
|
||||||
};
|
};
|
||||||
|
5081F6CF27B790DD0094B82D /* SecretiveUpdater */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 5081F6DC27B790DE0094B82D /* Build configuration list for PBXNativeTarget "SecretiveUpdater" */;
|
||||||
|
buildPhases = (
|
||||||
|
5081F6CC27B790DD0094B82D /* Sources */,
|
||||||
|
5081F6CD27B790DD0094B82D /* Frameworks */,
|
||||||
|
5081F6CE27B790DD0094B82D /* Resources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = SecretiveUpdater;
|
||||||
|
packageProductDependencies = (
|
||||||
|
5081F6E227B791620094B82D /* Brief */,
|
||||||
|
);
|
||||||
|
productName = SecretiveUpdater;
|
||||||
|
productReference = 5081F6D027B790DD0094B82D /* Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater.app */;
|
||||||
|
productType = "com.apple.product-type.application";
|
||||||
|
};
|
||||||
50A3B78924026B7500D209EA /* SecretAgent */ = {
|
50A3B78924026B7500D209EA /* SecretAgent */ = {
|
||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 50A3B79A24026B7600D209EA /* Build configuration list for PBXNativeTarget "SecretAgent" */;
|
buildConfigurationList = 50A3B79A24026B7600D209EA /* Build configuration list for PBXNativeTarget "SecretAgent" */;
|
||||||
@ -399,7 +477,7 @@
|
|||||||
50617D7723FCE48D0099B055 /* Project object */ = {
|
50617D7723FCE48D0099B055 /* Project object */ = {
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
LastSwiftUpdateCheck = 1220;
|
LastSwiftUpdateCheck = 1330;
|
||||||
LastUpgradeCheck = 1320;
|
LastUpgradeCheck = 1320;
|
||||||
ORGANIZATIONNAME = "Max Goedjen";
|
ORGANIZATIONNAME = "Max Goedjen";
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
@ -410,6 +488,9 @@
|
|||||||
CreatedOnToolsVersion = 11.3;
|
CreatedOnToolsVersion = 11.3;
|
||||||
TestTargetID = 50617D7E23FCE48D0099B055;
|
TestTargetID = 50617D7E23FCE48D0099B055;
|
||||||
};
|
};
|
||||||
|
5081F6CF27B790DD0094B82D = {
|
||||||
|
CreatedOnToolsVersion = 13.3;
|
||||||
|
};
|
||||||
50A3B78924026B7500D209EA = {
|
50A3B78924026B7500D209EA = {
|
||||||
CreatedOnToolsVersion = 11.4;
|
CreatedOnToolsVersion = 11.4;
|
||||||
};
|
};
|
||||||
@ -431,6 +512,7 @@
|
|||||||
50617D7E23FCE48D0099B055 /* Secretive */,
|
50617D7E23FCE48D0099B055 /* Secretive */,
|
||||||
50617D9323FCE48E0099B055 /* SecretiveTests */,
|
50617D9323FCE48E0099B055 /* SecretiveTests */,
|
||||||
50A3B78924026B7500D209EA /* SecretAgent */,
|
50A3B78924026B7500D209EA /* SecretAgent */,
|
||||||
|
5081F6CF27B790DD0094B82D /* SecretiveUpdater */,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
/* End PBXProject section */
|
/* End PBXProject section */
|
||||||
@ -454,6 +536,15 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
5081F6CE27B790DD0094B82D /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
5081F6D727B790DE0094B82D /* Assets.xcassets in Resources */,
|
||||||
|
5081F70027B792150094B82D /* Main.storyboard in Resources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
50A3B78824026B7500D209EA /* Resources */ = {
|
50A3B78824026B7500D209EA /* Resources */ = {
|
||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@ -479,6 +570,7 @@
|
|||||||
50571E0324393C2600F76F6C /* JustUpdatedChecker.swift in Sources */,
|
50571E0324393C2600F76F6C /* JustUpdatedChecker.swift in Sources */,
|
||||||
5079BA0F250F29BF00EA86F4 /* StoreListView.swift in Sources */,
|
5079BA0F250F29BF00EA86F4 /* StoreListView.swift in Sources */,
|
||||||
50617DD223FCEFA90099B055 /* PreviewStore.swift in Sources */,
|
50617DD223FCEFA90099B055 /* PreviewStore.swift in Sources */,
|
||||||
|
500ED3D427B7934A00A6DC28 /* UpdaterProtocol.swift in Sources */,
|
||||||
5066A6F7251829B1004B5A36 /* ShellConfigurationController.swift in Sources */,
|
5066A6F7251829B1004B5A36 /* ShellConfigurationController.swift in Sources */,
|
||||||
50033AC327813F1700253856 /* BundleIDs.swift in Sources */,
|
50033AC327813F1700253856 /* BundleIDs.swift in Sources */,
|
||||||
508A58B3241ED2180069DC07 /* AgentStatusChecker.swift in Sources */,
|
508A58B3241ED2180069DC07 /* AgentStatusChecker.swift in Sources */,
|
||||||
@ -487,6 +579,7 @@
|
|||||||
50153E20250AFCB200525160 /* UpdateView.swift in Sources */,
|
50153E20250AFCB200525160 /* UpdateView.swift in Sources */,
|
||||||
50571E0524393D1500F76F6C /* LaunchAgentController.swift in Sources */,
|
50571E0524393D1500F76F6C /* LaunchAgentController.swift in Sources */,
|
||||||
5066A6C82516FE6E004B5A36 /* CopyableView.swift in Sources */,
|
5066A6C82516FE6E004B5A36 /* CopyableView.swift in Sources */,
|
||||||
|
5088068F27B4A6FF0090BD57 /* UpdaterCommunicationController.swift in Sources */,
|
||||||
50B8550D24138C4F009958AC /* DeleteSecretView.swift in Sources */,
|
50B8550D24138C4F009958AC /* DeleteSecretView.swift in Sources */,
|
||||||
50BB046B2418AAAE00D6E079 /* EmptyStoreView.swift in Sources */,
|
50BB046B2418AAAE00D6E079 /* EmptyStoreView.swift in Sources */,
|
||||||
50617D8323FCE48E0099B055 /* App.swift in Sources */,
|
50617D8323FCE48E0099B055 /* App.swift in Sources */,
|
||||||
@ -505,6 +598,16 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
5081F6CC27B790DD0094B82D /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
5081F6D327B790DD0094B82D /* main.swift in Sources */,
|
||||||
|
5081F6E027B791110094B82D /* UpdaterProtocol.swift in Sources */,
|
||||||
|
5081F6E127B791110094B82D /* Updater.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
50A3B78624026B7500D209EA /* Sources */ = {
|
50A3B78624026B7500D209EA /* Sources */ = {
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@ -517,6 +620,16 @@
|
|||||||
/* End PBXSourcesBuildPhase section */
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXTargetDependency section */
|
/* Begin PBXTargetDependency section */
|
||||||
|
500ED3D727B796C800A6DC28 /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = 5081F6CF27B790DD0094B82D /* SecretiveUpdater */;
|
||||||
|
targetProxy = 500ED3D627B796C800A6DC28 /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
|
500ED3D927B7978700A6DC28 /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = 5081F6CF27B790DD0094B82D /* SecretiveUpdater */;
|
||||||
|
targetProxy = 500ED3D827B7978700A6DC28 /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
50142167278126B500BBAA70 /* PBXTargetDependency */ = {
|
50142167278126B500BBAA70 /* PBXTargetDependency */ = {
|
||||||
isa = PBXTargetDependency;
|
isa = PBXTargetDependency;
|
||||||
target = 50A3B78924026B7500D209EA /* SecretAgent */;
|
target = 50A3B78924026B7500D209EA /* SecretAgent */;
|
||||||
@ -760,6 +873,96 @@
|
|||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
5081F6DD27B790DE0094B82D /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
||||||
|
CODE_SIGN_ENTITLEMENTS = SecretiveUpdater/SecretiveUpdater.entitlements;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||||
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = SecretiveUpdater/Info.plist;
|
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 Max Goedjen. All rights reserved.";
|
||||||
|
INFOPLIST_KEY_NSMainStoryboardFile = Main;
|
||||||
|
INFOPLIST_KEY_NSPrincipalClass = NSApplication;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/../Frameworks",
|
||||||
|
);
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 12.3;
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater;
|
||||||
|
PRODUCT_NAME = Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
5081F6DE27B790DE0094B82D /* Test */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
||||||
|
CODE_SIGN_ENTITLEMENTS = SecretiveUpdater/SecretiveUpdater.entitlements;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||||
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = SecretiveUpdater/Info.plist;
|
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 Max Goedjen. All rights reserved.";
|
||||||
|
INFOPLIST_KEY_NSMainStoryboardFile = Main;
|
||||||
|
INFOPLIST_KEY_NSPrincipalClass = NSApplication;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/../Frameworks",
|
||||||
|
);
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 12.3;
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater;
|
||||||
|
PRODUCT_NAME = Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
};
|
||||||
|
name = Test;
|
||||||
|
};
|
||||||
|
5081F6DF27B790DE0094B82D /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
||||||
|
CODE_SIGN_ENTITLEMENTS = SecretiveUpdater/SecretiveUpdater.entitlements;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_TEAM = Z72PRUAWF6;
|
||||||
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = SecretiveUpdater/Info.plist;
|
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 Max Goedjen. All rights reserved.";
|
||||||
|
INFOPLIST_KEY_NSMainStoryboardFile = Main;
|
||||||
|
INFOPLIST_KEY_NSPrincipalClass = NSApplication;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/../Frameworks",
|
||||||
|
);
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 12.3;
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater;
|
||||||
|
PRODUCT_NAME = Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
508A5914241EF1A00069DC07 /* Test */ = {
|
508A5914241EF1A00069DC07 /* Test */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 508A58AB241E121B0069DC07 /* Config.xcconfig */;
|
baseConfigurationReference = 508A58AB241E121B0069DC07 /* Config.xcconfig */;
|
||||||
@ -830,6 +1033,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CODE_SIGN_ENTITLEMENTS = Secretive/Secretive.entitlements;
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
@ -972,6 +1176,16 @@
|
|||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
|
5081F6DC27B790DE0094B82D /* Build configuration list for PBXNativeTarget "SecretiveUpdater" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
5081F6DD27B790DE0094B82D /* Debug */,
|
||||||
|
5081F6DE27B790DE0094B82D /* Test */,
|
||||||
|
5081F6DF27B790DE0094B82D /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
50A3B79A24026B7600D209EA /* Build configuration list for PBXNativeTarget "SecretAgent" */ = {
|
50A3B79A24026B7600D209EA /* Build configuration list for PBXNativeTarget "SecretAgent" */ = {
|
||||||
isa = XCConfigurationList;
|
isa = XCConfigurationList;
|
||||||
buildConfigurations = (
|
buildConfigurations = (
|
||||||
@ -1021,6 +1235,10 @@
|
|||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
productName = Brief;
|
productName = Brief;
|
||||||
};
|
};
|
||||||
|
5081F6E227B791620094B82D /* Brief */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
productName = Brief;
|
||||||
|
};
|
||||||
/* End XCSwiftPackageProductDependency section */
|
/* End XCSwiftPackageProductDependency section */
|
||||||
};
|
};
|
||||||
rootObject = 50617D7723FCE48D0099B055 /* Project object */;
|
rootObject = 50617D7723FCE48D0099B055 /* Project object */;
|
||||||
|
@ -0,0 +1,78 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "1330"
|
||||||
|
version = "1.3">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "5081F6CF27B790DD0094B82D"
|
||||||
|
BuildableName = "Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater.app"
|
||||||
|
BlueprintName = "SecretiveUpdater"
|
||||||
|
ReferencedContainer = "container:Secretive.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<Testables>
|
||||||
|
</Testables>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "5081F6CF27B790DD0094B82D"
|
||||||
|
BuildableName = "Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater.app"
|
||||||
|
BlueprintName = "SecretiveUpdater"
|
||||||
|
ReferencedContainer = "container:Secretive.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "5081F6CF27B790DD0094B82D"
|
||||||
|
BuildableName = "Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater.app"
|
||||||
|
BlueprintName = "SecretiveUpdater"
|
||||||
|
ReferencedContainer = "container:Secretive.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
@ -16,6 +16,7 @@ struct Secretive: App {
|
|||||||
}()
|
}()
|
||||||
private let agentStatusChecker = AgentStatusChecker()
|
private let agentStatusChecker = AgentStatusChecker()
|
||||||
private let justUpdatedChecker = JustUpdatedChecker()
|
private let justUpdatedChecker = JustUpdatedChecker()
|
||||||
|
private let updaterController = UpdaterCommunicationController()
|
||||||
|
|
||||||
@AppStorage("defaultsHasRunSetup") var hasRunSetup = false
|
@AppStorage("defaultsHasRunSetup") var hasRunSetup = false
|
||||||
@State private var showingSetup = false
|
@State private var showingSetup = false
|
||||||
@ -23,11 +24,12 @@ struct Secretive: App {
|
|||||||
|
|
||||||
@SceneBuilder var body: some Scene {
|
@SceneBuilder var body: some Scene {
|
||||||
WindowGroup {
|
WindowGroup {
|
||||||
ContentView<Updater, AgentStatusChecker>(showingCreation: $showingCreation, runningSetup: $showingSetup, hasRunSetup: $hasRunSetup)
|
ContentView<UpdateChecker, AgentStatusChecker>(showingCreation: $showingCreation, runningSetup: $showingSetup, hasRunSetup: $hasRunSetup)
|
||||||
.environmentObject(storeList)
|
.environmentObject(storeList)
|
||||||
.environmentObject(Updater(checkOnLaunch: hasRunSetup))
|
.environmentObject(UpdateChecker(checkOnLaunch: hasRunSetup))
|
||||||
.environmentObject(agentStatusChecker)
|
.environmentObject(agentStatusChecker)
|
||||||
.onAppear {
|
.onAppear {
|
||||||
|
updaterController.installUpdate(url: URL(string: "https://github.com/maxgoedjen/secretive/releases/download/v2.1.1/Secretive.zip")!)
|
||||||
if !hasRunSetup {
|
if !hasRunSetup {
|
||||||
showingSetup = true
|
showingSetup = true
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
import Foundation
|
||||||
|
import Combine
|
||||||
|
import AppKit
|
||||||
|
import OSLog
|
||||||
|
import SecretKit
|
||||||
|
//import SecretiveUpdater
|
||||||
|
import ServiceManagement
|
||||||
|
|
||||||
|
class UpdaterCommunicationController: ObservableObject {
|
||||||
|
|
||||||
|
private(set) var updater: UpdaterProtocol? = nil
|
||||||
|
private var connection: NSXPCConnection? = nil
|
||||||
|
private var running = false
|
||||||
|
|
||||||
|
init() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func installUpdate(url: URL) {
|
||||||
|
guard !running else { return }
|
||||||
|
_ = SMLoginItemSetEnabled(Bundle.main.updaterBundleID as CFString, false)
|
||||||
|
SMLoginItemSetEnabled(Bundle.main.updaterBundleID as CFString, true)
|
||||||
|
connection = NSXPCConnection(machServiceName: Bundle.main.updaterBundleID)
|
||||||
|
connection?.remoteObjectInterface = NSXPCInterface(with: UpdaterProtocol.self)
|
||||||
|
connection?.invalidationHandler = {
|
||||||
|
Logger().warning("XPC connection invalidated")
|
||||||
|
}
|
||||||
|
connection?.resume()
|
||||||
|
updater = connection?.remoteObjectProxyWithErrorHandler({ error in
|
||||||
|
Logger().error("\(String(describing: error))")
|
||||||
|
}) as? UpdaterProtocol
|
||||||
|
running = true
|
||||||
|
let existingURL = Bundle.main.bundleURL
|
||||||
|
Task {
|
||||||
|
let result = try await updater?.installUpdate(url: url, to: existingURL)
|
||||||
|
print(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -4,4 +4,5 @@ import Foundation
|
|||||||
extension Bundle {
|
extension Bundle {
|
||||||
public var agentBundleID: String {(self.bundleIdentifier?.replacingOccurrences(of: "Host", with: "SecretAgent"))!}
|
public var agentBundleID: String {(self.bundleIdentifier?.replacingOccurrences(of: "Host", with: "SecretAgent"))!}
|
||||||
public var hostBundleID: String {(self.bundleIdentifier?.replacingOccurrences(of: "SecretAgent", with: "Host"))!}
|
public var hostBundleID: String {(self.bundleIdentifier?.replacingOccurrences(of: "SecretAgent", with: "Host"))!}
|
||||||
|
public var updaterBundleID: String { "Z72PRUAWF6.com.maxgoedjen.SecretiveUpdater" }
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import Foundation
|
|||||||
import Combine
|
import Combine
|
||||||
import Brief
|
import Brief
|
||||||
|
|
||||||
class PreviewUpdater: UpdaterProtocol {
|
class PreviewUpdater: UpdateCheckerProtocol {
|
||||||
|
|
||||||
let update: Release?
|
let update: Release?
|
||||||
let testBuild = false
|
let testBuild = false
|
||||||
|
@ -4,6 +4,10 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>com.apple.security.app-sandbox</key>
|
<key>com.apple.security.app-sandbox</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>$(TeamIdentifierPrefix)com.maxgoedjen.Secretive</string>
|
||||||
|
</array>
|
||||||
<key>com.apple.security.files.user-selected.read-write</key>
|
<key>com.apple.security.files.user-selected.read-write</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.network.client</key>
|
<key>com.apple.security.network.client</key>
|
||||||
|
@ -4,7 +4,7 @@ import SecureEnclaveSecretKit
|
|||||||
import SmartCardSecretKit
|
import SmartCardSecretKit
|
||||||
import Brief
|
import Brief
|
||||||
|
|
||||||
struct ContentView<UpdaterType: UpdaterProtocol, AgentStatusCheckerType: AgentStatusCheckerProtocol>: View {
|
struct ContentView<UpdaterType: UpdateCheckerProtocol, AgentStatusCheckerType: AgentStatusCheckerProtocol>: View {
|
||||||
|
|
||||||
@Binding var showingCreation: Bool
|
@Binding var showingCreation: Bool
|
||||||
@Binding var runningSetup: Bool
|
@Binding var runningSetup: Bool
|
||||||
@ -32,6 +32,9 @@ struct ContentView<UpdaterType: UpdaterProtocol, AgentStatusCheckerType: AgentSt
|
|||||||
appPathNotice
|
appPathNotice
|
||||||
newItem
|
newItem
|
||||||
}
|
}
|
||||||
|
.sheet(isPresented: $runningSetup) {
|
||||||
|
SetupView(visible: $runningSetup, setupComplete: $hasRunSetup)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -65,11 +68,11 @@ extension ContentView {
|
|||||||
.font(.headline)
|
.font(.headline)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
})
|
})
|
||||||
.background(color)
|
.background(color)
|
||||||
.cornerRadius(5)
|
.cornerRadius(5)
|
||||||
.popover(item: $selectedUpdate, attachmentAnchor: .point(.bottom), arrowEdge: .bottom) { update in
|
.popover(item: $selectedUpdate, attachmentAnchor: .point(.bottom), arrowEdge: .bottom) { update in
|
||||||
UpdateDetailView(update: update)
|
UpdateDetailView(update: update)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,11 +88,11 @@ extension ContentView {
|
|||||||
}, label: {
|
}, label: {
|
||||||
Image(systemName: "plus")
|
Image(systemName: "plus")
|
||||||
})
|
})
|
||||||
.popover(isPresented: $showingCreation, attachmentAnchor: .point(.bottom), arrowEdge: .bottom) {
|
.popover(isPresented: $showingCreation, attachmentAnchor: .point(.bottom), arrowEdge: .bottom) {
|
||||||
if let modifiable = storeList.modifiableStore {
|
if let modifiable = storeList.modifiableStore {
|
||||||
CreateSecretView(store: modifiable, showing: $showingCreation)
|
CreateSecretView(store: modifiable, showing: $showingCreation)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -113,15 +116,12 @@ extension ContentView {
|
|||||||
.font(.headline)
|
.font(.headline)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
})
|
})
|
||||||
.background(Color.orange)
|
.background(Color.orange)
|
||||||
.cornerRadius(5)
|
.cornerRadius(5)
|
||||||
} else {
|
} else {
|
||||||
EmptyView()
|
EmptyView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.sheet(isPresented: $runningSetup) {
|
|
||||||
SetupView(visible: $runningSetup, setupComplete: $hasRunSetup)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -142,19 +142,19 @@ extension ContentView {
|
|||||||
.font(.headline)
|
.font(.headline)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
})
|
})
|
||||||
.background(Color.orange)
|
.background(Color.orange)
|
||||||
.cornerRadius(5)
|
.cornerRadius(5)
|
||||||
.popover(isPresented: $showingAppPathNotice, attachmentAnchor: .point(.bottom), arrowEdge: .bottom) {
|
.popover(isPresented: $showingAppPathNotice, attachmentAnchor: .point(.bottom), arrowEdge: .bottom) {
|
||||||
VStack {
|
VStack {
|
||||||
Image(systemName: "exclamationmark.triangle")
|
Image(systemName: "exclamationmark.triangle")
|
||||||
.resizable()
|
.resizable()
|
||||||
.aspectRatio(contentMode: .fit)
|
.aspectRatio(contentMode: .fit)
|
||||||
.frame(width: 64)
|
.frame(width: 64)
|
||||||
Text("Secretive needs to be in your Applications folder to work properly. Please move it and relaunch.")
|
Text("Secretive needs to be in your Applications folder to work properly. Please move it and relaunch.")
|
||||||
.frame(maxWidth: 300)
|
.frame(maxWidth: 300)
|
||||||
}
|
|
||||||
.padding()
|
|
||||||
}
|
}
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ struct SecretDetailView<SecretType: Secret>: View {
|
|||||||
@State var secret: SecretType
|
@State var secret: SecretType
|
||||||
|
|
||||||
private let keyWriter = OpenSSHKeyWriter()
|
private let keyWriter = OpenSSHKeyWriter()
|
||||||
|
private let publicKeyFileStoreController = PublicKeyFileStoreController(homeDirectory: NSHomeDirectory().replacingOccurrences(of: Bundle.main.hostBundleID, with: Bundle.main.agentBundleID))
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
@ -18,6 +19,9 @@ struct SecretDetailView<SecretType: Secret>: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
.frame(height: 20)
|
.frame(height: 20)
|
||||||
CopyableView(title: "Public Key", image: Image(systemName: "key"), text: keyString)
|
CopyableView(title: "Public Key", image: Image(systemName: "key"), text: keyString)
|
||||||
|
Spacer()
|
||||||
|
.frame(height: 20)
|
||||||
|
CopyableView(title: "Public Key Path", image: Image(systemName: "lock.doc"), text: publicKeyFileStoreController.path(for: secret))
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -41,11 +45,6 @@ struct SecretDetailView<SecretType: Secret>: View {
|
|||||||
keyWriter.openSSHString(secret: secret, comment: "\(dashedKeyName)@\(dashedHostName)")
|
keyWriter.openSSHString(secret: secret, comment: "\(dashedKeyName)@\(dashedHostName)")
|
||||||
}
|
}
|
||||||
|
|
||||||
func copy() {
|
|
||||||
NSPasteboard.general.declareTypes([.string], owner: nil)
|
|
||||||
NSPasteboard.general.setString(keyString, forType: .string)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
@ -22,7 +22,7 @@ struct SetupView: View {
|
|||||||
}
|
}
|
||||||
.frame(width: proxy.size.width)
|
.frame(width: proxy.size.width)
|
||||||
}
|
}
|
||||||
.offset(x: -proxy.size.width * CGFloat(stepIndex), y: 0)
|
.offset(x: -proxy.size.width * Double(stepIndex), y: 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -44,7 +44,7 @@ struct StepView: View {
|
|||||||
let currentStep: Int
|
let currentStep: Int
|
||||||
|
|
||||||
// Ideally we'd have a geometry reader inside this view doing this for us, but that crashes on 11.0b7
|
// Ideally we'd have a geometry reader inside this view doing this for us, but that crashes on 11.0b7
|
||||||
let width: CGFloat
|
let width: Double
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack(alignment: .leading) {
|
ZStack(alignment: .leading) {
|
||||||
@ -53,7 +53,7 @@ struct StepView: View {
|
|||||||
.frame(height: 5)
|
.frame(height: 5)
|
||||||
Rectangle()
|
Rectangle()
|
||||||
.foregroundColor(.green)
|
.foregroundColor(.green)
|
||||||
.frame(width: max(0, ((width - (Constants.padding * 2)) / CGFloat(numberOfSteps - 1)) * CGFloat(currentStep) - (Constants.circleWidth / 2)), height: 5)
|
.frame(width: max(0, ((width - (Constants.padding * 2)) / Double(numberOfSteps - 1)) * Double(currentStep) - (Constants.circleWidth / 2)), height: 5)
|
||||||
HStack {
|
HStack {
|
||||||
ForEach(0..<numberOfSteps) { index in
|
ForEach(0..<numberOfSteps) { index in
|
||||||
ZStack {
|
ZStack {
|
||||||
@ -92,8 +92,8 @@ extension StepView {
|
|||||||
|
|
||||||
enum Constants {
|
enum Constants {
|
||||||
|
|
||||||
static let padding: CGFloat = 15
|
static let padding: Double = 15
|
||||||
static let circleWidth: CGFloat = 30
|
static let circleWidth: Double = 30
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import Brief
|
import Brief
|
||||||
|
|
||||||
struct UpdateDetailView<UpdaterType: Updater>: View {
|
struct UpdateDetailView<UpdaterType: UpdateChecker>: View {
|
||||||
|
|
||||||
@EnvironmentObject var updater: UpdaterType
|
@EnvironmentObject var updater: UpdaterType
|
||||||
|
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "mac",
|
||||||
|
"scale" : "1x",
|
||||||
|
"size" : "16x16"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "mac",
|
||||||
|
"scale" : "2x",
|
||||||
|
"size" : "16x16"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "mac",
|
||||||
|
"scale" : "1x",
|
||||||
|
"size" : "32x32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "mac",
|
||||||
|
"scale" : "2x",
|
||||||
|
"size" : "32x32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "mac",
|
||||||
|
"scale" : "1x",
|
||||||
|
"size" : "128x128"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "mac",
|
||||||
|
"scale" : "2x",
|
||||||
|
"size" : "128x128"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Mac Icon.png",
|
||||||
|
"idiom" : "mac",
|
||||||
|
"scale" : "1x",
|
||||||
|
"size" : "256x256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Mac Icon@0.25x.png",
|
||||||
|
"idiom" : "mac",
|
||||||
|
"scale" : "2x",
|
||||||
|
"size" : "256x256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "mac",
|
||||||
|
"scale" : "1x",
|
||||||
|
"size" : "512x512"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "mac",
|
||||||
|
"scale" : "2x",
|
||||||
|
"size" : "512x512"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
After Width: | Height: | Size: 40 KiB |
6
Sources/SecretiveUpdater/Assets.xcassets/Contents.json
Normal file
6
Sources/SecretiveUpdater/Assets.xcassets/Contents.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
8
Sources/SecretiveUpdater/Info.plist
Normal file
8
Sources/SecretiveUpdater/Info.plist
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>LSUIElement</key>
|
||||||
|
<false/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
683
Sources/SecretiveUpdater/Main.storyboard
Normal file
683
Sources/SecretiveUpdater/Main.storyboard
Normal file
@ -0,0 +1,683 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="20036.2" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||||
|
<dependencies>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="20036.2"/>
|
||||||
|
</dependencies>
|
||||||
|
<scenes>
|
||||||
|
<!--Application-->
|
||||||
|
<scene sceneID="JPo-4y-FX3">
|
||||||
|
<objects>
|
||||||
|
<application id="hnw-xV-0zn" sceneMemberID="viewController">
|
||||||
|
<menu key="mainMenu" title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
|
||||||
|
<items>
|
||||||
|
<menuItem title="SecretiveUpdater" id="1Xt-HY-uBw">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<menu key="submenu" title="SecretiveUpdater" systemMenu="apple" id="uQy-DD-JDr">
|
||||||
|
<items>
|
||||||
|
<menuItem title="About SecretiveUpdater" id="5kV-Vb-QxS">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="orderFrontStandardAboutPanel:" target="Ady-hI-5gd" id="Exp-CZ-Vem"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
|
||||||
|
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
|
||||||
|
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
|
||||||
|
<menuItem title="Services" id="NMo-om-nkz">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
|
||||||
|
<menuItem title="Hide SecretiveUpdater" keyEquivalent="h" id="Olw-nP-bQN">
|
||||||
|
<connections>
|
||||||
|
<action selector="hide:" target="Ady-hI-5gd" id="PnN-Uc-m68"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="hideOtherApplications:" target="Ady-hI-5gd" id="VT4-aY-XCT"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Show All" id="Kd2-mp-pUS">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="unhideAllApplications:" target="Ady-hI-5gd" id="Dhg-Le-xox"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
|
||||||
|
<menuItem title="Quit SecretiveUpdater" keyEquivalent="q" id="4sb-4s-VLi">
|
||||||
|
<connections>
|
||||||
|
<action selector="terminate:" target="Ady-hI-5gd" id="Te7-pn-YzF"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
</items>
|
||||||
|
</menu>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="File" id="dMs-cI-mzQ">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<menu key="submenu" title="File" id="bib-Uj-vzu">
|
||||||
|
<items>
|
||||||
|
<menuItem title="New" keyEquivalent="n" id="Was-JA-tGl">
|
||||||
|
<connections>
|
||||||
|
<action selector="newDocument:" target="Ady-hI-5gd" id="4Si-XN-c54"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Open…" keyEquivalent="o" id="IAo-SY-fd9">
|
||||||
|
<connections>
|
||||||
|
<action selector="openDocument:" target="Ady-hI-5gd" id="bVn-NM-KNZ"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Open Recent" id="tXI-mr-wws">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="oas-Oc-fiZ">
|
||||||
|
<items>
|
||||||
|
<menuItem title="Clear Menu" id="vNY-rz-j42">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="clearRecentDocuments:" target="Ady-hI-5gd" id="Daa-9d-B3U"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
</items>
|
||||||
|
</menu>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
|
||||||
|
<menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
|
||||||
|
<connections>
|
||||||
|
<action selector="performClose:" target="Ady-hI-5gd" id="HmO-Ls-i7Q"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Save…" keyEquivalent="s" id="pxx-59-PXV">
|
||||||
|
<connections>
|
||||||
|
<action selector="saveDocument:" target="Ady-hI-5gd" id="teZ-XB-qJY"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Save As…" keyEquivalent="S" id="Bw7-FT-i3A">
|
||||||
|
<connections>
|
||||||
|
<action selector="saveDocumentAs:" target="Ady-hI-5gd" id="mDf-zr-I0C"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Revert to Saved" keyEquivalent="r" id="KaW-ft-85H">
|
||||||
|
<connections>
|
||||||
|
<action selector="revertDocumentToSaved:" target="Ady-hI-5gd" id="iJ3-Pv-kwq"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem isSeparatorItem="YES" id="aJh-i4-bef"/>
|
||||||
|
<menuItem title="Page Setup…" keyEquivalent="P" id="qIS-W8-SiK">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="runPageLayout:" target="Ady-hI-5gd" id="Din-rz-gC5"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Print…" keyEquivalent="p" id="aTl-1u-JFS">
|
||||||
|
<connections>
|
||||||
|
<action selector="print:" target="Ady-hI-5gd" id="qaZ-4w-aoO"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
</items>
|
||||||
|
</menu>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Edit" id="5QF-Oa-p0T">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<menu key="submenu" title="Edit" id="W48-6f-4Dl">
|
||||||
|
<items>
|
||||||
|
<menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
|
||||||
|
<connections>
|
||||||
|
<action selector="undo:" target="Ady-hI-5gd" id="M6e-cu-g7V"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
|
||||||
|
<connections>
|
||||||
|
<action selector="redo:" target="Ady-hI-5gd" id="oIA-Rs-6OD"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
|
||||||
|
<menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
|
||||||
|
<connections>
|
||||||
|
<action selector="cut:" target="Ady-hI-5gd" id="YJe-68-I9s"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
|
||||||
|
<connections>
|
||||||
|
<action selector="copy:" target="Ady-hI-5gd" id="G1f-GL-Joy"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
|
||||||
|
<connections>
|
||||||
|
<action selector="paste:" target="Ady-hI-5gd" id="UvS-8e-Qdg"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="pasteAsPlainText:" target="Ady-hI-5gd" id="cEh-KX-wJQ"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Delete" id="pa3-QI-u2k">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="delete:" target="Ady-hI-5gd" id="0Mk-Ml-PaM"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
|
||||||
|
<connections>
|
||||||
|
<action selector="selectAll:" target="Ady-hI-5gd" id="VNm-Mi-diN"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
|
||||||
|
<menuItem title="Find" id="4EN-yA-p0u">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<menu key="submenu" title="Find" id="1b7-l0-nxx">
|
||||||
|
<items>
|
||||||
|
<menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
|
||||||
|
<connections>
|
||||||
|
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="cD7-Qs-BN4"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="WD3-Gg-5AJ"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
|
||||||
|
<connections>
|
||||||
|
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="NDo-RZ-v9R"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
|
||||||
|
<connections>
|
||||||
|
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="HOh-sY-3ay"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
|
||||||
|
<connections>
|
||||||
|
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="U76-nv-p5D"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
|
||||||
|
<connections>
|
||||||
|
<action selector="centerSelectionInVisibleArea:" target="Ady-hI-5gd" id="IOG-6D-g5B"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
</items>
|
||||||
|
</menu>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
|
||||||
|
<items>
|
||||||
|
<menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
|
||||||
|
<connections>
|
||||||
|
<action selector="showGuessPanel:" target="Ady-hI-5gd" id="vFj-Ks-hy3"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
|
||||||
|
<connections>
|
||||||
|
<action selector="checkSpelling:" target="Ady-hI-5gd" id="fz7-VC-reM"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
|
||||||
|
<menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="toggleContinuousSpellChecking:" target="Ady-hI-5gd" id="7w6-Qz-0kB"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="toggleGrammarChecking:" target="Ady-hI-5gd" id="muD-Qn-j4w"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="toggleAutomaticSpellingCorrection:" target="Ady-hI-5gd" id="2lM-Qi-WAP"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
</items>
|
||||||
|
</menu>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Substitutions" id="9ic-FL-obx">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
|
||||||
|
<items>
|
||||||
|
<menuItem title="Show Substitutions" id="z6F-FW-3nz">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="orderFrontSubstitutionsPanel:" target="Ady-hI-5gd" id="oku-mr-iSq"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
|
||||||
|
<menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="toggleSmartInsertDelete:" target="Ady-hI-5gd" id="3IJ-Se-DZD"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Smart Quotes" id="hQb-2v-fYv">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="toggleAutomaticQuoteSubstitution:" target="Ady-hI-5gd" id="ptq-xd-QOA"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Smart Dashes" id="rgM-f4-ycn">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="toggleAutomaticDashSubstitution:" target="Ady-hI-5gd" id="oCt-pO-9gS"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Smart Links" id="cwL-P1-jid">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="toggleAutomaticLinkDetection:" target="Ady-hI-5gd" id="Gip-E3-Fov"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Data Detectors" id="tRr-pd-1PS">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="toggleAutomaticDataDetection:" target="Ady-hI-5gd" id="R1I-Nq-Kbl"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Text Replacement" id="HFQ-gK-NFA">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="toggleAutomaticTextReplacement:" target="Ady-hI-5gd" id="DvP-Fe-Py6"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
</items>
|
||||||
|
</menu>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Transformations" id="2oI-Rn-ZJC">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<menu key="submenu" title="Transformations" id="c8a-y6-VQd">
|
||||||
|
<items>
|
||||||
|
<menuItem title="Make Upper Case" id="vmV-6d-7jI">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="uppercaseWord:" target="Ady-hI-5gd" id="sPh-Tk-edu"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Make Lower Case" id="d9M-CD-aMd">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="lowercaseWord:" target="Ady-hI-5gd" id="iUZ-b5-hil"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Capitalize" id="UEZ-Bs-lqG">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="capitalizeWord:" target="Ady-hI-5gd" id="26H-TL-nsh"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
</items>
|
||||||
|
</menu>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Speech" id="xrE-MZ-jX0">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<menu key="submenu" title="Speech" id="3rS-ZA-NoH">
|
||||||
|
<items>
|
||||||
|
<menuItem title="Start Speaking" id="Ynk-f8-cLZ">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="startSpeaking:" target="Ady-hI-5gd" id="654-Ng-kyl"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Stop Speaking" id="Oyz-dy-DGm">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="stopSpeaking:" target="Ady-hI-5gd" id="dX8-6p-jy9"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
</items>
|
||||||
|
</menu>
|
||||||
|
</menuItem>
|
||||||
|
</items>
|
||||||
|
</menu>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Format" id="jxT-CU-nIS">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<menu key="submenu" title="Format" id="GEO-Iw-cKr">
|
||||||
|
<items>
|
||||||
|
<menuItem title="Font" id="Gi5-1S-RQB">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<menu key="submenu" title="Font" systemMenu="font" id="aXa-aM-Jaq">
|
||||||
|
<items>
|
||||||
|
<menuItem title="Show Fonts" keyEquivalent="t" id="Q5e-8K-NDq">
|
||||||
|
<connections>
|
||||||
|
<action selector="orderFrontFontPanel:" target="YLy-65-1bz" id="WHr-nq-2xA"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Bold" tag="2" keyEquivalent="b" id="GB9-OM-e27">
|
||||||
|
<connections>
|
||||||
|
<action selector="addFontTrait:" target="YLy-65-1bz" id="hqk-hr-sYV"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Italic" tag="1" keyEquivalent="i" id="Vjx-xi-njq">
|
||||||
|
<connections>
|
||||||
|
<action selector="addFontTrait:" target="YLy-65-1bz" id="IHV-OB-c03"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Underline" keyEquivalent="u" id="WRG-CD-K1S">
|
||||||
|
<connections>
|
||||||
|
<action selector="underline:" target="Ady-hI-5gd" id="FYS-2b-JAY"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem isSeparatorItem="YES" id="5gT-KC-WSO"/>
|
||||||
|
<menuItem title="Bigger" tag="3" keyEquivalent="+" id="Ptp-SP-VEL">
|
||||||
|
<connections>
|
||||||
|
<action selector="modifyFont:" target="YLy-65-1bz" id="Uc7-di-UnL"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Smaller" tag="4" keyEquivalent="-" id="i1d-Er-qST">
|
||||||
|
<connections>
|
||||||
|
<action selector="modifyFont:" target="YLy-65-1bz" id="HcX-Lf-eNd"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem isSeparatorItem="YES" id="kx3-Dk-x3B"/>
|
||||||
|
<menuItem title="Kern" id="jBQ-r6-VK2">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<menu key="submenu" title="Kern" id="tlD-Oa-oAM">
|
||||||
|
<items>
|
||||||
|
<menuItem title="Use Default" id="GUa-eO-cwY">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="useStandardKerning:" target="Ady-hI-5gd" id="6dk-9l-Ckg"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Use None" id="cDB-IK-hbR">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="turnOffKerning:" target="Ady-hI-5gd" id="U8a-gz-Maa"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Tighten" id="46P-cB-AYj">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="tightenKerning:" target="Ady-hI-5gd" id="hr7-Nz-8ro"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Loosen" id="ogc-rX-tC1">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="loosenKerning:" target="Ady-hI-5gd" id="8i4-f9-FKE"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
</items>
|
||||||
|
</menu>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Ligatures" id="o6e-r0-MWq">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<menu key="submenu" title="Ligatures" id="w0m-vy-SC9">
|
||||||
|
<items>
|
||||||
|
<menuItem title="Use Default" id="agt-UL-0e3">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="useStandardLigatures:" target="Ady-hI-5gd" id="7uR-wd-Dx6"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Use None" id="J7y-lM-qPV">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="turnOffLigatures:" target="Ady-hI-5gd" id="iX2-gA-Ilz"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Use All" id="xQD-1f-W4t">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="useAllLigatures:" target="Ady-hI-5gd" id="KcB-kA-TuK"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
</items>
|
||||||
|
</menu>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Baseline" id="OaQ-X3-Vso">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<menu key="submenu" title="Baseline" id="ijk-EB-dga">
|
||||||
|
<items>
|
||||||
|
<menuItem title="Use Default" id="3Om-Ey-2VK">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="unscript:" target="Ady-hI-5gd" id="0vZ-95-Ywn"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Superscript" id="Rqc-34-cIF">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="superscript:" target="Ady-hI-5gd" id="3qV-fo-wpU"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Subscript" id="I0S-gh-46l">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="subscript:" target="Ady-hI-5gd" id="Q6W-4W-IGz"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Raise" id="2h7-ER-AoG">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="raiseBaseline:" target="Ady-hI-5gd" id="4sk-31-7Q9"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Lower" id="1tx-W0-xDw">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="lowerBaseline:" target="Ady-hI-5gd" id="OF1-bc-KW4"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
</items>
|
||||||
|
</menu>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem isSeparatorItem="YES" id="Ndw-q3-faq"/>
|
||||||
|
<menuItem title="Show Colors" keyEquivalent="C" id="bgn-CT-cEk">
|
||||||
|
<connections>
|
||||||
|
<action selector="orderFrontColorPanel:" target="Ady-hI-5gd" id="mSX-Xz-DV3"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem isSeparatorItem="YES" id="iMs-zA-UFJ"/>
|
||||||
|
<menuItem title="Copy Style" keyEquivalent="c" id="5Vv-lz-BsD">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="copyFont:" target="Ady-hI-5gd" id="GJO-xA-L4q"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Paste Style" keyEquivalent="v" id="vKC-jM-MkH">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="pasteFont:" target="Ady-hI-5gd" id="JfD-CL-leO"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
</items>
|
||||||
|
</menu>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Text" id="Fal-I4-PZk">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<menu key="submenu" title="Text" id="d9c-me-L2H">
|
||||||
|
<items>
|
||||||
|
<menuItem title="Align Left" keyEquivalent="{" id="ZM1-6Q-yy1">
|
||||||
|
<connections>
|
||||||
|
<action selector="alignLeft:" target="Ady-hI-5gd" id="zUv-R1-uAa"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Center" keyEquivalent="|" id="VIY-Ag-zcb">
|
||||||
|
<connections>
|
||||||
|
<action selector="alignCenter:" target="Ady-hI-5gd" id="spX-mk-kcS"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Justify" id="J5U-5w-g23">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="alignJustified:" target="Ady-hI-5gd" id="ljL-7U-jND"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Align Right" keyEquivalent="}" id="wb2-vD-lq4">
|
||||||
|
<connections>
|
||||||
|
<action selector="alignRight:" target="Ady-hI-5gd" id="r48-bG-YeY"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem isSeparatorItem="YES" id="4s2-GY-VfK"/>
|
||||||
|
<menuItem title="Writing Direction" id="H1b-Si-o9J">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<menu key="submenu" title="Writing Direction" id="8mr-sm-Yjd">
|
||||||
|
<items>
|
||||||
|
<menuItem title="Paragraph" enabled="NO" id="ZvO-Gk-QUH">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem id="YGs-j5-SAR">
|
||||||
|
<string key="title"> Default</string>
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="makeBaseWritingDirectionNatural:" target="Ady-hI-5gd" id="qtV-5e-UBP"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem id="Lbh-J2-qVU">
|
||||||
|
<string key="title"> Left to Right</string>
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="makeBaseWritingDirectionLeftToRight:" target="Ady-hI-5gd" id="S0X-9S-QSf"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem id="jFq-tB-4Kx">
|
||||||
|
<string key="title"> Right to Left</string>
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="makeBaseWritingDirectionRightToLeft:" target="Ady-hI-5gd" id="5fk-qB-AqJ"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem isSeparatorItem="YES" id="swp-gr-a21"/>
|
||||||
|
<menuItem title="Selection" enabled="NO" id="cqv-fj-IhA">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem id="Nop-cj-93Q">
|
||||||
|
<string key="title"> Default</string>
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="makeTextWritingDirectionNatural:" target="Ady-hI-5gd" id="lPI-Se-ZHp"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem id="BgM-ve-c93">
|
||||||
|
<string key="title"> Left to Right</string>
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="makeTextWritingDirectionLeftToRight:" target="Ady-hI-5gd" id="caW-Bv-w94"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem id="RB4-Sm-HuC">
|
||||||
|
<string key="title"> Right to Left</string>
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="makeTextWritingDirectionRightToLeft:" target="Ady-hI-5gd" id="EXD-6r-ZUu"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
</items>
|
||||||
|
</menu>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem isSeparatorItem="YES" id="fKy-g9-1gm"/>
|
||||||
|
<menuItem title="Show Ruler" id="vLm-3I-IUL">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="toggleRuler:" target="Ady-hI-5gd" id="FOx-HJ-KwY"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Copy Ruler" keyEquivalent="c" id="MkV-Pr-PK5">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="copyRuler:" target="Ady-hI-5gd" id="71i-fW-3W2"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Paste Ruler" keyEquivalent="v" id="LVM-kO-fVI">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="pasteRuler:" target="Ady-hI-5gd" id="cSh-wd-qM2"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
</items>
|
||||||
|
</menu>
|
||||||
|
</menuItem>
|
||||||
|
</items>
|
||||||
|
</menu>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="View" id="H8h-7b-M4v">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<menu key="submenu" title="View" id="HyV-fh-RgO">
|
||||||
|
<items>
|
||||||
|
<menuItem title="Show Toolbar" keyEquivalent="t" id="snW-S8-Cw5">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="toggleToolbarShown:" target="Ady-hI-5gd" id="BXY-wc-z0C"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Customize Toolbar…" id="1UK-8n-QPP">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="runToolbarCustomizationPalette:" target="Ady-hI-5gd" id="pQI-g3-MTW"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem isSeparatorItem="YES" id="hB3-LF-h0Y"/>
|
||||||
|
<menuItem title="Show Sidebar" keyEquivalent="s" id="kIP-vf-haE">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="toggleSidebar:" target="Ady-hI-5gd" id="iwa-gc-5KM"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="toggleFullScreen:" target="Ady-hI-5gd" id="dU3-MA-1Rq"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
</items>
|
||||||
|
</menu>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Window" id="aUF-d1-5bR">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
|
||||||
|
<items>
|
||||||
|
<menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
|
||||||
|
<connections>
|
||||||
|
<action selector="performMiniaturize:" target="Ady-hI-5gd" id="VwT-WD-YPe"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Zoom" id="R4o-n2-Eq4">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="performZoom:" target="Ady-hI-5gd" id="DIl-cC-cCs"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
|
||||||
|
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="arrangeInFront:" target="Ady-hI-5gd" id="DRN-fu-gQh"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
</items>
|
||||||
|
</menu>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Help" id="wpr-3q-Mcd">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
|
||||||
|
<items>
|
||||||
|
<menuItem title="SecretiveUpdater Help" keyEquivalent="?" id="FKE-Sm-Kum">
|
||||||
|
<connections>
|
||||||
|
<action selector="showHelp:" target="Ady-hI-5gd" id="y7X-2Q-9no"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
</items>
|
||||||
|
</menu>
|
||||||
|
</menuItem>
|
||||||
|
</items>
|
||||||
|
</menu>
|
||||||
|
<connections>
|
||||||
|
<outlet property="delegate" destination="Voe-Tx-rLC" id="PrD-fu-P6m"/>
|
||||||
|
</connections>
|
||||||
|
</application>
|
||||||
|
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="SecretiveUpdater" customModuleProvider="target"/>
|
||||||
|
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
|
||||||
|
<customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="75" y="0.0"/>
|
||||||
|
</scene>
|
||||||
|
</scenes>
|
||||||
|
</document>
|
10
Sources/SecretiveUpdater/SecretiveUpdater.entitlements
Normal file
10
Sources/SecretiveUpdater/SecretiveUpdater.entitlements
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>$(TeamIdentifierPrefix)com.maxgoedjen.Secretive</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
110
Sources/SecretiveUpdater/Updater.swift
Normal file
110
Sources/SecretiveUpdater/Updater.swift
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
import Foundation
|
||||||
|
import Brief
|
||||||
|
import AppleArchive
|
||||||
|
import System
|
||||||
|
import Cocoa
|
||||||
|
import Security.Authorization
|
||||||
|
import Security.AuthorizationTags
|
||||||
|
|
||||||
|
class Updater: UpdaterProtocol {
|
||||||
|
|
||||||
|
func installUpdate(url: URL, to destinationURL: URL) async throws -> String {
|
||||||
|
// let (downloadedURL, _) = try await URLSession.shared.download(from: url)
|
||||||
|
// let unzipped = try await decompress(url: downloadedURL)
|
||||||
|
// try await move(url: unzipped, to: destinationURL)
|
||||||
|
// let config = NSWorkspace.OpenConfiguration()
|
||||||
|
// config.activates = true
|
||||||
|
// TODO: clean
|
||||||
|
_ = try await authorize()
|
||||||
|
// if let host = NSRunningApplication.runningApplications(withBundleIdentifier: "com.maxgoedjen.Secretive.Host").first(where: { $0.bundleURL?.path.hasPrefix("/Applications") ?? false }) {
|
||||||
|
// host.terminate()
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
func decompress(url: URL) async throws -> URL {
|
||||||
|
let zipURL = url.deletingPathExtension().appendingPathExtension("zip")
|
||||||
|
try FileManager.default.copyItem(at: url, to: zipURL)
|
||||||
|
let id = UUID()
|
||||||
|
let destinationURL = FileManager.default.temporaryDirectory.appendingPathComponent("\(id.uuidString)/")
|
||||||
|
_ = try FileManager.default.createDirectory(at: destinationURL, withIntermediateDirectories: true, attributes: [:])
|
||||||
|
let process = Process()
|
||||||
|
let pipe = Pipe()
|
||||||
|
process.launchPath = "/usr/bin/unzip"
|
||||||
|
process.arguments = ["-o", zipURL.path, "-d", destinationURL.path]
|
||||||
|
process.standardOutput = pipe
|
||||||
|
try process.run()
|
||||||
|
_ = try pipe.fileHandleForReading.readToEnd()
|
||||||
|
guard let appURL = try FileManager.default.contentsOfDirectory(at: destinationURL, includingPropertiesForKeys: nil).first(where: { $0.pathExtension == "app" }) else {
|
||||||
|
throw DecompressionError(reason: "Unzip failed")
|
||||||
|
}
|
||||||
|
return appURL
|
||||||
|
}
|
||||||
|
|
||||||
|
func move(url: URL, to destinationURL: URL) async throws {
|
||||||
|
let auth = try await authorize()
|
||||||
|
try await move(url: url, to: destinationURL)
|
||||||
|
try await revokeAuthorization(auth)
|
||||||
|
}
|
||||||
|
|
||||||
|
func authorize() async throws -> AuthorizationRef {
|
||||||
|
let flags = AuthorizationFlags()
|
||||||
|
var authorization: AuthorizationRef? = nil
|
||||||
|
AuthorizationCreate(nil, nil, flags, &authorization)
|
||||||
|
let authFlags: AuthorizationFlags = [.interactionAllowed, .extendRights, .preAuthorize]
|
||||||
|
var result: OSStatus?
|
||||||
|
kAuthorizationRightExecute.withCString { cString in
|
||||||
|
var item = AuthorizationItem(name: cString, valueLength: 0, value: nil, flags: 0)
|
||||||
|
withUnsafeMutablePointer(to: &item) { pointer in
|
||||||
|
var rights = AuthorizationRights(count: 1, items: pointer)
|
||||||
|
result = AuthorizationCopyRights(authorization!, &rights, nil, authFlags, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
guard result == errAuthorizationSuccess, let authorization = authorization else {
|
||||||
|
throw RightsNotAcquiredError()
|
||||||
|
}
|
||||||
|
return authorization
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func revokeAuthorization(_ authorization: AuthorizationRef) async throws {
|
||||||
|
AuthorizationFree(authorization, .destroyRights)
|
||||||
|
}
|
||||||
|
|
||||||
|
func priveledgedMove(url: URL, to destination: URL) async throws {
|
||||||
|
try FileManager.default.replaceItemAt(destination, withItemAt: url)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Updater {
|
||||||
|
|
||||||
|
struct DecompressionError: Error, LocalizedError {
|
||||||
|
let reason: String
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RightsNotAcquiredError: Error, LocalizedError {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension URLSession {
|
||||||
|
|
||||||
|
@available(macOS, deprecated: 12.0)
|
||||||
|
public func download(from url: URL) async throws -> (URL, URLResponse) {
|
||||||
|
try await withCheckedThrowingContinuation { continuation in
|
||||||
|
let task = downloadTask(with: url) { url, response, error in
|
||||||
|
guard let url = url, let response = response else {
|
||||||
|
continuation.resume(throwing: error ?? UnknownError())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
continuation.resume(returning: (url, response))
|
||||||
|
}
|
||||||
|
task.resume()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct UnknownError: Error {}
|
||||||
|
|
||||||
|
}
|
8
Sources/SecretiveUpdater/UpdaterProtocol.swift
Normal file
8
Sources/SecretiveUpdater/UpdaterProtocol.swift
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import Foundation
|
||||||
|
import Brief
|
||||||
|
|
||||||
|
@objc public protocol UpdaterProtocol {
|
||||||
|
|
||||||
|
func installUpdate(url: URL, to: URL) async throws -> String
|
||||||
|
|
||||||
|
}
|
26
Sources/SecretiveUpdater/main.swift
Normal file
26
Sources/SecretiveUpdater/main.swift
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
class ServiceDelegate: NSObject, NSXPCListenerDelegate {
|
||||||
|
|
||||||
|
let exported: UpdaterProtocol
|
||||||
|
|
||||||
|
init(exportedObject: UpdaterProtocol) {
|
||||||
|
self.exported = exportedObject
|
||||||
|
}
|
||||||
|
|
||||||
|
func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool {
|
||||||
|
newConnection.exportedInterface = NSXPCInterface(with: UpdaterProtocol.self)
|
||||||
|
newConnection.exportedObject = exported
|
||||||
|
newConnection.resume()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
let updater = Updater()
|
||||||
|
let delegate = ServiceDelegate(exportedObject: Updater())
|
||||||
|
let listener = NSXPCListener(machServiceName: Bundle.main.bundleIdentifier!)
|
||||||
|
listener.delegate = delegate
|
||||||
|
listener.resume()
|
||||||
|
try "Hello world".data(using: .utf8)?.write(to: URL(fileURLWithPath: "/Users/max/Downloads/\(UUID().uuidString).txt"))
|
||||||
|
RunLoop.current.run()
|
Reference in New Issue
Block a user