newPlugin determines if the given candidate is valid and returns a Plugin. If the candidate fails one of the tests then `Plugin.Err` is set, and is always a `pluginError`, but the `Plugin` is still returned with no error. An error is only returned due to a non-recoverable error.
(c pluginCandidate, cmds []*cobra.Command)
| 59 | // returned with no error. An error is only returned due to a |
| 60 | // non-recoverable error. |
| 61 | func newPlugin(c pluginCandidate, cmds []*cobra.Command) (Plugin, error) { |
| 62 | path := c.Path() |
| 63 | if path == "" { |
| 64 | return Plugin{}, errors.New("plugin candidate path cannot be empty") |
| 65 | } |
| 66 | |
| 67 | // The candidate listing process should have skipped anything |
| 68 | // which would fail here, so there are all real errors. |
| 69 | fullname := filepath.Base(path) |
| 70 | if fullname == "." { |
| 71 | return Plugin{}, fmt.Errorf("unable to determine basename of plugin candidate %q", path) |
| 72 | } |
| 73 | var err error |
| 74 | if fullname, err = trimExeSuffix(fullname); err != nil { |
| 75 | return Plugin{}, fmt.Errorf("plugin candidate %q: %w", path, err) |
| 76 | } |
| 77 | if !strings.HasPrefix(fullname, metadata.NamePrefix) { |
| 78 | return Plugin{}, fmt.Errorf("plugin candidate %q: does not have %q prefix", path, metadata.NamePrefix) |
| 79 | } |
| 80 | |
| 81 | p := Plugin{ |
| 82 | Name: strings.TrimPrefix(fullname, metadata.NamePrefix), |
| 83 | Path: path, |
| 84 | } |
| 85 | |
| 86 | // Now apply the candidate tests, so these update p.Err. |
| 87 | if !isValidPluginName(p.Name) { |
| 88 | p.Err = newPluginError("plugin candidate %q did not match %q", p.Name, pluginNameFormat) |
| 89 | return p, nil |
| 90 | } |
| 91 | |
| 92 | for _, cmd := range cmds { |
| 93 | // Ignore conflicts with commands which are |
| 94 | // just plugin stubs (i.e. from a previous |
| 95 | // call to AddPluginCommandStubs). |
| 96 | if IsPluginCommand(cmd) { |
| 97 | continue |
| 98 | } |
| 99 | if cmd.Name() == p.Name { |
| 100 | p.Err = newPluginError("plugin %q duplicates builtin command", p.Name) |
| 101 | return p, nil |
| 102 | } |
| 103 | if cmd.HasAlias(p.Name) { |
| 104 | p.Err = newPluginError("plugin %q duplicates an alias of builtin command %q", p.Name, cmd.Name()) |
| 105 | return p, nil |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | // We are supposed to check for relevant execute permissions here. Instead we rely on an attempt to execute. |
| 110 | meta, err := c.Metadata() |
| 111 | if err != nil { |
| 112 | p.Err = wrapAsPluginError(err, "failed to fetch metadata") |
| 113 | return p, nil |
| 114 | } |
| 115 | |
| 116 | if err := json.Unmarshal(meta, &p.Metadata); err != nil { |
| 117 | p.Err = wrapAsPluginError(err, "invalid metadata") |
| 118 | return p, nil |
searching dependent graphs…