Skip to content

Commit 04851e7

Browse files
authored
Install plugin by archive remote url or local path (stripe#937)
* install plugin by archive remote url or local path * do not duplicate unmanaged versions in config * add more sanity check * remove unused param
1 parent e3bd893 commit 04851e7

File tree

3 files changed

+287
-31
lines changed

3 files changed

+287
-31
lines changed

pkg/cmd/plugin/install.go

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ type InstallCmd struct {
2424
cfg *config.Config
2525
Cmd *cobra.Command
2626
fs afero.Fs
27+
28+
archiveURL string
29+
archivePath string
2730
}
2831

2932
// NewInstallCmd creates a command for installing plugins
@@ -34,13 +37,16 @@ func NewInstallCmd(config *config.Config) *InstallCmd {
3437

3538
ic.Cmd = &cobra.Command{
3639
Use: "install",
37-
Args: validators.ExactArgs(1),
40+
Args: validators.MaximumNArgs(1),
3841
Short: "Install a Stripe CLI plugin",
3942
Long: `Install a Stripe CLI plugin. To download a specific version, run stripe install [plugin_name]@[version].
4043
By default, the most recent version will be installed.`,
4144
RunE: ic.runInstallCmd,
4245
}
4346

47+
ic.Cmd.Flags().StringVar(&ic.archiveURL, "archive-url", "", "Install a plugin by an archive URL")
48+
ic.Cmd.Flags().StringVar(&ic.archivePath, "archive", "", "Install a plugin by an archive path")
49+
4450
return ic
4551
}
4652

@@ -58,15 +64,8 @@ func parseInstallArg(arg string) (string, string) {
5864
return plugin, version
5965
}
6066

61-
func (ic *InstallCmd) runInstallCmd(cmd *cobra.Command, args []string) error {
62-
// Refresh the plugin before proceeding
63-
err := plugins.RefreshPluginManifest(cmd.Context(), ic.cfg, ic.fs, stripe.DefaultAPIBaseURL)
64-
65-
if err != nil {
66-
return err
67-
}
68-
69-
pluginName, version := parseInstallArg(args[0])
67+
func (ic *InstallCmd) installPluginByName(cmd *cobra.Command, arg string) error {
68+
pluginName, version := parseInstallArg(arg)
7069
plugin, err := plugins.LookUpPlugin(cmd.Context(), ic.cfg, ic.fs, pluginName)
7170

7271
if err != nil {
@@ -85,12 +84,53 @@ func (ic *InstallCmd) runInstallCmd(cmd *cobra.Command, args []string) error {
8584

8685
err = plugin.Install(ctx, ic.cfg, ic.fs, version, stripe.DefaultAPIBaseURL)
8786

87+
return err
88+
}
89+
90+
func (ic *InstallCmd) installPluginByArchive(cmd *cobra.Command) error {
91+
if ic.archiveURL == "" && ic.archivePath == "" {
92+
return fmt.Errorf("please provide the plugin name or the archive URL/path to install")
93+
}
94+
95+
if ic.archiveURL != "" {
96+
err := plugins.FetchAndExtractRemoteArchive(cmd.Context(), ic.cfg, ic.archiveURL)
97+
if err != nil {
98+
return err
99+
}
100+
} else if ic.archivePath != "" {
101+
err := plugins.ExtractLocalArchive(cmd.Context(), ic.cfg, ic.archivePath)
102+
if err != nil {
103+
return err
104+
}
105+
}
106+
107+
return nil
108+
}
109+
110+
func (ic *InstallCmd) runInstallCmd(cmd *cobra.Command, args []string) error {
111+
var err error
112+
113+
if len(args) == 0 {
114+
err = ic.installPluginByArchive(cmd)
115+
} else {
116+
// Refresh the plugin before proceeding
117+
err = plugins.RefreshPluginManifest(cmd.Context(), ic.cfg, ic.fs, stripe.DefaultAPIBaseURL)
118+
if err != nil {
119+
return err
120+
}
121+
122+
err = ic.installPluginByName(cmd, args[0])
123+
if err != nil {
124+
return err
125+
}
126+
}
127+
88128
if err == nil {
89129
color := ansi.Color(os.Stdout)
90130
fmt.Println(color.Green("✔ installation complete."))
91131
}
92132

93-
return err
133+
return nil
94134
}
95135

96136
func withSIGTERMCancel(ctx context.Context, onCancel func()) context.Context {

pkg/plugins/plugin.go

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@ var (
3333

3434
// Plugin contains the plugin properties
3535
type Plugin struct {
36-
Shortname string
37-
Shortdesc string
38-
Binary string
36+
Shortname string `toml:"Shortname"`
37+
Shortdesc string `toml:"Shortdesc"`
38+
Binary string `toml:"Binary"`
3939
Releases []Release `toml:"Release"`
40-
MagicCookieValue string
40+
MagicCookieValue string `toml:"MagicCookieValue"`
4141
}
4242

4343
// PluginList contains a list of plugins
@@ -47,10 +47,11 @@ type PluginList struct {
4747

4848
// Release is the type that holds release data for a specific build of a plugin
4949
type Release struct {
50-
Arch string
51-
OS string
52-
Version string
53-
Sum string
50+
Arch string `toml:"Arch"`
51+
OS string `toml:"OS"`
52+
Version string `toml:"Version"`
53+
Sum string `toml:"Sum"`
54+
Unmanaged bool `toml:"Unmanaged"`
5455
}
5556

5657
// getPluginInterface computes the correct metadata needed for starting the hcplugin client
@@ -250,6 +251,20 @@ func (p *Plugin) Uninstall(ctx context.Context, config config.IConfig, fs afero.
250251
}
251252

252253
func (p *Plugin) downloadAndSavePlugin(config config.IConfig, pluginDownloadURL string, fs afero.Fs, version string) error {
254+
body, err := FetchRemoteResource(pluginDownloadURL)
255+
if err != nil {
256+
return err
257+
}
258+
259+
err = p.verifychecksumAndSavePlugin(body, config, fs, version)
260+
if err != nil {
261+
return err
262+
}
263+
264+
return nil
265+
}
266+
267+
func (p *Plugin) verifychecksumAndSavePlugin(pluginData []byte, config config.IConfig, fs afero.Fs, version string) error {
253268
logger := log.WithFields(log.Fields{
254269
"prefix": "plugins.plugin.Install",
255270
})
@@ -260,30 +275,21 @@ func (p *Plugin) downloadAndSavePlugin(config config.IConfig, pluginDownloadURL
260275

261276
logger.Debugf("installing %s to %s...", p.Shortname, pluginFilePath)
262277

263-
body, err := FetchRemoteResource(pluginDownloadURL)
264-
265-
if err != nil {
266-
return err
267-
}
268-
269-
reader := bytes.NewReader(body)
270-
271-
err = p.verifyChecksum(reader, version)
278+
reader := bytes.NewReader(pluginData)
272279

280+
err := p.verifyChecksum(reader, version)
273281
if err != nil {
274282
logger.Debug("could not match checksum of plugin")
275283
return err
276284
}
277285

278286
err = fs.MkdirAll(pluginDir, 0755)
279-
280287
if err != nil {
281288
logger.Debugf("could not create plugin directory: %s", pluginDir)
282289
return err
283290
}
284291

285-
err = afero.WriteFile(fs, pluginFilePath, body, 0755)
286-
292+
err = afero.WriteFile(fs, pluginFilePath, pluginData, 0755)
287293
if err != nil {
288294
logger.Debug("could not save plugin to disk")
289295
return err

0 commit comments

Comments
 (0)