mirror of
https://github.com/maxgoedjen/secretive.git
synced 2025-09-20 03:10:57 +00:00
.
This commit is contained in:
parent
c42bfe38a3
commit
b21abbb49f
@ -3182,6 +3182,50 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"integrations_configure_using_secret_empty_create" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "You'll need to create a Secret before configuring this action."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"integrations_configure_using_secret_header" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Configure Using Secret"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"integrations_configure_using_secret_no_secret" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "No Secret"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"integrations_configure_using_secret_secret_title" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Secret"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"integrations_getting_started_multiple_config" : {
|
"integrations_getting_started_multiple_config" : {
|
||||||
"extractionState" : "manual",
|
"extractionState" : "manual",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
|
@ -47,10 +47,6 @@ public final class PublicKeyFileStoreController: Sendable {
|
|||||||
return directory.appending(component: "\(minimalHex).pub").path()
|
return directory.appending(component: "\(minimalHex).pub").path()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func publicKeyPath(for name: String) -> String {
|
|
||||||
return directory.appending(component: "\(name).pub").path()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Short-circuit check to ship enumerating a bunch of paths if there's nothing in the cert directory.
|
/// Short-circuit check to ship enumerating a bunch of paths if there's nothing in the cert directory.
|
||||||
public var hasAnyCertificates: Bool {
|
public var hasAnyCertificates: Bool {
|
||||||
do {
|
do {
|
||||||
|
@ -51,7 +51,7 @@ struct FauxToolbarModifier<ToolbarContent: View>: ViewModifier {
|
|||||||
var toolbarContent: ToolbarContent
|
var toolbarContent: ToolbarContent
|
||||||
|
|
||||||
func body(content: Content) -> some View {
|
func body(content: Content) -> some View {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
content
|
content
|
||||||
Divider()
|
Divider()
|
||||||
HStack {
|
HStack {
|
||||||
@ -69,6 +69,10 @@ struct FauxToolbarModifier<ToolbarContent: View>: ViewModifier {
|
|||||||
|
|
||||||
struct IntegrationsDetailView: View {
|
struct IntegrationsDetailView: View {
|
||||||
|
|
||||||
|
@Environment(\.secretStoreList) private var secretStoreList
|
||||||
|
@State var creating = false
|
||||||
|
|
||||||
|
@State var selectedSecret: AnySecret?
|
||||||
@Binding private var selectedInstruction: ConfigurationFileInstructions?
|
@Binding private var selectedInstruction: ConfigurationFileInstructions?
|
||||||
private let instructions = Instructions()
|
private let instructions = Instructions()
|
||||||
|
|
||||||
@ -115,13 +119,51 @@ struct IntegrationsDetailView: View {
|
|||||||
.formStyle(.grouped)
|
.formStyle(.grouped)
|
||||||
case .tool:
|
case .tool:
|
||||||
Form {
|
Form {
|
||||||
|
if selectedInstruction.requiresSecret {
|
||||||
|
if secretStoreList.allSecrets.isEmpty {
|
||||||
|
Section {
|
||||||
|
Text(.integrationsConfigureUsingSecretEmptyCreate)
|
||||||
|
if let store = secretStoreList.modifiableStore {
|
||||||
|
HStack {
|
||||||
|
Spacer()
|
||||||
|
Button(.createSecretTitle) {
|
||||||
|
creating = true
|
||||||
|
}
|
||||||
|
.sheet(isPresented: $creating) {
|
||||||
|
CreateSecretView(store: store) { created in
|
||||||
|
selectedSecret = created
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Section {
|
||||||
|
Picker(.integrationsConfigureUsingSecretSecretTitle, selection: $selectedSecret) {
|
||||||
|
if selectedSecret == nil {
|
||||||
|
Text(.integrationsConfigureUsingSecretNoSecret)
|
||||||
|
.tag(nil as (AnySecret?))
|
||||||
|
}
|
||||||
|
ForEach(secretStoreList.allSecrets) { secret in
|
||||||
|
Text(secret.name)
|
||||||
|
.tag(secret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} header: {
|
||||||
|
Text(.integrationsConfigureUsingSecretHeader)
|
||||||
|
}
|
||||||
|
.onAppear {
|
||||||
|
selectedSecret = secretStoreList.allSecrets.first
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
ForEach(selectedInstruction.steps) { stepGroup in
|
ForEach(selectedInstruction.steps) { stepGroup in
|
||||||
Section {
|
Section {
|
||||||
ConfigurationItemView(title: .integrationsPathTitle, value: stepGroup.path, action: .revealInFinder(stepGroup.path))
|
ConfigurationItemView(title: .integrationsPathTitle, value: stepGroup.path, action: .revealInFinder(stepGroup.path))
|
||||||
ForEach(stepGroup.steps, id: \.self.key) { step in
|
ForEach(stepGroup.steps, id: \.self.key) { step in
|
||||||
ConfigurationItemView(title: .integrationsAddThisTitle, action: .copy(String(localized: step))) {
|
ConfigurationItemView(title: .integrationsAddThisTitle, action: .copy(String(localized: step))) {
|
||||||
HStack {
|
HStack {
|
||||||
Text(step)
|
Text(placeholdersReplaced(text: String(localized: step)))
|
||||||
.padding(8)
|
.padding(8)
|
||||||
.font(.system(.subheadline, design: .monospaced))
|
.font(.system(.subheadline, design: .monospaced))
|
||||||
Spacer()
|
Spacer()
|
||||||
@ -180,11 +222,23 @@ struct IntegrationsDetailView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func placeholdersReplaced(text: String) -> String {
|
||||||
|
guard let selectedSecret else { return text }
|
||||||
|
let writer = OpenSSHPublicKeyWriter()
|
||||||
|
let fileController = PublicKeyFileStoreController(homeDirectory: URL.agentHomeURL)
|
||||||
|
return text
|
||||||
|
.replacingOccurrences(of: Instructions.Constants.publicKeyPlaceholder, with: writer.openSSHString(secret: selectedSecret))
|
||||||
|
.replacingOccurrences(of: Instructions.Constants.publicKeyPathPlaceholder, with: fileController.publicKeyPath(for: selectedSecret))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct Instructions {
|
private struct Instructions {
|
||||||
|
|
||||||
private let publicKeyPath = PublicKeyFileStoreController(homeDirectory: URL.agentHomeURL).publicKeyPath(for: String(localized: .integrationsPublicKeyPathPlaceholder))
|
enum Constants {
|
||||||
|
static let publicKeyPathPlaceholder = "_PUBLIC_KEY_PATH_PLACEHOLDER_"
|
||||||
|
static let publicKeyPlaceholder = "_PUBLIC_KEY_PLACEHOLDER_"
|
||||||
|
}
|
||||||
|
|
||||||
var defaultShell: ConfigurationFileInstructions {
|
var defaultShell: ConfigurationFileInstructions {
|
||||||
zsh
|
zsh
|
||||||
@ -207,14 +261,14 @@ private struct Instructions {
|
|||||||
tool: .integrationsToolNameGitSigning,
|
tool: .integrationsToolNameGitSigning,
|
||||||
steps: [
|
steps: [
|
||||||
.init(path: "~/.gitconfig", steps: [
|
.init(path: "~/.gitconfig", steps: [
|
||||||
.integrationsGitStepGitconfigDescription(publicKeyPathPlaceholder: publicKeyPath)
|
.integrationsGitStepGitconfigDescription(publicKeyPathPlaceholder: Constants.publicKeyPathPlaceholder)
|
||||||
],
|
],
|
||||||
note: .integrationsGitStepGitconfigSectionNote
|
note: .integrationsGitStepGitconfigSectionNote
|
||||||
),
|
),
|
||||||
.init(
|
.init(
|
||||||
path: "~/.gitallowedsigners",
|
path: "~/.gitallowedsigners",
|
||||||
steps: [
|
steps: [
|
||||||
.integrationsPublicKeyPlaceholder
|
LocalizedStringResource(stringLiteral: Constants.publicKeyPlaceholder)
|
||||||
],
|
],
|
||||||
note: .integrationsGitStepGitallowedsignersDescription
|
note: .integrationsGitStepGitallowedsignersDescription
|
||||||
),
|
),
|
||||||
@ -293,26 +347,42 @@ struct ConfigurationFileInstructions: Hashable, Identifiable {
|
|||||||
var id: ID
|
var id: ID
|
||||||
var tool: LocalizedStringResource
|
var tool: LocalizedStringResource
|
||||||
var steps: [StepGroup]
|
var steps: [StepGroup]
|
||||||
|
var requiresSecret: Bool
|
||||||
var website: URL?
|
var website: URL?
|
||||||
|
|
||||||
init(tool: LocalizedStringResource, configPath: String, configText: LocalizedStringResource, website: URL? = nil, note: LocalizedStringResource? = nil) {
|
init(
|
||||||
|
tool: LocalizedStringResource,
|
||||||
|
configPath: String,
|
||||||
|
configText: LocalizedStringResource,
|
||||||
|
requiresSecret: Bool = false,
|
||||||
|
website: URL? = nil,
|
||||||
|
note: LocalizedStringResource? = nil
|
||||||
|
) {
|
||||||
self.id = .tool(String(localized: tool))
|
self.id = .tool(String(localized: tool))
|
||||||
self.tool = tool
|
self.tool = tool
|
||||||
self.steps = [StepGroup(path: configPath, steps: [configText], note: note)]
|
self.steps = [StepGroup(path: configPath, steps: [configText], note: note)]
|
||||||
|
self.requiresSecret = requiresSecret
|
||||||
self.website = website
|
self.website = website
|
||||||
}
|
}
|
||||||
|
|
||||||
init(tool: LocalizedStringResource, steps: [StepGroup], website: URL? = nil) {
|
init(
|
||||||
|
tool: LocalizedStringResource,
|
||||||
|
steps: [StepGroup],
|
||||||
|
requiresSecret: Bool = false,
|
||||||
|
website: URL? = nil
|
||||||
|
) {
|
||||||
self.id = .tool(String(localized: tool))
|
self.id = .tool(String(localized: tool))
|
||||||
self.tool = tool
|
self.tool = tool
|
||||||
self.steps = steps
|
self.steps = steps
|
||||||
|
self.requiresSecret = true
|
||||||
self.website = website
|
self.website = website
|
||||||
}
|
}
|
||||||
|
|
||||||
init(_ name: LocalizedStringResource, id: ID) {
|
init(_ name: LocalizedStringResource, id: ID) {
|
||||||
self.id = id
|
self.id = id
|
||||||
tool = name
|
tool = name
|
||||||
self.steps = []
|
steps = []
|
||||||
|
requiresSecret = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func hash(into hasher: inout Hasher) {
|
func hash(into hasher: inout Hasher) {
|
||||||
@ -341,7 +411,6 @@ struct ConfigurationFileInstructions: Hashable, Identifiable {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#Preview {
|
#Preview {
|
||||||
IntegrationsView()
|
IntegrationsView()
|
||||||
.frame(height: 500)
|
.frame(height: 500)
|
||||||
|
Loading…
Reference in New Issue
Block a user