2026-09-15 17:10:54 +05:30
// Copyright IBM Corp. 2024, 2026
2023-08-10 15:53:29 -07:00
// SPDX-License-Identifier: BUSL-1.1
2023-03-02 15:37:05 -05:00
2022-02-10 22:53:50 +01:00
package command
import (
"context"
"crypto/sha256"
2023-10-25 16:06:45 -04:00
"fmt"
2024-03-12 16:16:54 -04:00
"log"
2022-02-10 22:53:50 +01:00
"os"
2024-03-12 16:16:54 -04:00
"path/filepath"
2022-02-10 22:53:50 +01:00
"runtime"
"strings"
"github.com/hashicorp/go-version"
2024-03-12 16:16:54 -04:00
"github.com/hashicorp/hcl/v2"
2022-02-10 22:53:50 +01:00
"github.com/hashicorp/packer/hcl2template/addrs"
2024-03-12 16:16:54 -04:00
"github.com/hashicorp/packer/packer"
2022-02-10 22:53:50 +01:00
plugingetter "github.com/hashicorp/packer/packer/plugin-getter"
"github.com/mitchellh/cli"
)
type PluginsRemoveCommand struct {
Meta
}
func ( c * PluginsRemoveCommand ) Synopsis () string {
return "Remove Packer plugins [matching a version]"
}
func ( c * PluginsRemoveCommand ) Help () string {
helpText := `
Usage: packer plugins remove <plugin> [<version constraint>]
2024-03-12 16:16:54 -04:00
This command will remove one or more installed Packer plugins.
2022-02-10 22:53:50 +01:00
2024-05-13 15:01:17 -04:00
To remove a plugin matching a version constraint for the current OS and architecture.
2024-03-12 16:16:54 -04:00
packer plugins remove github.com/hashicorp/happycloud v1.2.3
To remove all versions of a plugin for the current OS and architecture omit the version constraint.
packer plugins remove github.com/hashicorp/happycloud
To remove a single plugin binary from the Packer plugin directory specify the absolute path to an installed binary. This syntax does not allow for version matching.
packer plugins remove ~/.config/plugins/github.com/hashicorp/happycloud/packer-plugin-happycloud_v1.0.0_x5.0_linux_amd64
2022-02-10 22:53:50 +01:00
`
return strings . TrimSpace ( helpText )
}
func ( c * PluginsRemoveCommand ) Run ( args [] string ) int {
ctx , cleanup := handleTermInterrupt ( c . Ui )
defer cleanup ()
return c . RunContext ( ctx , args )
}
2024-03-12 16:16:54 -04:00
// deletePluginBinary removes a local plugin binary, and its related checksum file.
func deletePluginBinary ( pluginPath string ) error {
if err := os . Remove ( pluginPath ); err != nil {
return err
}
shasumFile := fmt . Sprintf ( "%s_SHA256SUM" , pluginPath )
if _ , err := os . Stat ( shasumFile ); err != nil {
log . Printf ( "[INFO] No SHA256SUM file to remove for the plugin, ignoring." )
return nil
}
return os . Remove ( shasumFile )
}
2022-02-10 22:53:50 +01:00
func ( c * PluginsRemoveCommand ) RunContext ( buildCtx context . Context , args [] string ) int {
if len ( args ) < 1 || len ( args ) > 2 {
return cli . RunResultHelp
}
2024-03-12 16:16:54 -04:00
pluginDir , err := packer . PluginFolder ()
if err != nil {
return writeDiags ( c . Ui , nil , hcl . Diagnostics {
& hcl . Diagnostic {
Severity : hcl . DiagError ,
Summary : "Failed to get the plugin directory" ,
Detail : fmt . Sprintf (
"The directory in which plugins are installed could not be fetched from the environment. This is likely a Packer bug. Error: %s" ,
err ),
},
})
}
if filepath . IsAbs ( args [ 0 ]) {
if len ( args ) != 1 {
c . Ui . Error ( "Unsupported: no version constraint may be specified with a local plugin path.\n" )
return cli . RunResultHelp
}
if ! strings . Contains ( args [ 0 ], pluginDir ) {
return writeDiags ( c . Ui , nil , hcl . Diagnostics {
& hcl . Diagnostic {
Severity : hcl . DiagError ,
Summary : "Invalid plugin location" ,
Detail : fmt . Sprintf (
"The path %q is not under the plugin directory inferred by Packer (%s) and will not be removed." ,
args [ 0 ],
pluginDir ),
},
})
}
log . Printf ( "will delete plugin located at %q" , args [ 0 ])
err := deletePluginBinary ( args [ 0 ])
if err != nil {
return writeDiags ( c . Ui , nil , hcl . Diagnostics {
& hcl . Diagnostic {
Severity : hcl . DiagError ,
Summary : "Failed to delete plugin" ,
Detail : fmt . Sprintf ( "The plugin %q failed to be deleted with the following error: %q" , args [ 0 ], err ),
},
})
}
c . Ui . Say ( args [ 0 ])
return 0
}
2022-02-10 22:53:50 +01:00
opts := plugingetter . ListInstallationsOptions {
2024-01-15 14:41:25 -05:00
PluginDirectory : c . Meta . CoreConfig . Components . PluginConfig . PluginDirectory ,
2022-02-10 22:53:50 +01:00
BinaryInstallationOptions : plugingetter . BinaryInstallationOptions {
OS : runtime . GOOS ,
ARCH : runtime . GOARCH ,
Checksummers : [] plugingetter . Checksummer {
{ Type : "sha256" , Hash : sha256 . New ()},
},
},
}
2022-03-04 04:50:26 -05:00
if runtime . GOOS == "windows" && opts . Ext == "" {
opts . BinaryInstallationOptions . Ext = ".exe"
}
2024-05-09 17:11:15 -04:00
plugin , err := addrs . ParsePluginSourceString ( args [ 0 ])
if err != nil {
c . Ui . Errorf ( "Invalid source string %q: %s" , args [ 0 ], err )
2022-02-10 22:53:50 +01:00
return 1
}
// a plugin requirement that matches them all
pluginRequirement := plugingetter . Requirement {
Identifier : plugin ,
}
if len ( args ) > 1 {
constraints , err := version . NewConstraint ( args [ 1 ])
if err != nil {
c . Ui . Error ( err . Error ())
return 1
}
pluginRequirement . VersionConstraints = constraints
}
installations , err := pluginRequirement . ListInstallations ( opts )
if err != nil {
c . Ui . Error ( err . Error ())
return 1
}
for _ , installation := range installations {
2024-03-12 16:16:54 -04:00
err := deletePluginBinary ( installation . BinaryPath )
if err != nil {
c . Ui . Error ( fmt . Sprintf ( "Failed to remove plugin %q: %q" , installation . BinaryPath , err ))
continue
2023-10-25 16:06:45 -04:00
}
2022-02-10 22:53:50 +01:00
c . Ui . Message ( installation . BinaryPath )
}
2023-10-26 16:46:15 -04:00
if len ( installations ) == 0 {
errMsg := fmt . Sprintf ( "No installed plugin found matching the plugin constraints %s" , args [ 0 ])
if len ( args ) == 2 {
errMsg = fmt . Sprintf ( "%s %s" , errMsg , args [ 1 ])
}
c . Ui . Error ( errMsg )
return 1
}
2022-02-10 22:53:50 +01:00
return 0
}