Improve updater (#203)
This commit is contained in:
parent
4105c1d6f6
commit
698a69a034
|
@ -11,7 +11,10 @@ public class Updater: ObservableObject, UpdaterProtocol {
|
||||||
|
|
||||||
@Published public var update: Release?
|
@Published public var update: Release?
|
||||||
|
|
||||||
public init(checkOnLaunch: Bool) {
|
private let osVersion: SemVer
|
||||||
|
|
||||||
|
public init(checkOnLaunch: Bool, osVersion: SemVer = SemVer(ProcessInfo.processInfo.operatingSystemVersion)) {
|
||||||
|
self.osVersion = osVersion
|
||||||
if checkOnLaunch {
|
if checkOnLaunch {
|
||||||
// Don't do a launch check if the user hasn't seen the setup prompt explaining updater yet.
|
// Don't do a launch check if the user hasn't seen the setup prompt explaining updater yet.
|
||||||
checkForUpdates()
|
checkForUpdates()
|
||||||
|
@ -25,8 +28,8 @@ public class Updater: ObservableObject, UpdaterProtocol {
|
||||||
public func checkForUpdates() {
|
public func checkForUpdates() {
|
||||||
URLSession.shared.dataTask(with: Constants.updateURL) { data, _, _ in
|
URLSession.shared.dataTask(with: Constants.updateURL) { data, _, _ in
|
||||||
guard let data = data else { return }
|
guard let data = data else { return }
|
||||||
guard let release = try? JSONDecoder().decode(Release.self, from: data) else { return }
|
guard let releases = try? JSONDecoder().decode([Release].self, from: data) else { return }
|
||||||
self.evaluate(release: release)
|
self.evaluate(releases: releases)
|
||||||
}.resume()
|
}.resume()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,11 +45,16 @@ public class Updater: ObservableObject, UpdaterProtocol {
|
||||||
|
|
||||||
extension Updater {
|
extension Updater {
|
||||||
|
|
||||||
func evaluate(release: Release) {
|
func evaluate(releases: [Release]) {
|
||||||
|
guard let release = releases
|
||||||
|
.sorted()
|
||||||
|
.reversed()
|
||||||
|
.filter({ !$0.prerelease })
|
||||||
|
.first(where: { $0.minimumOSVersion <= osVersion }) else { return }
|
||||||
guard !userIgnored(release: release) else { return }
|
guard !userIgnored(release: release) else { return }
|
||||||
guard !release.prerelease else { return }
|
guard !release.prerelease else { return }
|
||||||
let latestVersion = SemVer(release.name)
|
let latestVersion = SemVer(release.name)
|
||||||
let currentVersion = SemVer(Bundle.main.infoDictionary!["CFBundleShortVersionString"] as! String)
|
let currentVersion = SemVer(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "0.0.0")
|
||||||
if latestVersion > currentVersion {
|
if latestVersion > currentVersion {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.update = release
|
self.update = release
|
||||||
|
@ -64,11 +72,11 @@ extension Updater {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SemVer {
|
public struct SemVer {
|
||||||
|
|
||||||
let versionNumbers: [Int]
|
let versionNumbers: [Int]
|
||||||
|
|
||||||
init(_ version: String) {
|
public init(_ version: String) {
|
||||||
// Betas have the format 1.2.3_beta1
|
// Betas have the format 1.2.3_beta1
|
||||||
let strippedBeta = version.split(separator: "_").first!
|
let strippedBeta = version.split(separator: "_").first!
|
||||||
var split = strippedBeta.split(separator: ".").compactMap { Int($0) }
|
var split = strippedBeta.split(separator: ".").compactMap { Int($0) }
|
||||||
|
@ -78,11 +86,15 @@ struct SemVer {
|
||||||
versionNumbers = split
|
versionNumbers = split
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public init(_ version: OperatingSystemVersion) {
|
||||||
|
versionNumbers = [version.majorVersion, version.minorVersion, version.patchVersion]
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SemVer: Comparable {
|
extension SemVer: Comparable {
|
||||||
|
|
||||||
static func < (lhs: SemVer, rhs: SemVer) -> Bool {
|
public static func < (lhs: SemVer, rhs: SemVer) -> Bool {
|
||||||
for (latest, current) in zip(lhs.versionNumbers, rhs.versionNumbers) {
|
for (latest, current) in zip(lhs.versionNumbers, rhs.versionNumbers) {
|
||||||
if latest < current {
|
if latest < current {
|
||||||
return true
|
return true
|
||||||
|
@ -99,7 +111,7 @@ extension SemVer: Comparable {
|
||||||
extension Updater {
|
extension Updater {
|
||||||
|
|
||||||
enum Constants {
|
enum Constants {
|
||||||
static let updateURL = URL(string: "https://api.github.com/repos/maxgoedjen/secretive/releases/latest")!
|
static let updateURL = URL(string: "https://api.github.com/repos/maxgoedjen/secretive/releases")!
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -128,12 +140,32 @@ extension Release: Identifiable {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Release: Comparable {
|
||||||
|
|
||||||
|
public static func < (lhs: Release, rhs: Release) -> Bool {
|
||||||
|
lhs.version < rhs.version
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
extension Release {
|
extension Release {
|
||||||
|
|
||||||
public var critical: Bool {
|
public var critical: Bool {
|
||||||
body.contains(Constants.securityContent)
|
body.contains(Constants.securityContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var version: SemVer {
|
||||||
|
SemVer(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
public var minimumOSVersion: SemVer {
|
||||||
|
guard let range = body.range(of: "Minimum macOS Version"),
|
||||||
|
let numberStart = body.rangeOfCharacter(from: CharacterSet.decimalDigits, options: [], range: range.upperBound..<body.endIndex) else { return SemVer("11.0.0") }
|
||||||
|
let numbersEnd = body.rangeOfCharacter(from: CharacterSet.whitespacesAndNewlines, options: [], range: numberStart.upperBound..<body.endIndex)?.lowerBound ?? body.endIndex
|
||||||
|
let version = numberStart.lowerBound..<numbersEnd
|
||||||
|
return SemVer(String(body[version]))
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Release {
|
extension Release {
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
import XCTest
|
||||||
|
@testable import Brief
|
||||||
|
|
||||||
|
class ReleaseParsingTests: XCTestCase {
|
||||||
|
|
||||||
|
func testNonCritical() {
|
||||||
|
let release = Release(name: "1.0.0", prerelease: false, html_url: URL(string: "https://example.com")!, body: "Initial release")
|
||||||
|
XCTAssert(release.critical == false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCritical() {
|
||||||
|
let release = Release(name: "1.0.0", prerelease: false, html_url: URL(string: "https://example.com")!, body: "Critical Security Update")
|
||||||
|
XCTAssert(release.critical == true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testOSMissing() {
|
||||||
|
let release = Release(name: "1.0.0", prerelease: false, html_url: URL(string: "https://example.com")!, body: "Critical Security Update")
|
||||||
|
XCTAssert(release.minimumOSVersion == SemVer("11.0.0"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func testOSPresentWithContentBelow() {
|
||||||
|
let release = Release(name: "1.0.0", prerelease: false, html_url: URL(string: "https://example.com")!, body: "Critical Security Update ##Minimum macOS Version\n1.2.3\nBuild info")
|
||||||
|
XCTAssert(release.minimumOSVersion == SemVer("1.2.3"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func testOSPresentAtEnd() {
|
||||||
|
let release = Release(name: "1.0.0", prerelease: false, html_url: URL(string: "https://example.com")!, body: "Critical Security Update Minimum macOS Version: 1.2.3")
|
||||||
|
XCTAssert(release.minimumOSVersion == SemVer("1.2.3"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func testOSWithMacOSPrefix() {
|
||||||
|
let release = Release(name: "1.0.0", prerelease: false, html_url: URL(string: "https://example.com")!, body: "Critical Security Update Minimum macOS Version: macOS 1.2.3")
|
||||||
|
XCTAssert(release.minimumOSVersion == SemVer("1.2.3"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func testOSGreaterThanMinimum() {
|
||||||
|
let release = Release(name: "1.0.0", prerelease: false, html_url: URL(string: "https://example.com")!, body: "Critical Security Update Minimum macOS Version: 1.2.3")
|
||||||
|
XCTAssert(release.minimumOSVersion < SemVer("11.0.0"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func testOSEqualToMinimum() {
|
||||||
|
let release = Release(name: "1.0.0", prerelease: false, html_url: URL(string: "https://example.com")!, body: "Critical Security Update Minimum macOS Version: 11.2.3")
|
||||||
|
XCTAssert(release.minimumOSVersion <= SemVer("11.2.3"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func testOSLessThanMinimum() {
|
||||||
|
let release = Release(name: "1.0.0", prerelease: false, html_url: URL(string: "https://example.com")!, body: "Critical Security Update Minimum macOS Version: 1.2.3")
|
||||||
|
XCTAssert(release.minimumOSVersion > SemVer("1.0.0"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func testGreatestSelectedIfOldPatchIsPublishedLater() {
|
||||||
|
// 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.
|
||||||
|
let updater = Updater(checkOnLaunch: false, osVersion: SemVer("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 = [
|
||||||
|
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.1", prerelease: false, html_url: URL(string: "https://example.com")!, body: "Bug fixes Minimum macOS Version: 1.2.3"),
|
||||||
|
two,
|
||||||
|
Release(name: "1.0.2", prerelease: false, html_url: URL(string: "https://example.com")!, body: "Emergency patch! Minimum macOS Version: 1.2.3"),
|
||||||
|
]
|
||||||
|
|
||||||
|
let expectation = XCTestExpectation()
|
||||||
|
updater.evaluate(releases: releases)
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
XCTAssert(updater.update == two)
|
||||||
|
expectation.fulfill()
|
||||||
|
}
|
||||||
|
wait(for: [expectation], timeout: 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testLatestVersionIsRunnable() {
|
||||||
|
// 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.
|
||||||
|
let updater = Updater(checkOnLaunch: false, osVersion: SemVer("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 = [
|
||||||
|
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.1", prerelease: false, html_url: URL(string: "https://example.com")!, body: "Bug fixes Minimum macOS Version: 1.2.3"),
|
||||||
|
Release(name: "2.0.0", prerelease: false, html_url: URL(string: "https://example.com")!, body: "2.0 available! Minimum macOS Version: 2.2.3"),
|
||||||
|
Release(name: "1.0.2", prerelease: false, html_url: URL(string: "https://example.com")!, body: "Emergency patch! Minimum macOS Version: 1.2.3"),
|
||||||
|
]
|
||||||
|
let expectation = XCTestExpectation()
|
||||||
|
updater.evaluate(releases: releases)
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
XCTAssert(updater.update == oneOhTwo)
|
||||||
|
expectation.fulfill()
|
||||||
|
}
|
||||||
|
wait(for: [expectation], timeout: 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSorting() {
|
||||||
|
let two = Release(name: "2.0.0", prerelease: false, html_url: URL(string: "https://example.com")!, body: "2.0 available!")
|
||||||
|
let releases = [
|
||||||
|
Release(name: "1.0.0", prerelease: false, html_url: URL(string: "https://example.com")!, body: "Initial release"),
|
||||||
|
Release(name: "1.0.1", prerelease: false, html_url: URL(string: "https://example.com")!, body: "Bug fixes"),
|
||||||
|
two,
|
||||||
|
Release(name: "1.0.2", prerelease: false, html_url: URL(string: "https://example.com")!, body: "Emergency patch!"),
|
||||||
|
]
|
||||||
|
let sorted = releases.sorted().reversed().first
|
||||||
|
XCTAssert(sorted == two)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -27,6 +27,21 @@ class SemVerTests: XCTestCase {
|
||||||
XCTAssert(current < new)
|
XCTAssert(current < new)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testRegularParsing() {
|
||||||
|
let current = SemVer("1.0.2")
|
||||||
|
XCTAssert(current.versionNumbers == [1, 0, 2])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testNoPatch() {
|
||||||
|
let current = SemVer("1.1")
|
||||||
|
XCTAssert(current.versionNumbers == [1, 1, 0])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testGarbage() {
|
||||||
|
let current = SemVer("Test")
|
||||||
|
XCTAssert(current.versionNumbers == [0, 0, 0])
|
||||||
|
}
|
||||||
|
|
||||||
func testBeta() {
|
func testBeta() {
|
||||||
let current = SemVer("1.0.2")
|
let current = SemVer("1.0.2")
|
||||||
let new = SemVer("1.1.0_beta1")
|
let new = SemVer("1.1.0_beta1")
|
|
@ -64,7 +64,7 @@
|
||||||
508BF28E25B4F005009EFB7E /* InternetAccessPolicy.plist in Resources */ = {isa = PBXBuildFile; fileRef = 508BF28D25B4F005009EFB7E /* InternetAccessPolicy.plist */; };
|
508BF28E25B4F005009EFB7E /* InternetAccessPolicy.plist in Resources */ = {isa = PBXBuildFile; fileRef = 508BF28D25B4F005009EFB7E /* InternetAccessPolicy.plist */; };
|
||||||
508BF2AA25B4F1CB009EFB7E /* InternetAccessPolicy.plist in Resources */ = {isa = PBXBuildFile; fileRef = 508BF29425B4F140009EFB7E /* InternetAccessPolicy.plist */; };
|
508BF2AA25B4F1CB009EFB7E /* InternetAccessPolicy.plist in Resources */ = {isa = PBXBuildFile; fileRef = 508BF29425B4F140009EFB7E /* InternetAccessPolicy.plist */; };
|
||||||
5091D2BC25183B830049FD9B /* ApplicationDirectoryController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5091D2BB25183B830049FD9B /* ApplicationDirectoryController.swift */; };
|
5091D2BC25183B830049FD9B /* ApplicationDirectoryController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5091D2BB25183B830049FD9B /* ApplicationDirectoryController.swift */; };
|
||||||
5091D3222519D56D0049FD9B /* BriefTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5091D3212519D56D0049FD9B /* BriefTests.swift */; };
|
5091D3222519D56D0049FD9B /* SemVerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5091D3212519D56D0049FD9B /* SemVerTests.swift */; };
|
||||||
5091D3242519D56D0049FD9B /* Brief.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 506772FB2426F3F400034DED /* Brief.framework */; };
|
5091D3242519D56D0049FD9B /* Brief.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 506772FB2426F3F400034DED /* Brief.framework */; };
|
||||||
5099A02423FD2AAA0062B6F2 /* CreateSecretView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5099A02323FD2AAA0062B6F2 /* CreateSecretView.swift */; };
|
5099A02423FD2AAA0062B6F2 /* CreateSecretView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5099A02323FD2AAA0062B6F2 /* CreateSecretView.swift */; };
|
||||||
5099A02723FE34FA0062B6F2 /* SmartCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5099A02623FE34FA0062B6F2 /* SmartCard.swift */; };
|
5099A02723FE34FA0062B6F2 /* SmartCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5099A02623FE34FA0062B6F2 /* SmartCard.swift */; };
|
||||||
|
@ -75,6 +75,7 @@
|
||||||
5099A07C240242BA0062B6F2 /* AgentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5099A07B240242BA0062B6F2 /* AgentTests.swift */; };
|
5099A07C240242BA0062B6F2 /* AgentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5099A07B240242BA0062B6F2 /* AgentTests.swift */; };
|
||||||
5099A07E240242BA0062B6F2 /* SecretAgentKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 5099A06E240242BA0062B6F2 /* SecretAgentKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
5099A07E240242BA0062B6F2 /* SecretAgentKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 5099A06E240242BA0062B6F2 /* SecretAgentKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
5099A08A240242C20062B6F2 /* SSHAgentProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5099A089240242C20062B6F2 /* SSHAgentProtocol.swift */; };
|
5099A08A240242C20062B6F2 /* SSHAgentProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5099A089240242C20062B6F2 /* SSHAgentProtocol.swift */; };
|
||||||
|
509FA3B625B53C49005E2535 /* ReleaseParsingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 509FA3B525B53C49005E2535 /* ReleaseParsingTests.swift */; };
|
||||||
50A3B79124026B7600D209EA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 50A3B79024026B7600D209EA /* Assets.xcassets */; };
|
50A3B79124026B7600D209EA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 50A3B79024026B7600D209EA /* Assets.xcassets */; };
|
||||||
50A3B79424026B7600D209EA /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 50A3B79324026B7600D209EA /* Preview Assets.xcassets */; };
|
50A3B79424026B7600D209EA /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 50A3B79324026B7600D209EA /* Preview Assets.xcassets */; };
|
||||||
50A3B79724026B7600D209EA /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 50A3B79524026B7600D209EA /* Main.storyboard */; };
|
50A3B79724026B7600D209EA /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 50A3B79524026B7600D209EA /* Main.storyboard */; };
|
||||||
|
@ -280,7 +281,7 @@
|
||||||
508BF29425B4F140009EFB7E /* InternetAccessPolicy.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; name = InternetAccessPolicy.plist; path = SecretAgent/InternetAccessPolicy.plist; sourceTree = SOURCE_ROOT; };
|
508BF29425B4F140009EFB7E /* InternetAccessPolicy.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; name = InternetAccessPolicy.plist; path = SecretAgent/InternetAccessPolicy.plist; sourceTree = SOURCE_ROOT; };
|
||||||
5091D2BB25183B830049FD9B /* ApplicationDirectoryController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationDirectoryController.swift; sourceTree = "<group>"; };
|
5091D2BB25183B830049FD9B /* ApplicationDirectoryController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationDirectoryController.swift; sourceTree = "<group>"; };
|
||||||
5091D31F2519D56D0049FD9B /* BriefTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BriefTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
5091D31F2519D56D0049FD9B /* BriefTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BriefTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
5091D3212519D56D0049FD9B /* BriefTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BriefTests.swift; sourceTree = "<group>"; };
|
5091D3212519D56D0049FD9B /* SemVerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SemVerTests.swift; sourceTree = "<group>"; };
|
||||||
5091D3232519D56D0049FD9B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
5091D3232519D56D0049FD9B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
5099A02323FD2AAA0062B6F2 /* CreateSecretView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateSecretView.swift; sourceTree = "<group>"; };
|
5099A02323FD2AAA0062B6F2 /* CreateSecretView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateSecretView.swift; sourceTree = "<group>"; };
|
||||||
5099A02623FE34FA0062B6F2 /* SmartCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmartCard.swift; sourceTree = "<group>"; };
|
5099A02623FE34FA0062B6F2 /* SmartCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmartCard.swift; sourceTree = "<group>"; };
|
||||||
|
@ -294,6 +295,7 @@
|
||||||
5099A07B240242BA0062B6F2 /* AgentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgentTests.swift; sourceTree = "<group>"; };
|
5099A07B240242BA0062B6F2 /* AgentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgentTests.swift; sourceTree = "<group>"; };
|
||||||
5099A07D240242BA0062B6F2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
5099A07D240242BA0062B6F2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
5099A089240242C20062B6F2 /* SSHAgentProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSHAgentProtocol.swift; sourceTree = "<group>"; };
|
5099A089240242C20062B6F2 /* SSHAgentProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSHAgentProtocol.swift; sourceTree = "<group>"; };
|
||||||
|
509FA3B525B53C49005E2535 /* ReleaseParsingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReleaseParsingTests.swift; sourceTree = "<group>"; };
|
||||||
50A3B78A24026B7500D209EA /* SecretAgent.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SecretAgent.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
50A3B78A24026B7500D209EA /* SecretAgent.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SecretAgent.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
50A3B79024026B7600D209EA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
50A3B79024026B7600D209EA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
50A3B79324026B7600D209EA /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
50A3B79324026B7600D209EA /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||||
|
@ -565,7 +567,8 @@
|
||||||
5091D3202519D56D0049FD9B /* BriefTests */ = {
|
5091D3202519D56D0049FD9B /* BriefTests */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
5091D3212519D56D0049FD9B /* BriefTests.swift */,
|
5091D3212519D56D0049FD9B /* SemVerTests.swift */,
|
||||||
|
509FA3B525B53C49005E2535 /* ReleaseParsingTests.swift */,
|
||||||
5091D3232519D56D0049FD9B /* Info.plist */,
|
5091D3232519D56D0049FD9B /* Info.plist */,
|
||||||
);
|
);
|
||||||
path = BriefTests;
|
path = BriefTests;
|
||||||
|
@ -1073,7 +1076,8 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
5091D3222519D56D0049FD9B /* BriefTests.swift in Sources */,
|
509FA3B625B53C49005E2535 /* ReleaseParsingTests.swift in Sources */,
|
||||||
|
5091D3222519D56D0049FD9B /* SemVerTests.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue