diff --git a/cmd/set.go b/cmd/set.go index 7475f5a..4e70e18 100644 --- a/cmd/set.go +++ b/cmd/set.go @@ -13,12 +13,16 @@ package cmd import ( + "fmt" + "io/fs" "os" "strings" "git.rob.mx/nidito/joao/internal/command" + opclient "git.rob.mx/nidito/joao/internal/op-client" "git.rob.mx/nidito/joao/internal/registry" "git.rob.mx/nidito/joao/pkg/config" + "github.com/sirupsen/logrus" ) func init() { @@ -70,6 +74,10 @@ Will read from stdin (or ﹅--from﹅ a file) and store it at the ﹅PATH Description: "Treat input as JSON-encoded", Type: "bool", }, + "flush": { + Description: "Save to 1Password after saving to file", + Type: "bool", + }, }, Action: func(cmd *command.Command) error { path := cmd.Arguments[0].ToValue().(string) @@ -80,6 +88,7 @@ Will read from stdin (or ﹅--from﹅ a file) and store it at the ﹅PATH secret := cmd.Options["secret"].ToValue().(bool) input := cmd.Options["input"].ToValue().(string) parseJSON := cmd.Options["json"].ToValue().(bool) + flush := cmd.Options["flush"].ToValue().(bool) cfg, err = config.Load(path, false) if err != nil { @@ -103,7 +112,23 @@ Will read from stdin (or ﹅--from﹅ a file) and store it at the ﹅PATH return err } - _, err = cmd.Cobra.OutOrStdout().Write(b) + var mode fs.FileMode = 644 + // var mode uint32 = + if info, err := os.Stat(path); err == nil { + mode = info.Mode().Perm() + } + + if err := os.WriteFile(path, b, mode); err != nil { + return fmt.Errorf("could not save changes to %s: %w", path, err) + } + + if flush { + if err := opclient.Update(cfg.Vault, cfg.Name, cfg.ToOP()); err != nil { + return fmt.Errorf("could not flush to 1password: %w", err) + } + } + + logrus.Info("Done") return err }, }).SetBindings() diff --git a/go.mod b/go.mod index 33aec83..b69639a 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.18 require ( github.com/1Password/connect-sdk-go v1.5.0 + github.com/alessio/shellescape v1.4.1 github.com/charmbracelet/glamour v0.6.0 github.com/fatih/color v1.13.0 github.com/go-playground/validator/v10 v10.11.1 diff --git a/go.sum b/go.sum index 5ef7d8f..dde8adf 100644 --- a/go.sum +++ b/go.sum @@ -7,6 +7,8 @@ github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXY github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek= github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s= +github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= +github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= github.com/aymanbagabas/go-osc52 v1.0.3 h1:DTwqENW7X9arYimJrPeGZcV0ln14sGMt3pHZspWD+Mg= github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= diff --git a/internal/command/arguments.go b/internal/command/arguments.go index 18b84c8..7c41f50 100644 --- a/internal/command/arguments.go +++ b/internal/command/arguments.go @@ -173,9 +173,6 @@ func (arg *Argument) IsKnown() bool { func (arg *Argument) ToString() string { val := arg.ToValue() - if val == nil { - return "" - } if arg.Variadic { val := val.([]string) diff --git a/internal/command/command.go b/internal/command/command.go index be421b4..860716b 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -21,8 +21,8 @@ import ( "github.com/spf13/pflag" ) -type CommandHelpFunc func(printLinks bool) string -type CommandAction func(cmd *Command) error +type HelpFunc func(printLinks bool) string +type Action func(cmd *Command) error type Command struct { Path []string @@ -33,10 +33,10 @@ type Command struct { // A list of arguments for a command Arguments Arguments `json:"arguments" yaml:"arguments" validate:"dive"` // A map of option names to option definitions - Options Options `json:"options" yaml:"options" validate:"dive"` - HelpFunc CommandHelpFunc `json:"-" yaml:"-"` + Options Options `json:"options" yaml:"options" validate:"dive"` + HelpFunc HelpFunc `json:"-" yaml:"-"` // The action to take upon running - Action CommandAction + Action Action runtimeFlags *pflag.FlagSet Cobra *cobra.Command } diff --git a/internal/command/value.go b/internal/command/value.go index 3dcf8f5..232ed1f 100644 --- a/internal/command/value.go +++ b/internal/command/value.go @@ -157,7 +157,6 @@ func (vs *ValueSource) Resolve(currentValue string) (values []string, flag cobra values = strings.Split(stdout.String(), "\n") flag = cobra.ShellCompDirectiveDefault - err = nil case vs.Script != "": if vs.command == nil { return nil, cobra.ShellCompDirectiveError, fmt.Errorf("bug: command is nil") @@ -178,7 +177,7 @@ func (vs *ValueSource) Resolve(currentValue string) (values []string, flag cobra vs.computed = &values if vs.SuggestRaw { - flag = flag | cobra.ShellCompDirectiveNoSpace + flag |= cobra.ShellCompDirectiveNoSpace } vs.flag = flag diff --git a/internal/op-client/cli.go b/internal/op-client/cli.go index 0f9f16c..63798b7 100644 --- a/internal/op-client/cli.go +++ b/internal/op-client/cli.go @@ -10,7 +10,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -package opClient +package opclient import ( "bytes" @@ -18,14 +18,21 @@ import ( "fmt" "os" "os/exec" + "strings" op "github.com/1Password/connect-sdk-go/onepassword" + "github.com/alessio/shellescape" + "github.com/sirupsen/logrus" ) type CLI struct{} -func (b *CLI) Get(vault, name string) (*op.Item, error) { - cmd := exec.Command("op", "item", "--format", "json", "--vault", vault, "get", name) +func invoke(vault string, args ...string) (bytes.Buffer, error) { + if vault != "" { + args = append([]string{"--vault", shellescape.Quote(vault)}, args...) + } + logrus.Debugf("invoking op with args: %s", args) + cmd := exec.Command("op", args...) cmd.Env = os.Environ() var stdout bytes.Buffer @@ -33,10 +40,19 @@ func (b *CLI) Get(vault, name string) (*op.Item, error) { var stderr bytes.Buffer cmd.Stderr = &stderr if err := cmd.Run(); err != nil { - return nil, err + return stderr, err } if cmd.ProcessState.ExitCode() > 0 { - return nil, fmt.Errorf("op exited with %d: %s", cmd.ProcessState.ExitCode(), stderr.Bytes()) + return stderr, fmt.Errorf("op exited with %d: %s", cmd.ProcessState.ExitCode(), stderr.Bytes()) + } + + return stdout, nil +} + +func (b *CLI) Get(vault, name string) (*op.Item, error) { + stdout, err := invoke(vault, "item", "--format", "json", "get", name) + if err != nil { + return nil, err } var item *op.Item @@ -47,7 +63,96 @@ func (b *CLI) Get(vault, name string) (*op.Item, error) { return item, nil } +func (b *CLI) create(item *op.Item) error { + logrus.Infof("Creating new item: %s/%s", item.Vault.ID, item.Title) + cmd := exec.Command("op", "--vault", shellescape.Quote(item.Vault.ID), "item", "create") + + itemJSON, err := json.Marshal(item) + if err != nil { + return fmt.Errorf("could not serialize op item into json: %w", err) + } + + cmd.Stdin = bytes.NewBuffer(itemJSON) + cmd.Env = os.Environ() + var stdout bytes.Buffer + cmd.Stdout = &stdout + var stderr bytes.Buffer + cmd.Stderr = &stderr + if err := cmd.Run(); err != nil { + return fmt.Errorf("could not create item: %w", err) + } + + if cmd.ProcessState.ExitCode() > 0 { + return fmt.Errorf("op exited with %d: %s", cmd.ProcessState.ExitCode(), stderr.Bytes()) + } + logrus.Infof("Item %s/%s created", item.Vault.ID, item.Title) + return nil +} + +type hashResult int + +const ( + HashItemError hashResult = iota + HashItemMissing + HashMatch + HashMismatch +) + +func hashesMatch(item *op.Item) (hashResult, error) { + stdout, err := invoke(item.Vault.ID, "item", "get", "--fields", "label=password", item.Title) + if err != nil { + if strings.Contains(stdout.String(), fmt.Sprintf("\"%s\" isn't an item in the \"%s\" vault", item.Vault.ID, item.Title)) { + return HashItemMissing, nil + } + + return HashItemError, err + } + + res := HashMismatch + if strings.TrimSpace(stdout.String()) == item.GetValue("password") { + res = HashMatch + } + return res, nil +} + func (b *CLI) Update(vault, name string, item *op.Item) error { + status, err := hashesMatch(item) + if err != nil { + return err + } + + switch status { + case HashItemMissing: + return b.create(item) + case HashMatch: + logrus.Warn("item is already up to date") + return nil + case HashMismatch: + logrus.Infof("Item %s/%s already exists, updating", item.Vault.ID, item.Title) + } + + args := []string{"item", "edit", name, "--"} + + for _, field := range item.Fields { + kind := strings.ToLower(field.Purpose) + if kind != "password" { + kind = "text" + } + name := strings.ReplaceAll(field.Label, ".", "\\.") + if field.Section != nil { + name = field.Section.ID + "." + name + } + + key := fmt.Sprintf("%s[%s]", name, kind) + args = append(args, fmt.Sprintf("%s=%s", key, field.Value)) + } + + stdout, err := invoke(vault, args...) + if err != nil { + logrus.Errorf("op stderr: %s", stdout.String()) + return err + } + logrus.Infof("Item %s/%s updated", item.Vault.ID, item.Title) return nil } diff --git a/internal/op-client/connect.go b/internal/op-client/connect.go index b6fb232..019f1d8 100644 --- a/internal/op-client/connect.go +++ b/internal/op-client/connect.go @@ -10,17 +10,14 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -package opClient +package opclient import ( - "fmt" - "github.com/1Password/connect-sdk-go/connect" op "github.com/1Password/connect-sdk-go/onepassword" - "github.com/sirupsen/logrus" ) -// UUIDLength defines the required length of UUIDs +// UUIDLength defines the required length of UUIDs. const UUIDLength = 26 // IsValidClientUUID returns true if the given client uuid is valid. @@ -50,30 +47,30 @@ func NewConnect(host, token string) *Connect { return &Connect{client: client} } -func (b *Connect) getVaultId(vaultIdentifier string) (string, error) { - if !IsValidClientUUID(vaultIdentifier) { - vaults, err := b.client.GetVaultsByTitle(vaultIdentifier) - if err != nil { - return "", err - } +// func (b *Connect) getVaultId(vaultIdentifier string) (string, error) { +// if !IsValidClientUUID(vaultIdentifier) { +// vaults, err := b.client.GetVaultsByTitle(vaultIdentifier) +// if err != nil { +// return "", err +// } - if len(vaults) == 0 { - return "", fmt.Errorf("No vaults found with identifier %q", vaultIdentifier) - } +// if len(vaults) == 0 { +// return "", fmt.Errorf("no vaults found with identifier %q", vaultIdentifier) +// } - oldestVault := vaults[0] - if len(vaults) > 1 { - for _, returnedVault := range vaults { - if returnedVault.CreatedAt.Before(oldestVault.CreatedAt) { - oldestVault = returnedVault - } - } - logrus.Infof("%v 1Password vaults found with the title %q. Will use vault %q as it is the oldest.", len(vaults), vaultIdentifier, oldestVault.ID) - } - vaultIdentifier = oldestVault.ID - } - return vaultIdentifier, nil -} +// oldestVault := vaults[0] +// if len(vaults) > 1 { +// for _, returnedVault := range vaults { +// if returnedVault.CreatedAt.Before(oldestVault.CreatedAt) { +// oldestVault = returnedVault +// } +// } +// logrus.Infof("%v 1Password vaults found with the title %q. Will use vault %q as it is the oldest.", len(vaults), vaultIdentifier, oldestVault.ID) +// } +// vaultIdentifier = oldestVault.ID +// } +// return vaultIdentifier, nil +// } func (b *Connect) Get(vault, name string) (*op.Item, error) { return b.client.GetItem(name, vault) diff --git a/internal/op-client/op.go b/internal/op-client/op.go index dece000..8c5f84c 100644 --- a/internal/op-client/op.go +++ b/internal/op-client/op.go @@ -10,7 +10,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -package opClient +package opclient import ( op "github.com/1Password/connect-sdk-go/onepassword" diff --git a/internal/registry/registry.go b/internal/registry/registry.go index 26bee72..bc5d222 100644 --- a/internal/registry/registry.go +++ b/internal/registry/registry.go @@ -153,7 +153,7 @@ func Execute(version string) error { container := ccRoot for idx, cp := range cmd.Path { if idx == len(cmd.Path)-1 { - logrus.Debugf("adding command %s to %s", leaf.Name(), cmd.Path[0:idx]) + // logrus.Debugf("adding command %s to %s", leaf.Name(), cmd.Path[0:idx]) container.AddCommand(leaf) break } diff --git a/pkg/config/config.go b/pkg/config/config.go index 1f4e592..b24904b 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -14,6 +14,7 @@ package config import ( "bytes" + "crypto/md5" "encoding/json" "fmt" "io/ioutil" @@ -25,6 +26,9 @@ import ( "gopkg.in/yaml.v3" ) +const YAMLTypeSecret string = "!!secret" +const YAMLTypeMetaConfig string = "!!joao" + type Config struct { Vault string Name string @@ -55,6 +59,9 @@ var defaultItemFields = []*op.ItemField{ func (cfg *Config) ToMap() map[string]any { ret := map[string]any{} for _, child := range cfg.Tree.Content { + if child.Name() == "" { + continue + } ret[child.Name()] = child.AsMap() } return ret @@ -64,22 +71,26 @@ func (cfg *Config) ToOP() *op.Item { sections := []*op.ItemSection{annotationsSection} fields := append([]*op.ItemField{}, defaultItemFields...) - for _, leaf := range cfg.Tree.Content { - if len(leaf.Content) == 0 { - fields = append(fields, leaf.ToOP()...) + newHash := md5.New() + datafields := cfg.Tree.ToOP() + for _, field := range datafields { + newHash.Write([]byte(field.ID + field.Value)) + } + fields[0].Value = fmt.Sprintf("%x", newHash.Sum(nil)) + fields = append(fields, datafields...) + + for i := 0; i < len(cfg.Tree.Content); i += 2 { + value := cfg.Tree.Content[i+1] + if value.Type == YAMLTypeMetaConfig { continue } - if leaf.Kind != yaml.SequenceNode { + if value.Kind == yaml.MappingNode { sections = append(sections, &op.ItemSection{ - ID: leaf.Name(), - Label: leaf.Name(), + ID: value.Name(), + Label: value.Name(), }) } - - for _, child := range leaf.Content { - fields = append(fields, child.ToOP()...) - } } return &op.Item{ @@ -95,61 +106,57 @@ type opDetails struct { Vault string `yaml:"vault"` Name string `yaml:"name"` NameTemplate string `yaml:"nameTemplate"` + Repo string } -type opConfig interface { - Name() string - Vault() string -} +// type opConfig interface { +// Name() string +// Vault() string +// } -type inFileConfig struct { - *opDetails - *yaml.Node -} +// type inFileConfig struct { +// *opDetails +// *yaml.Node +// } -type virtualConfig struct { - *opDetails -} +// type virtualConfig struct { +// *opDetails +// } -func (ifc *inFileConfig) MarshalYAML() (any, error) { - return ifc.Node, nil -} +// func (ifc *inFileConfig) MarshalYAML() (any, error) { +// return ifc.Node, nil +// } -func (vc *virtualConfig) MarshalYAML() (any, error) { - return nil, nil -} +// func (vc *virtualConfig) MarshalYAML() (any, error) { +// return nil, nil +// } -func (ifc *inFileConfig) UnmarshalYAML(node *yaml.Node) error { - ifc.Node = node - d := &opDetails{} +// func (ifc *inFileConfig) UnmarshalYAML(node *yaml.Node) error { +// ifc.Node = node +// d := &opDetails{} - if err := node.Decode(&d); err != nil { - return err - } - ifc.opDetails = d +// if err := node.Decode(&d); err != nil { +// return err +// } +// ifc.opDetails = d - return nil -} +// return nil +// } -func (ifc *inFileConfig) Name() string { - return ifc.opDetails.Name -} +// func (ifc *inFileConfig) Name() string { +// return ifc.opDetails.Name +// } -func (ifc *inFileConfig) Vault() string { - return ifc.opDetails.Name -} +// func (ifc *inFileConfig) Vault() string { +// return ifc.opDetails.Name +// } type singleModeConfig struct { Config *opDetails `yaml:"_config,omitempty"` } -type repoModeConfig struct { - Repo string - Vault string `yaml:"vault"` - NameTemplate string `yaml:"nameTemplate"` -} - -func ConfigFromFile(path string) (*Config, error) { +// FromFile reads a path and returns a config. +func FromFile(path string) (*Config, error) { buf, err := ioutil.ReadFile(path) if err != nil { return nil, fmt.Errorf("could not read file %s", path) @@ -165,7 +172,7 @@ func ConfigFromFile(path string) (*Config, error) { } logrus.Debugf("Found name: %s and vault: %s", name, vault) - cfg, err := ConfigFromYAML(buf) + cfg, err := FromYAML(buf) if err != nil { return nil, err } @@ -175,12 +182,16 @@ func ConfigFromFile(path string) (*Config, error) { return cfg, nil } -func ConfigFromYAML(data []byte) (*Config, error) { +// FromYAML reads yaml bytes and returns a config. +func FromYAML(data []byte) (*Config, error) { cfg := &Config{ Tree: NewEntry("root", yaml.MappingNode), } - yaml.Unmarshal(data, &cfg.Tree) + err := yaml.Unmarshal(data, &cfg.Tree) + if err != nil { + return nil, err + } return cfg, nil } @@ -188,7 +199,7 @@ func ConfigFromYAML(data []byte) (*Config, error) { func scalarsIn(data map[string]yaml.Node, parents []string) ([]string, error) { keys := []string{} for key, leaf := range data { - if key == "_joao" { + if key == "_config" && len(parents) == 0 { continue } switch leaf.Kind { @@ -219,7 +230,6 @@ func scalarsIn(data map[string]yaml.Node, parents []string) ([]string, error) { default: logrus.Fatalf("found unknown %v at %s", leaf.Kind, key) } - } sort.Strings(keys) @@ -228,12 +238,16 @@ func scalarsIn(data map[string]yaml.Node, parents []string) ([]string, error) { func KeysFromYAML(data []byte) ([]string, error) { cfg := map[string]yaml.Node{} - yaml.Unmarshal(data, &cfg) + err := yaml.Unmarshal(data, &cfg) + if err != nil { + return nil, err + } return scalarsIn(cfg, []string{}) } -func ConfigFromOP(item *op.Item) (*Config, error) { +// FromOP reads a config from an op item and returns a config. +func FromOP(item *op.Item) (*Config, error) { cfg := &Config{ Vault: item.Vault.ID, Name: item.Title, @@ -253,8 +267,7 @@ func (cfg *Config) AsYAML(redacted bool) ([]byte, error) { var out bytes.Buffer enc := yaml.NewEncoder(&out) enc.SetIndent(2) - err := enc.Encode(cfg) - if err != nil { + if err := enc.Encode(cfg); err != nil { return nil, fmt.Errorf("could not serialize config as yaml: %w", err) } return out.Bytes(), nil @@ -290,7 +303,7 @@ func (cfg *Config) Set(path []string, data []byte, isSecret, parseEntry bool) er valueStr = strings.Trim(valueStr, "\n") if isSecret { newEntry.Style = yaml.TaggedStyle - newEntry.Tag = "!!secret" + newEntry.Tag = YAMLTypeSecret } newEntry.Kind = yaml.ScalarNode newEntry.Value = valueStr diff --git a/pkg/config/entry.go b/pkg/config/entry.go index 95db8aa..4f24d63 100644 --- a/pkg/config/entry.go +++ b/pkg/config/entry.go @@ -131,6 +131,9 @@ func (e *Entry) UnmarshalYAML(node *yaml.Node) error { logrus.Errorf("decode map key: %s", keyNode.Value) return err } + if valueNode.Tag == YAMLTypeMetaConfig { + key.Type = YAMLTypeMetaConfig + } value.SetPath(e.Path, key.Value) e.Content = append(e.Content, key, value) } @@ -141,7 +144,7 @@ func (e *Entry) UnmarshalYAML(node *yaml.Node) error { } func (e *Entry) IsSecret() bool { - return e.Tag == "!!secret" + return e.Tag == YAMLTypeSecret } func (e *Entry) TypeStr() string { @@ -252,7 +255,7 @@ func (e *Entry) FromOP(fields []*op.ItemField) error { case "secret": value = secretValue(value.(string)) style = yaml.TaggedStyle - tag = "!!secret" + tag = YAMLTypeSecret default: // either no annotation or an unknown value value = valueStr @@ -295,13 +298,14 @@ func (e *Entry) FromOP(fields []*op.ItemField) error { func (e *Entry) ToOP() []*op.ItemField { ret := []*op.ItemField{} var section *op.ItemSection - name := e.Path[len(e.Path)-1] - if len(e.Path) > 1 { - section = &op.ItemSection{ID: e.Path[0]} - name = strings.Join(e.Path[1:], ".") - } - if len(e.Content) == 0 { + if e.Kind == yaml.ScalarNode { + name := e.Path[len(e.Path)-1] + if len(e.Path) > 1 { + section = &op.ItemSection{ID: e.Path[0]} + name = strings.Join(e.Path[1:], ".") + } + fieldType := "STRING" if e.IsSecret() { fieldType = "CONCEALED" @@ -324,12 +328,24 @@ func (e *Entry) ToOP() []*op.ItemField { Type: fieldType, Value: e.Value, }) - } else { + return ret + } + + if e.Kind == yaml.SequenceNode { + ret := []*op.ItemField{} for _, child := range e.Content { ret = append(ret, child.ToOP()...) } + return ret } + for i := 0; i < len(e.Content); i += 2 { + child := e.Content[i+1] + if child.Type == YAMLTypeMetaConfig { + continue + } + ret = append(ret, child.ToOP()...) + } return ret } diff --git a/pkg/config/lookup.go b/pkg/config/lookup.go index 3e2ff32..587184a 100644 --- a/pkg/config/lookup.go +++ b/pkg/config/lookup.go @@ -20,12 +20,12 @@ import ( "gopkg.in/yaml.v3" ) -func (c *Config) Lookup(query []string) (*Entry, error) { +func (cfg *Config) Lookup(query []string) (*Entry, error) { if len(query) == 0 || len(query) == 1 && query[0] == "." { - return c.Tree, nil + return cfg.Tree, nil } - entry := c.Tree + entry := cfg.Tree for _, part := range query { entry = entry.ChildNamed(part) if entry == nil { @@ -36,12 +36,12 @@ func (c *Config) Lookup(query []string) (*Entry, error) { return entry, nil } -func findRepoConfig(from string) (*repoModeConfig, error) { +func findRepoConfig(from string) (*opDetails, error) { parts := strings.Split(from, "/") - for i := len(parts); i > 0; i -= 1 { + for i := len(parts); i > 0; i-- { query := strings.Join(parts[0:i], "/") if bytes, err := os.ReadFile(query + "/.joao.yaml"); err == nil { - rmc := &repoModeConfig{Repo: query} + rmc := &opDetails{Repo: query} err := yaml.Unmarshal(bytes, rmc) if err != nil { return nil, err diff --git a/pkg/config/util.go b/pkg/config/util.go index 91a63fb..761cd80 100644 --- a/pkg/config/util.go +++ b/pkg/config/util.go @@ -38,7 +38,7 @@ func argIsYAMLFile(path string) bool { // return "config/" + strings.ReplaceAll(name, ":", "/") + ".yaml" // } -func vaultAndNameFrom(path string, buf []byte) (string, string, error) { +func vaultAndNameFrom(path string, buf []byte) (name string, vault string, err error) { smc := &singleModeConfig{} if buf == nil { var err error @@ -48,8 +48,8 @@ func vaultAndNameFrom(path string, buf []byte) (string, string, error) { } } - if err := yaml.Unmarshal(buf, &smc); err == nil && smc.Config != nil { - return smc.Config.Vault, smc.Config.Name, nil + if err = yaml.Unmarshal(buf, &smc); err == nil && smc.Config != nil { + return smc.Config.Name, smc.Config.Vault, nil } rmc, err := findRepoConfig(path) @@ -85,12 +85,11 @@ func vaultAndNameFrom(path string, buf []byte) (string, string, error) { } func Load(ref string, preferRemote bool) (*Config, error) { - isYaml := argIsYAMLFile(ref) if preferRemote { name := ref vault := "" - if isYaml { + if argIsYAMLFile(ref) { var err error name, vault, err = vaultAndNameFrom(ref, nil) if err != nil { @@ -109,12 +108,12 @@ func Load(ref string, preferRemote bool) (*Config, error) { return nil, err } - return ConfigFromOP(item) + return FromOP(item) } - if !isYaml { + if !argIsYAMLFile(ref) { return nil, fmt.Errorf("could not load %s from local as it's not a path", ref) } - return ConfigFromFile(ref) + return FromFile(ref) }