check / check (push) Failing after 2s
REPO_POLICIES.md requires the module root sneak.berlin/go/<name>. go.mod, every import (rewritten with gofmt -r), the -X flags in script/build and the examples in the pkg READMEs now use the new path. go mod tidy lists go-humanize and fatih/color as direct requirements, since internal/cli imports them. This breaks anyone who fetched or imported git.eeqj.de/sneak/secret: they must switch to sneak.berlin/go/secret, which resolves to this repository. Model: opus-5-5
186 lines
5.1 KiB
Go
186 lines
5.1 KiB
Go
package cli
|
|
|
|
import (
|
|
"maps"
|
|
"slices"
|
|
"strings"
|
|
|
|
"github.com/spf13/afero"
|
|
"github.com/spf13/cobra"
|
|
"sneak.berlin/go/secret/internal/vault"
|
|
)
|
|
|
|
// getSecretNamesCompletionFunc returns a completion function that provides
|
|
// secret names
|
|
func getSecretNamesCompletionFunc(fs afero.Fs, stateDir string) func(
|
|
cmd *cobra.Command, args []string, toComplete string,
|
|
) ([]string, cobra.ShellCompDirective) {
|
|
return func(
|
|
_ *cobra.Command, _ []string, toComplete string,
|
|
) ([]string, cobra.ShellCompDirective) {
|
|
// Get current vault
|
|
vlt, err := vault.GetCurrentVault(fs, stateDir)
|
|
if err != nil {
|
|
return nil, cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
|
|
// Get list of secrets
|
|
secrets, err := vlt.ListSecrets()
|
|
if err != nil {
|
|
return nil, cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
|
|
// Filter secrets based on what user has typed
|
|
var completions []string
|
|
|
|
for _, secret := range secrets {
|
|
if strings.HasPrefix(secret, toComplete) {
|
|
completions = append(completions, secret)
|
|
}
|
|
}
|
|
|
|
return completions, cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
}
|
|
|
|
// getUnlockerIDsCompletionFunc returns a completion function that provides
|
|
// unlocker IDs, the names of the unlockers' directories in unlockers.d
|
|
func getUnlockerIDsCompletionFunc(fs afero.Fs, stateDir string) func(
|
|
cmd *cobra.Command, args []string, toComplete string,
|
|
) ([]string, cobra.ShellCompDirective) {
|
|
return func(
|
|
_ *cobra.Command, _ []string, toComplete string,
|
|
) ([]string, cobra.ShellCompDirective) {
|
|
// Get current vault
|
|
vlt, err := vault.GetCurrentVault(fs, stateDir)
|
|
if err != nil {
|
|
return nil, cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
|
|
unlockerMetadata, err := vlt.ListUnlockers()
|
|
if err != nil {
|
|
return nil, cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
|
|
var completions []string
|
|
|
|
for _, id := range slices.Sorted(maps.Keys(unlockerMetadata)) {
|
|
if strings.HasPrefix(id, toComplete) {
|
|
completions = append(completions, id)
|
|
}
|
|
}
|
|
|
|
return completions, cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
}
|
|
|
|
// getVaultNamesCompletionFunc returns a completion function that provides
|
|
// vault names
|
|
func getVaultNamesCompletionFunc(fs afero.Fs, stateDir string) func(
|
|
cmd *cobra.Command, args []string, toComplete string,
|
|
) ([]string, cobra.ShellCompDirective) {
|
|
return func(
|
|
_ *cobra.Command, _ []string, toComplete string,
|
|
) ([]string, cobra.ShellCompDirective) {
|
|
vaults, err := vault.ListVaults(fs, stateDir)
|
|
if err != nil {
|
|
return nil, cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
|
|
var completions []string
|
|
|
|
for _, v := range vaults {
|
|
if strings.HasPrefix(v, toComplete) {
|
|
completions = append(completions, v)
|
|
}
|
|
}
|
|
|
|
return completions, cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
}
|
|
|
|
// completeVaultQualifiedSecrets completes "vault:secret" references once a
|
|
// colon is present in the input. It completes nothing when the vault part
|
|
// is not a valid vault name, so that a name such as ".." cannot list a
|
|
// directory outside vaults.d.
|
|
func completeVaultQualifiedSecrets(
|
|
fs afero.Fs, stateDir, toComplete string,
|
|
) []string {
|
|
var completions []string
|
|
|
|
// Complete secret names for the specified vault
|
|
parts := strings.SplitN(toComplete, ":", vaultSecretParts)
|
|
vaultName := parts[0]
|
|
secretPrefix := parts[1]
|
|
|
|
if vault.ValidateVaultName(vaultName) != nil {
|
|
return nil
|
|
}
|
|
|
|
vlt := vault.NewVault(fs, stateDir, vaultName)
|
|
|
|
secrets, err := vlt.ListSecrets()
|
|
if err == nil {
|
|
for _, secretName := range secrets {
|
|
if strings.HasPrefix(secretName, secretPrefix) {
|
|
completions = append(completions, vaultName+":"+secretName)
|
|
}
|
|
}
|
|
}
|
|
|
|
return completions
|
|
}
|
|
|
|
// completeUnqualifiedVaultSecrets completes vault names (with a ":"
|
|
// suffix) and secrets from the current vault
|
|
func completeUnqualifiedVaultSecrets(
|
|
fs afero.Fs, stateDir, toComplete string,
|
|
) []string {
|
|
var completions []string
|
|
|
|
// Complete vault names with ":" suffix
|
|
vaults, err := vault.ListVaults(fs, stateDir)
|
|
if err == nil {
|
|
for _, v := range vaults {
|
|
if strings.HasPrefix(v, toComplete) {
|
|
completions = append(completions, v+":")
|
|
}
|
|
}
|
|
}
|
|
|
|
// Also complete secrets from current vault (for within-vault moves)
|
|
currentVlt, err := vault.GetCurrentVault(fs, stateDir)
|
|
if err == nil {
|
|
secrets, err := currentVlt.ListSecrets()
|
|
if err == nil {
|
|
for _, secretName := range secrets {
|
|
if strings.HasPrefix(secretName, toComplete) {
|
|
completions = append(completions, secretName)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return completions
|
|
}
|
|
|
|
// getVaultSecretCompletionFunc returns a completion function for the
|
|
// vault:secret format. It completes vault names with ":" suffix, and
|
|
// after ":" it completes secrets from that vault.
|
|
func getVaultSecretCompletionFunc(fs afero.Fs, stateDir string) func(
|
|
cmd *cobra.Command, args []string, toComplete string,
|
|
) ([]string, cobra.ShellCompDirective) {
|
|
return func(
|
|
_ *cobra.Command, _ []string, toComplete string,
|
|
) ([]string, cobra.ShellCompDirective) {
|
|
// Check if we're completing after a vault: prefix
|
|
if strings.Contains(toComplete, ":") {
|
|
return completeVaultQualifiedSecrets(fs, stateDir, toComplete),
|
|
cobra.ShellCompDirectiveNoFileComp
|
|
}
|
|
|
|
return completeUnqualifiedVaultSecrets(fs, stateDir, toComplete),
|
|
cobra.ShellCompDirectiveNoSpace
|
|
}
|
|
}
|