Skip to content

Commit

Permalink
Run pre-uninstall script when a platform or tool is uninstalled
Browse files Browse the repository at this point in the history
  • Loading branch information
MatteoPologruto committed Sep 11, 2023
1 parent a3f8510 commit 7cdef86
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 48 deletions.
63 changes: 46 additions & 17 deletions arduino/cores/packagemanager/install_uninstall.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ func (pme *Explorer) DownloadAndInstallPlatformUpgrades(
downloadCB rpc.DownloadProgressCB,
taskCB rpc.TaskProgressCB,
skipPostInstall bool,
skipPreUninstall bool,
) (*cores.PlatformRelease, error) {
if platformRef.PlatformVersion != nil {
return nil, &arduino.InvalidArgumentError{Message: tr("Upgrade doesn't accept parameters with version")}
Expand All @@ -62,7 +63,7 @@ func (pme *Explorer) DownloadAndInstallPlatformUpgrades(
if err != nil {
return nil, &arduino.PlatformNotFoundError{Platform: platformRef.String()}
}
if err := pme.DownloadAndInstallPlatformAndTools(platformRelease, tools, downloadCB, taskCB, skipPostInstall); err != nil {
if err := pme.DownloadAndInstallPlatformAndTools(platformRelease, tools, downloadCB, taskCB, skipPostInstall, skipPreUninstall); err != nil {
return nil, err
}

Expand All @@ -75,7 +76,7 @@ func (pme *Explorer) DownloadAndInstallPlatformUpgrades(
func (pme *Explorer) DownloadAndInstallPlatformAndTools(
platformRelease *cores.PlatformRelease, requiredTools []*cores.ToolRelease,
downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB,
skipPostInstall bool) error {
skipPostInstall bool, skipPreUninstall bool) error {
log := pme.log.WithField("platform", platformRelease)

// Prerequisite checks before install
Expand Down Expand Up @@ -142,15 +143,15 @@ func (pme *Explorer) DownloadAndInstallPlatformAndTools(

// If upgrading remove previous release
if installed != nil {
uninstallErr := pme.UninstallPlatform(installed, taskCB)
uninstallErr := pme.UninstallPlatform(installed, taskCB, skipPreUninstall)

// In case of error try to rollback
if uninstallErr != nil {
log.WithError(uninstallErr).Error("Error upgrading platform.")
taskCB(&rpc.TaskProgress{Message: tr("Error upgrading platform: %s", uninstallErr)})

// Rollback
if err := pme.UninstallPlatform(platformRelease, taskCB); err != nil {
if err := pme.UninstallPlatform(platformRelease, taskCB, skipPreUninstall); err != nil {
log.WithError(err).Error("Error rolling-back changes.")
taskCB(&rpc.TaskProgress{Message: tr("Error rolling-back changes: %s", err)})
}
Expand All @@ -162,7 +163,7 @@ func (pme *Explorer) DownloadAndInstallPlatformAndTools(
for _, tool := range installedTools {
taskCB(&rpc.TaskProgress{Name: tr("Uninstalling %s, tool is no more required", tool)})
if !pme.IsToolRequired(tool) {
pme.UninstallTool(tool, taskCB)
pme.UninstallTool(tool, taskCB, skipPreUninstall)
}
}

Expand All @@ -175,7 +176,7 @@ func (pme *Explorer) DownloadAndInstallPlatformAndTools(
if !platformRelease.IsInstalled() {
return errors.New(tr("platform not installed"))
}
stdout, stderr, err := pme.RunPostInstallScript(platformRelease.InstallDir)
stdout, stderr, err := pme.RunPreOrPostScript(platformRelease.InstallDir, "post_install")
skipEmptyMessageTaskProgressCB(taskCB)(&rpc.TaskProgress{Message: string(stdout), Completed: true})
skipEmptyMessageTaskProgressCB(taskCB)(&rpc.TaskProgress{Message: string(stderr), Completed: true})
if err != nil {
Expand Down Expand Up @@ -229,16 +230,16 @@ func (pme *Explorer) cacheInstalledJSON(platformRelease *cores.PlatformRelease)
return nil
}

// RunPostInstallScript runs the post_install.sh (or post_install.bat) script for the
// specified platformRelease or toolRelease.
func (pme *Explorer) RunPostInstallScript(installDir *paths.Path) ([]byte, []byte, error) {
postInstallFilename := "post_install.sh"
// RunPreOrPostScript runs either the post_install.sh (or post_install.bat) or the pre_uninstall.sh (or pre_uninstall.bat)
// script for the specified platformRelease or toolRelease.
func (pme *Explorer) RunPreOrPostScript(installDir *paths.Path, prefix string) ([]byte, []byte, error) {
scriptFilename := prefix + ".sh"
if runtime.GOOS == "windows" {
postInstallFilename = "post_install.bat"
scriptFilename = prefix + ".bat"
}
postInstall := installDir.Join(postInstallFilename)
if postInstall.Exist() && postInstall.IsNotDir() {
cmd, err := executils.NewProcessFromPath(pme.GetEnvVarsForSpawnedProcess(), postInstall)
script := installDir.Join(scriptFilename)
if script.Exist() && script.IsNotDir() {
cmd, err := executils.NewProcessFromPath(pme.GetEnvVarsForSpawnedProcess(), script)
if err != nil {
return []byte{}, []byte{}, err
}
Expand Down Expand Up @@ -270,7 +271,7 @@ func (pme *Explorer) IsManagedPlatformRelease(platformRelease *cores.PlatformRel
}

// UninstallPlatform remove a PlatformRelease.
func (pme *Explorer) UninstallPlatform(platformRelease *cores.PlatformRelease, taskCB rpc.TaskProgressCB) error {
func (pme *Explorer) UninstallPlatform(platformRelease *cores.PlatformRelease, taskCB rpc.TaskProgressCB, skipPreUninstall bool) error {
log := pme.log.WithField("platform", platformRelease)

log.Info("Uninstalling platform")
Expand All @@ -289,6 +290,20 @@ func (pme *Explorer) UninstallPlatform(platformRelease *cores.PlatformRelease, t
return &arduino.FailedUninstallError{Message: err.Error()}
}

if !skipPreUninstall {
log.Info("Running pre_uninstall script")
taskCB(&rpc.TaskProgress{Message: tr("Running pre_uninstall script.")})
stdout, stderr, err := pme.RunPreOrPostScript(platformRelease.InstallDir, "pre_uninstall")
skipEmptyMessageTaskProgressCB(taskCB)(&rpc.TaskProgress{Message: string(stdout), Completed: true})
skipEmptyMessageTaskProgressCB(taskCB)(&rpc.TaskProgress{Message: string(stderr), Completed: true})
if err != nil {
taskCB(&rpc.TaskProgress{Message: tr("WARNING cannot run pre_uninstall script: %s", err), Completed: true})
}
} else {
log.Info("Skipping pre_uninstall script.")
taskCB(&rpc.TaskProgress{Message: tr("Skipping pre_uninstall script.")})
}

if err := platformRelease.InstallDir.RemoveAll(); err != nil {
err = fmt.Errorf(tr("removing platform files: %s"), err)
log.WithError(err).Error("Error uninstalling")
Expand Down Expand Up @@ -339,7 +354,7 @@ func (pme *Explorer) InstallTool(toolRelease *cores.ToolRelease, taskCB rpc.Task
if !skipPostInstall {
log.Info("Running tool post_install script")
taskCB(&rpc.TaskProgress{Message: tr("Configuring tool.")})
stdout, stderr, err := pme.RunPostInstallScript(toolRelease.InstallDir)
stdout, stderr, err := pme.RunPreOrPostScript(toolRelease.InstallDir, "post_install")
skipEmptyMessageTaskProgressCB(taskCB)(&rpc.TaskProgress{Message: string(stdout)})
skipEmptyMessageTaskProgressCB(taskCB)(&rpc.TaskProgress{Message: string(stderr)})
if err != nil {
Expand Down Expand Up @@ -373,7 +388,7 @@ func (pme *Explorer) IsManagedToolRelease(toolRelease *cores.ToolRelease) bool {
}

// UninstallTool remove a ToolRelease.
func (pme *Explorer) UninstallTool(toolRelease *cores.ToolRelease, taskCB rpc.TaskProgressCB) error {
func (pme *Explorer) UninstallTool(toolRelease *cores.ToolRelease, taskCB rpc.TaskProgressCB, skipPreUninstall bool) error {
log := pme.log.WithField("Tool", toolRelease)
log.Info("Uninstalling tool")

Expand All @@ -388,6 +403,20 @@ func (pme *Explorer) UninstallTool(toolRelease *cores.ToolRelease, taskCB rpc.Ta
return err
}

if !skipPreUninstall {
log.Info("Running pre_uninstall script")
taskCB(&rpc.TaskProgress{Message: tr("Running pre_uninstall script.")})
stdout, stderr, err := pme.RunPreOrPostScript(toolRelease.InstallDir, "pre_uninstall")
skipEmptyMessageTaskProgressCB(taskCB)(&rpc.TaskProgress{Message: string(stdout), Completed: true})
skipEmptyMessageTaskProgressCB(taskCB)(&rpc.TaskProgress{Message: string(stderr), Completed: true})
if err != nil {
taskCB(&rpc.TaskProgress{Message: tr("WARNING cannot run pre_uninstall script: %s", err), Completed: true})
}
} else {
log.Info("Skipping pre_uninstall script.")
taskCB(&rpc.TaskProgress{Message: tr("Skipping pre_uninstall script.")})
}

if err := toolRelease.InstallDir.RemoveAll(); err != nil {
err = &arduino.FailedUninstallError{Message: err.Error()}
log.WithError(err).Error("Error uninstalling")
Expand Down
2 changes: 1 addition & 1 deletion arduino/cores/packagemanager/package_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -949,7 +949,7 @@ func TestRunPostInstall(t *testing.T) {
require.NoError(t, err)
err = os.Chmod(scriptPath.String(), 0777)
require.NoError(t, err)
stdout, stderr, err := pme.RunPostInstallScript(dir)
stdout, stderr, err := pme.RunPreOrPostScript(dir, "post_install")
require.NoError(t, err)

// `HasPrefix` because windows seem to add a trailing space at the end
Expand Down
2 changes: 1 addition & 1 deletion commands/core/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func PlatformInstall(ctx context.Context, req *rpc.PlatformInstallRequest, downl
}
}

if err := pme.DownloadAndInstallPlatformAndTools(platformRelease, tools, downloadCB, taskCB, req.GetSkipPostInstall()); err != nil {
if err := pme.DownloadAndInstallPlatformAndTools(platformRelease, tools, downloadCB, taskCB, req.GetSkipPostInstall(), req.GetSkipPreUninstall()); err != nil {
return err
}

Expand Down
4 changes: 2 additions & 2 deletions commands/core/uninstall.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,14 @@ func platformUninstall(ctx context.Context, req *rpc.PlatformUninstallRequest, t
return &arduino.NotFoundError{Message: tr("Can't find dependencies for platform %s", ref), Cause: err}
}

if err := pme.UninstallPlatform(platform, taskCB); err != nil {
if err := pme.UninstallPlatform(platform, taskCB, req.GetSkipPreUninstall()); err != nil {
return err
}

for _, tool := range tools {
if !pme.IsToolRequired(tool) {
taskCB(&rpc.TaskProgress{Name: tr("Uninstalling %s, tool is no more required", tool)})
pme.UninstallTool(tool, taskCB)
pme.UninstallTool(tool, taskCB, req.GetSkipPreUninstall())
}
}

Expand Down
3 changes: 2 additions & 1 deletion commands/core/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package core

import (
"context"

"github.com/arduino/arduino-cli/arduino/cores"

"github.com/arduino/arduino-cli/arduino"
Expand All @@ -39,7 +40,7 @@ func PlatformUpgrade(ctx context.Context, req *rpc.PlatformUpgradeRequest, downl
Package: req.PlatformPackage,
PlatformArchitecture: req.Architecture,
}
platform, err := pme.DownloadAndInstallPlatformUpgrades(ref, downloadCB, taskCB, req.GetSkipPostInstall())
platform, err := pme.DownloadAndInstallPlatformUpgrades(ref, downloadCB, taskCB, req.GetSkipPostInstall(), req.GetSkipPreUninstall())
if err != nil {
return platform, err
}
Expand Down
21 changes: 11 additions & 10 deletions internal/cli/core/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import (

func initInstallCommand() *cobra.Command {
var noOverwrite bool
var postInstallFlags arguments.PrePostScriptsFlags
var scriptFlags arguments.PrePostScriptsFlags
installCommand := &cobra.Command{
Use: fmt.Sprintf("install %s:%s[@%s]...", tr("PACKAGER"), tr("ARCH"), tr("VERSION")),
Short: tr("Installs one or more cores and corresponding tool dependencies."),
Expand All @@ -45,18 +45,18 @@ func initInstallCommand() *cobra.Command {
arguments.CheckFlagsConflicts(cmd, "run-post-install", "skip-post-install")
},
Run: func(cmd *cobra.Command, args []string) {
runInstallCommand(args, postInstallFlags, noOverwrite)
runInstallCommand(args, scriptFlags, noOverwrite)
},
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return arguments.GetInstallableCores(), cobra.ShellCompDirectiveDefault
},
}
postInstallFlags.AddToCommand(installCommand)
scriptFlags.AddToCommand(installCommand)
installCommand.Flags().BoolVar(&noOverwrite, "no-overwrite", false, tr("Do not overwrite already installed platforms."))
return installCommand
}

func runInstallCommand(args []string, postInstallFlags arguments.PrePostScriptsFlags, noOverwrite bool) {
func runInstallCommand(args []string, scriptFlags arguments.PrePostScriptsFlags, noOverwrite bool) {
inst := instance.CreateAndInit()
logrus.Info("Executing `arduino-cli core install`")

Expand All @@ -67,12 +67,13 @@ func runInstallCommand(args []string, postInstallFlags arguments.PrePostScriptsF

for _, platformRef := range platformsRefs {
platformInstallRequest := &rpc.PlatformInstallRequest{
Instance: inst,
PlatformPackage: platformRef.PackageName,
Architecture: platformRef.Architecture,
Version: platformRef.Version,
SkipPostInstall: postInstallFlags.DetectSkipPostInstallValue(),
NoOverwrite: noOverwrite,
Instance: inst,
PlatformPackage: platformRef.PackageName,
Architecture: platformRef.Architecture,
Version: platformRef.Version,
SkipPostInstall: scriptFlags.DetectSkipPostInstallValue(),
NoOverwrite: noOverwrite,
SkipPreUninstall: scriptFlags.DetectSkipPreUninstallValue(),
}
_, err := core.PlatformInstall(context.Background(), platformInstallRequest, feedback.ProgressBar(), feedback.TaskProgress())
if err != nil {
Expand Down
15 changes: 10 additions & 5 deletions internal/cli/core/uninstall.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,25 @@ import (
)

func initUninstallCommand() *cobra.Command {
var preUninstallFlags arguments.PrePostScriptsFlags
uninstallCommand := &cobra.Command{
Use: fmt.Sprintf("uninstall %s:%s ...", tr("PACKAGER"), tr("ARCH")),
Short: tr("Uninstalls one or more cores and corresponding tool dependencies if no longer used."),
Long: tr("Uninstalls one or more cores and corresponding tool dependencies if no longer used."),
Example: " " + os.Args[0] + " core uninstall arduino:samd\n",
Args: cobra.MinimumNArgs(1),
Run: runUninstallCommand,
Run: func(cmd *cobra.Command, args []string) {
runUninstallCommand(args, preUninstallFlags)
},
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return arguments.GetUninstallableCores(), cobra.ShellCompDirectiveDefault
},
}
preUninstallFlags.AddToCommand(uninstallCommand)
return uninstallCommand
}

func runUninstallCommand(cmd *cobra.Command, args []string) {
func runUninstallCommand(args []string, preUninstallFlags arguments.PrePostScriptsFlags) {
inst := instance.CreateAndInit()
logrus.Info("Executing `arduino-cli core uninstall`")

Expand All @@ -60,9 +64,10 @@ func runUninstallCommand(cmd *cobra.Command, args []string) {
}
for _, platformRef := range platformsRefs {
_, err := core.PlatformUninstall(context.Background(), &rpc.PlatformUninstallRequest{
Instance: inst,
PlatformPackage: platformRef.PackageName,
Architecture: platformRef.Architecture,
Instance: inst,
PlatformPackage: platformRef.PackageName,
Architecture: platformRef.Architecture,
SkipPreUninstall: preUninstallFlags.DetectSkipPreUninstallValue(),
}, feedback.NewTaskProgressCB())
if err != nil {
feedback.Fatal(tr("Error during uninstall: %v", err), feedback.ErrGeneric)
Expand Down
17 changes: 9 additions & 8 deletions internal/cli/core/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,21 @@ func initUpgradeCommand() *cobra.Command {
" # " + tr("upgrade arduino:samd to the latest version") + "\n" +
" " + os.Args[0] + " core upgrade arduino:samd",
Run: func(cmd *cobra.Command, args []string) {
runUpgradeCommand(args, postInstallFlags.DetectSkipPostInstallValue())
runUpgradeCommand(args, postInstallFlags.DetectSkipPostInstallValue(), postInstallFlags.DetectSkipPreUninstallValue())
},
}
postInstallFlags.AddToCommand(upgradeCommand)
return upgradeCommand
}

func runUpgradeCommand(args []string, skipPostInstall bool) {
func runUpgradeCommand(args []string, skipPostInstall bool, skipPreUninstall bool) {
inst := instance.CreateAndInit()
logrus.Info("Executing `arduino-cli core upgrade`")
Upgrade(inst, args, skipPostInstall)
Upgrade(inst, args, skipPostInstall, skipPreUninstall)
}

// Upgrade upgrades one or all installed platforms to the latest version.
func Upgrade(inst *rpc.Instance, args []string, skipPostInstall bool) {
func Upgrade(inst *rpc.Instance, args []string, skipPostInstall bool, skipPreUninstall bool) {
// if no platform was passed, upgrade allthethings
if len(args) == 0 {
targets, err := core.PlatformList(&rpc.PlatformListRequest{
Expand Down Expand Up @@ -102,10 +102,11 @@ func Upgrade(inst *rpc.Instance, args []string, skipPostInstall bool) {
}

r := &rpc.PlatformUpgradeRequest{
Instance: inst,
PlatformPackage: platformRef.PackageName,
Architecture: platformRef.Architecture,
SkipPostInstall: skipPostInstall,
Instance: inst,
PlatformPackage: platformRef.PackageName,
Architecture: platformRef.Architecture,
SkipPostInstall: skipPostInstall,
SkipPreUninstall: skipPreUninstall,
}
response, err := core.PlatformUpgrade(context.Background(), r, feedback.ProgressBar(), feedback.TaskProgress())
warningMissingIndex(response)
Expand Down
6 changes: 3 additions & 3 deletions internal/cli/upgrade/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,16 @@ func NewCommand() *cobra.Command {
Example: " " + os.Args[0] + " upgrade",
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
runUpgradeCommand(postInstallFlags.DetectSkipPostInstallValue())
runUpgradeCommand(postInstallFlags.DetectSkipPostInstallValue(), postInstallFlags.DetectSkipPreUninstallValue())
},
}
postInstallFlags.AddToCommand(upgradeCommand)
return upgradeCommand
}

func runUpgradeCommand(skipPostInstall bool) {
func runUpgradeCommand(skipPostInstall bool, skipPreUninstall bool) {
inst := instance.CreateAndInit()
logrus.Info("Executing `arduino-cli upgrade`")
lib.Upgrade(inst, []string{})
core.Upgrade(inst, []string{}, skipPostInstall)
core.Upgrade(inst, []string{}, skipPostInstall, skipPreUninstall)
}

0 comments on commit 7cdef86

Please sign in to comment.