Quick prompting for stuff on debug/test builds

This commit is contained in:
Max Goedjen 2021-12-31 14:52:40 -08:00
parent 6eee29e1fa
commit b8411008ae
No known key found for this signature in database
GPG Key ID: E58C21DD77B9B8E8
6 changed files with 64 additions and 47 deletions

View File

@ -4,12 +4,14 @@ import Combine
public protocol UpdaterProtocol: ObservableObject { public protocol UpdaterProtocol: ObservableObject {
var update: Release? { get } var update: Release? { get }
var testBuild: Bool { get }
} }
public class Updater: ObservableObject, UpdaterProtocol { public class Updater: ObservableObject, UpdaterProtocol {
@Published public var update: Release? @Published public var update: Release?
public let testBuild: Bool
private let osVersion: SemVer private let osVersion: SemVer
private let currentVersion: SemVer private let currentVersion: SemVer
@ -17,6 +19,7 @@ public class Updater: ObservableObject, UpdaterProtocol {
public init(checkOnLaunch: Bool, osVersion: SemVer = SemVer(ProcessInfo.processInfo.operatingSystemVersion), currentVersion: SemVer = SemVer(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "0.0.0")) { public init(checkOnLaunch: Bool, osVersion: SemVer = SemVer(ProcessInfo.processInfo.operatingSystemVersion), currentVersion: SemVer = SemVer(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "0.0.0")) {
self.osVersion = osVersion self.osVersion = osVersion
self.currentVersion = currentVersion self.currentVersion = currentVersion
testBuild = currentVersion == SemVer("0.0.0")
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()

View File

@ -36,7 +36,7 @@ struct Secretive: App {
if agentStatusChecker.running && justUpdatedChecker.justUpdated { if agentStatusChecker.running && justUpdatedChecker.justUpdated {
// Relaunch the agent, since it'll be running from earlier update still // Relaunch the agent, since it'll be running from earlier update still
reinstallAgent() reinstallAgent()
} else if !agentStatusChecker.running { } else if !agentStatusChecker.running && !agentStatusChecker.developmentBuild {
forceLaunchAgent() forceLaunchAgent()
} }
} }

View File

@ -5,6 +5,7 @@ import SecretKit
protocol AgentStatusCheckerProtocol: ObservableObject { protocol AgentStatusCheckerProtocol: ObservableObject {
var running: Bool { get } var running: Bool { get }
var developmentBuild: Bool { get }
} }
class AgentStatusChecker: ObservableObject, AgentStatusCheckerProtocol { class AgentStatusChecker: ObservableObject, AgentStatusCheckerProtocol {
@ -36,6 +37,12 @@ class AgentStatusChecker: ObservableObject, AgentStatusCheckerProtocol {
return nil return nil
} }
// Whether Secretive is being run in an Xcode environment.
var developmentBuild: Bool {
Bundle.main.bundleURL.absoluteString.contains("/Library/Developer/Xcode")
}
} }

View File

@ -4,6 +4,7 @@ import Combine
class PreviewAgentStatusChecker: AgentStatusCheckerProtocol { class PreviewAgentStatusChecker: AgentStatusCheckerProtocol {
let running: Bool let running: Bool
let developmentBuild = false
init(running: Bool = true) { init(running: Bool = true) {
self.running = running self.running = running

View File

@ -5,6 +5,7 @@ import Brief
class PreviewUpdater: UpdaterProtocol { class PreviewUpdater: UpdaterProtocol {
let update: Release? let update: Release?
let testBuild = false
init(update: Update = .none) { init(update: Update = .none) {
switch update { switch update {

View File

@ -3,18 +3,18 @@ import SecretKit
import Brief import Brief
struct ContentView<UpdaterType: UpdaterProtocol, AgentStatusCheckerType: AgentStatusCheckerProtocol>: View { struct ContentView<UpdaterType: UpdaterProtocol, AgentStatusCheckerType: AgentStatusCheckerProtocol>: View {
@Binding var showingCreation: Bool @Binding var showingCreation: Bool
@Binding var runningSetup: Bool @Binding var runningSetup: Bool
@Binding var hasRunSetup: Bool @Binding var hasRunSetup: Bool
@EnvironmentObject private var storeList: SecretStoreList @EnvironmentObject private var storeList: SecretStoreList
@EnvironmentObject private var updater: UpdaterType @EnvironmentObject private var updater: UpdaterType
@EnvironmentObject private var agentStatusChecker: AgentStatusCheckerType @EnvironmentObject private var agentStatusChecker: AgentStatusCheckerType
@State private var selectedUpdate: Release? @State private var selectedUpdate: Release?
@State private var showingAppPathNotice = false @State private var showingAppPathNotice = false
var body: some View { var body: some View {
VStack { VStack {
if storeList.anyAvailable { if storeList.anyAvailable {
@ -31,11 +31,11 @@ struct ContentView<UpdaterType: UpdaterProtocol, AgentStatusCheckerType: AgentSt
newItem newItem
} }
} }
} }
extension ContentView { extension ContentView {
var updateNotice: ToolbarItem<Void, AnyView> { var updateNotice: ToolbarItem<Void, AnyView> {
guard let update = updater.update else { guard let update = updater.update else {
return ToolbarItem { AnyView(EmptyView()) } return ToolbarItem { AnyView(EmptyView()) }
@ -46,8 +46,13 @@ extension ContentView {
text = "Critical Security Update Required" text = "Critical Security Update Required"
color = .red color = .red
} else { } else {
text = "Update Available" if updater.testBuild {
color = .orange text = "Test Build"
color = .blue
} else {
text = "Update Available"
color = .orange
}
} }
return ToolbarItem { return ToolbarItem {
AnyView( AnyView(
@ -58,15 +63,15 @@ 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)
} }
) )
} }
} }
var newItem: ToolbarItem<Void, AnyView> { var newItem: ToolbarItem<Void, AnyView> {
guard storeList.modifiableStore?.isAvailable ?? false else { guard storeList.modifiableStore?.isAvailable ?? false else {
return ToolbarItem { AnyView(EmptyView()) } return ToolbarItem { AnyView(EmptyView()) }
@ -78,21 +83,21 @@ 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)
}
} }
}
) )
} }
} }
var setupNotice: ToolbarItem<Void, AnyView> { var setupNotice: ToolbarItem<Void, AnyView> {
return ToolbarItem { return ToolbarItem {
AnyView( AnyView(
Group { Group {
if runningSetup || !hasRunSetup || !agentStatusChecker.running { if (runningSetup || !hasRunSetup || !agentStatusChecker.running) && !agentStatusChecker.developmentBuild {
Button(action: { Button(action: {
runningSetup = true runningSetup = true
}, label: { }, label: {
@ -106,19 +111,19 @@ 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) { .sheet(isPresented: $runningSetup) {
SetupView(visible: $runningSetup, setupComplete: $hasRunSetup) SetupView(visible: $runningSetup, setupComplete: $hasRunSetup)
} }
) )
} }
} }
var appPathNotice: ToolbarItem<Void, AnyView> { var appPathNotice: ToolbarItem<Void, AnyView> {
let controller = ApplicationDirectoryController() let controller = ApplicationDirectoryController()
guard !controller.isInApplicationsDirectory else { guard !controller.isInApplicationsDirectory else {
@ -135,29 +140,29 @@ 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()
}
) )
} }
} }
} }
#if DEBUG #if DEBUG
struct ContentView_Previews: PreviewProvider { struct ContentView_Previews: PreviewProvider {
private static let storeList: SecretStoreList = { private static let storeList: SecretStoreList = {
let list = SecretStoreList() let list = SecretStoreList()
list.add(store: SecureEnclave.Store()) list.add(store: SecureEnclave.Store())
@ -166,11 +171,11 @@ struct ContentView_Previews: PreviewProvider {
}() }()
private static let agentStatusChecker = AgentStatusChecker() private static let agentStatusChecker = AgentStatusChecker()
private static let justUpdatedChecker = JustUpdatedChecker() private static let justUpdatedChecker = JustUpdatedChecker()
@State var hasRunSetup = false @State var hasRunSetup = false
@State private var showingSetup = false @State private var showingSetup = false
@State private var showingCreation = false @State private var showingCreation = false
static var previews: some View { static var previews: some View {
Group { Group {
// Empty on modifiable and nonmodifiable // Empty on modifiable and nonmodifiable
@ -178,7 +183,7 @@ struct ContentView_Previews: PreviewProvider {
.environmentObject(Preview.storeList(stores: [Preview.Store(numberOfRandomSecrets: 0)], modifiableStores: [Preview.StoreModifiable(numberOfRandomSecrets: 0)])) .environmentObject(Preview.storeList(stores: [Preview.Store(numberOfRandomSecrets: 0)], modifiableStores: [Preview.StoreModifiable(numberOfRandomSecrets: 0)]))
.environmentObject(PreviewUpdater()) .environmentObject(PreviewUpdater())
.environmentObject(agentStatusChecker) .environmentObject(agentStatusChecker)
// 5 items on modifiable and nonmodifiable // 5 items on modifiable and nonmodifiable
ContentView<PreviewUpdater, AgentStatusChecker>(showingCreation: .constant(false), runningSetup: .constant(false), hasRunSetup: .constant(true)) ContentView<PreviewUpdater, AgentStatusChecker>(showingCreation: .constant(false), runningSetup: .constant(false), hasRunSetup: .constant(true))
.environmentObject(Preview.storeList(stores: [Preview.Store()], modifiableStores: [Preview.StoreModifiable()])) .environmentObject(Preview.storeList(stores: [Preview.Store()], modifiableStores: [Preview.StoreModifiable()]))
@ -186,7 +191,7 @@ struct ContentView_Previews: PreviewProvider {
.environmentObject(agentStatusChecker) .environmentObject(agentStatusChecker)
} }
.environmentObject(agentStatusChecker) .environmentObject(agentStatusChecker)
} }
} }