• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mongodb / mongodb-atlas-cli / 25848964073

14 May 2026 07:59AM UTC coverage: 22.479% (-41.3%) from 63.771%
25848964073

push

github

web-flow
build(deps): bump test-summary/action from 2.4 to 2.6 (#4576)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

8987 of 39979 relevant lines covered (22.48%)

0.25 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

5.66
/internal/cli/plugin/install.go
1
// Copyright 2024 MongoDB Inc
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//      http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15
package plugin
16

17
import (
18
        "context"
19
        "fmt"
20
        "os"
21

22
        "github.com/google/go-github/v61/github"
23
        "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/cli"
24
        "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/cli/require"
25
        "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/flag"
26
        "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/plugin"
27
        "github.com/mongodb/mongodb-atlas-cli/atlascli/internal/usage"
28
        "github.com/spf13/cobra"
29
)
30

31
type InstallOpts struct {
32
        cli.ProjectOpts
33
        cli.PreRunOpts
34
        cli.OutputOpts
35
        Opts
36
        ghClient                  *github.Client
37
        githubAsset               *GithubAsset
38
        skipSignatureVerification bool
39
}
40

41
func (opts *InstallOpts) checkForDuplicatePlugins() error {
1✔
42
        _, err := opts.findPluginWithGithubValues(opts.githubAsset.owner, opts.githubAsset.name)
1✔
43
        if err != nil {
2✔
44
                return nil
1✔
45
        }
1✔
46

47
        return fmt.Errorf("a plugin from the repository %s is already installed.\nTo update the plugin run: \n\tatlas plugin update %s", opts.githubAsset.repository(), opts.githubAsset.repository())
1✔
48
}
49

50
// checks if the plugin manifest is valid and that the plugin
51
// doesn't contain any commands that conflict with existing CLI commands.
52
func (opts *InstallOpts) validatePlugin(pluginDirectoryPath string) error {
×
53
        // Get the manifest from the plugin directory
×
54
        manifest, err := plugin.GetManifestFromPluginDirectory(pluginDirectoryPath)
×
55
        if err != nil {
×
56
                return err
×
57
        }
×
58

59
        err = validateManifest(manifest)
×
60
        if err != nil {
×
61
                return err
×
62
        }
×
63

64
        // check for duplicate plugin names
65
        for _, p := range opts.getValidPlugins() {
×
66
                if manifest.Name == p.Name {
×
67
                        return fmt.Errorf("a plugin with the name %s already exists", manifest.Name)
×
68
                }
×
69
        }
70

71
        // Check for duplicate commands
72
        existingCommandsSet := createExistingCommandsSet(opts.existingCommands)
×
73
        if manifest.HasDuplicateCommand(existingCommandsSet) {
×
74
                return fmt.Errorf(`could not load plugin "%s" because it contains a command that already exists in the AtlasCLI or another plugin`, opts.githubAsset.repository())
×
75
        }
×
76

77
        return nil
×
78
}
79

80
func (opts *InstallOpts) Run(ctx context.Context) error {
×
81
        // get all plugin assets info from github repository
×
82
        assets, err := opts.githubAsset.getReleaseAssets(opts.ghClient)
×
83
        if err != nil {
×
84
                return err
×
85
        }
×
86

87
        // find correct assetID, signatureID and pubKeyID using system requirements
88
        assetID, signatureID, pubKeyID, err := opts.githubAsset.getIDs(assets)
×
89
        if err != nil {
×
90
                return err
×
91
        }
×
92

93
        // When signatureID and pubKeyID are 0, the signature check is skipped.
94
        if opts.skipSignatureVerification {
×
95
                signatureID = 0
×
96
                pubKeyID = 0
×
97
        }
×
98

99
        // download plugin asset archive file and save it as ReadCloser
100
        rc, err := opts.githubAsset.getPluginAssetsAsReadCloser(opts.ghClient, assetID, signatureID, pubKeyID)
×
101
        if err != nil {
×
102
                return err
×
103
        }
×
104
        defer rc.Close()
×
105

×
106
        // use the ReadCloser to save the asset archive file in the default plugin directory
×
107
        pluginArchiveFilePath, err := saveReadCloserToPluginAssetArchiveFile(rc)
×
108
        if err != nil {
×
109
                return err
×
110
        }
×
111
        defer os.Remove(pluginArchiveFilePath) // delete archive file after install command finishes
×
112

×
113
        // try to extract content of plugin archive file and save it in default plugin directory
×
114
        pluginDirectoryPath, err := extractPluginAssetArchiveFile(ctx, pluginArchiveFilePath, opts.githubAsset.getPluginDirectoryName())
×
115
        if err != nil {
×
116
                return err
×
117
        }
×
118

119
        // validate the extracted plugin files
120
        // if plugin is invalid, delete all of its files
121
        err = opts.validatePlugin(pluginDirectoryPath)
×
122
        if err != nil {
×
123
                os.RemoveAll(pluginDirectoryPath)
×
124
                return err
×
125
        }
×
126

127
        return opts.Print(fmt.Sprintf("Plugin %s successfully installed", opts.githubAsset.repository()))
×
128
}
129

130
func InstallBuilder(pluginOpts *Opts) *cobra.Command {
×
131
        opts := &InstallOpts{
×
132
                ghClient: NewAuthenticatedGithubClient(),
×
133
        }
×
134
        opts.Opts = *pluginOpts
×
135

×
136
        const use = "install"
×
137
        cmd := &cobra.Command{
×
138
                Use:     use + " [<github-owner>/<github-repository-name>]",
×
139
                Aliases: cli.GenerateAliases(use),
×
140
                Annotations: map[string]string{
×
141
                        "<github-owner>/<github-repository-name>Desc": "Repository identifier.",
×
142
                },
×
143
                Short: "Install Atlas CLI plugin from a GitHub repository.",
×
144
                Long: `Install an Atlas CLI plugin from a GitHub repository.
×
145
You can specify a GitHub repository using either the "<github-owner>/<github-repository-name>" format or a full URL.
×
146
When you install the plugin, its latest release on GitHub is used by default.
×
147
To install a specific version of the plugin, append the version number directly to the plugin name using the @ symbol.
×
148

×
149
MongoDB provides an example plugin: https://github.com/mongodb/atlas-cli-plugin-example
×
150
`,
×
151
                Args: require.ExactArgs(1),
×
152
                Example: `  # Install the latest version of the plugin:
×
153
  atlas plugin install mongodb/atlas-cli-plugin-example
×
154
  atlas plugin install https://github.com/mongodb/atlas-cli-plugin-example
×
155
  
×
156
  # Install a specific version of the plugin:
×
157
  atlas plugin install mongodb/atlas-cli-plugin-example@1.0.4
×
158
  atlas plugin install https://github.com/mongodb/atlas-cli-plugin-example/@v1.2.3`,
×
159
                PreRunE: func(_ *cobra.Command, args []string) error {
×
160
                        githubAssetRelease, err := parseGithubReleaseValues(args[0])
×
161
                        if err != nil {
×
162
                                return err
×
163
                        }
×
164
                        opts.githubAsset = githubAssetRelease
×
165

×
166
                        return opts.PreRunE(opts.checkForDuplicatePlugins)
×
167
                },
168
                RunE: func(cmd *cobra.Command, _ []string) error {
×
169
                        return opts.Run(cmd.Context())
×
170
                },
×
171
        }
172

173
        cmd.Flags().BoolVar(&opts.skipSignatureVerification, flag.SkipSignatureVerification, false, usage.SkipSignatureVerification)
×
174

×
175
        return cmd
×
176
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc