Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 29c70df

Browse files
Add support for pre_uninstall scripts (#2311)
* Add skip_pre_uninstall parameter to gRPC platform requests * Add pre-uninstall flags to CLI arguments * Run pre-uninstall script when a platform or tool is uninstalled * Document the changes * Include pre-uninstall script run into the unit test
1 parent f6645a8 commit 29c70df

File tree

13 files changed

+343
-187
lines changed

13 files changed

+343
-187
lines changed

‎arduino/cores/packagemanager/install_uninstall.go‎

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ func (pme *Explorer) DownloadAndInstallPlatformUpgrades(
3838
downloadCB rpc.DownloadProgressCB,
3939
taskCB rpc.TaskProgressCB,
4040
skipPostInstall bool,
41+
skipPreUninstall bool,
4142
) (*cores.PlatformRelease, error) {
4243
if platformRef.PlatformVersion != nil {
4344
return nil, &arduino.InvalidArgumentError{Message: tr("Upgrade doesn't accept parameters with version")}
@@ -62,7 +63,7 @@ func (pme *Explorer) DownloadAndInstallPlatformUpgrades(
6263
if err != nil {
6364
return nil, &arduino.PlatformNotFoundError{Platform: platformRef.String()}
6465
}
65-
if err := pme.DownloadAndInstallPlatformAndTools(platformRelease, tools, downloadCB, taskCB, skipPostInstall); err != nil {
66+
if err := pme.DownloadAndInstallPlatformAndTools(platformRelease, tools, downloadCB, taskCB, skipPostInstall, skipPreUninstall); err != nil {
6667
return nil, err
6768
}
6869

@@ -75,7 +76,7 @@ func (pme *Explorer) DownloadAndInstallPlatformUpgrades(
7576
func (pme *Explorer) DownloadAndInstallPlatformAndTools(
7677
platformRelease *cores.PlatformRelease, requiredTools []*cores.ToolRelease,
7778
downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB,
78-
skipPostInstall bool) error {
79+
skipPostInstall bool, skipPreUninstallbool) error {
7980
log := pme.log.WithField("platform", platformRelease)
8081

8182
// Prerequisite checks before install
@@ -142,15 +143,15 @@ func (pme *Explorer) DownloadAndInstallPlatformAndTools(
142143

143144
// If upgrading remove previous release
144145
if installed != nil {
145-
uninstallErr := pme.UninstallPlatform(installed, taskCB)
146+
uninstallErr := pme.UninstallPlatform(installed, taskCB, skipPreUninstall)
146147

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

152153
// Rollback
153-
if err := pme.UninstallPlatform(platformRelease, taskCB); err != nil {
154+
if err := pme.UninstallPlatform(platformRelease, taskCB, skipPreUninstall); err != nil {
154155
log.WithError(err).Error("Error rolling-back changes.")
155156
taskCB(&rpc.TaskProgress{Message: tr("Error rolling-back changes: %s", err)})
156157
}
@@ -162,7 +163,7 @@ func (pme *Explorer) DownloadAndInstallPlatformAndTools(
162163
for _, tool := range installedTools {
163164
taskCB(&rpc.TaskProgress{Name: tr("Uninstalling %s, tool is no more required", tool)})
164165
if !pme.IsToolRequired(tool) {
165-
pme.UninstallTool(tool, taskCB)
166+
pme.UninstallTool(tool, taskCB, skipPreUninstall)
166167
}
167168
}
168169

@@ -175,7 +176,7 @@ func (pme *Explorer) DownloadAndInstallPlatformAndTools(
175176
if !platformRelease.IsInstalled() {
176177
return errors.New(tr("platform not installed"))
177178
}
178-
stdout, stderr, err := pme.RunPostInstallScript(platformRelease.InstallDir)
179+
stdout, stderr, err := pme.RunPreOrPostScript(platformRelease.InstallDir, "post_install")
179180
skipEmptyMessageTaskProgressCB(taskCB)(&rpc.TaskProgress{Message: string(stdout), Completed: true})
180181
skipEmptyMessageTaskProgressCB(taskCB)(&rpc.TaskProgress{Message: string(stderr), Completed: true})
181182
if err != nil {
@@ -229,16 +230,16 @@ func (pme *Explorer) cacheInstalledJSON(platformRelease *cores.PlatformRelease)
229230
return nil
230231
}
231232

232-
// RunPostInstallScript runs the post_install.sh (or post_install.bat) script for the
233-
// specified platformRelease or toolRelease.
234-
func (pme *Explorer) RunPostInstallScript(installDir *paths.Path) ([]byte, []byte, error) {
235-
postInstallFilename := "post_install.sh"
233+
// RunPreOrPostScript runs either the post_install.sh (or post_install.bat) or the pre_uninstall.sh (or pre_uninstall.bat)
234+
// script for the specified platformRelease or toolRelease.
235+
func (pme *Explorer) RunPreOrPostScript(installDir *paths.Path, prefixstring) ([]byte, []byte, error) {
236+
scriptFilename := prefix+".sh"
236237
if runtime.GOOS == "windows" {
237-
postInstallFilename = "post_install.bat"
238+
scriptFilename = prefix+".bat"
238239
}
239-
postInstall := installDir.Join(postInstallFilename)
240-
if postInstall.Exist() && postInstall.IsNotDir() {
241-
cmd, err := executils.NewProcessFromPath(pme.GetEnvVarsForSpawnedProcess(), postInstall)
240+
script := installDir.Join(scriptFilename)
241+
if script.Exist() && script.IsNotDir() {
242+
cmd, err := executils.NewProcessFromPath(pme.GetEnvVarsForSpawnedProcess(), script)
242243
if err != nil {
243244
return []byte{}, []byte{}, err
244245
}
@@ -270,7 +271,7 @@ func (pme *Explorer) IsManagedPlatformRelease(platformRelease *cores.PlatformRel
270271
}
271272

272273
// UninstallPlatform remove a PlatformRelease.
273-
func (pme *Explorer) UninstallPlatform(platformRelease *cores.PlatformRelease, taskCB rpc.TaskProgressCB) error {
274+
func (pme *Explorer) UninstallPlatform(platformRelease *cores.PlatformRelease, taskCB rpc.TaskProgressCB, skipPreUninstallbool) error {
274275
log := pme.log.WithField("platform", platformRelease)
275276

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

293+
if !skipPreUninstall {
294+
log.Info("Running pre_uninstall script")
295+
taskCB(&rpc.TaskProgress{Message: tr("Running pre_uninstall script.")})
296+
stdout, stderr, err := pme.RunPreOrPostScript(platformRelease.InstallDir, "pre_uninstall")
297+
skipEmptyMessageTaskProgressCB(taskCB)(&rpc.TaskProgress{Message: string(stdout), Completed: true})
298+
skipEmptyMessageTaskProgressCB(taskCB)(&rpc.TaskProgress{Message: string(stderr), Completed: true})
299+
if err != nil {
300+
taskCB(&rpc.TaskProgress{Message: tr("WARNING cannot run pre_uninstall script: %s", err), Completed: true})
301+
}
302+
} else {
303+
log.Info("Skipping pre_uninstall script.")
304+
taskCB(&rpc.TaskProgress{Message: tr("Skipping pre_uninstall script.")})
305+
}
306+
292307
if err := platformRelease.InstallDir.RemoveAll(); err != nil {
293308
err = fmt.Errorf(tr("removing platform files: %s"), err)
294309
log.WithError(err).Error("Error uninstalling")
@@ -339,7 +354,7 @@ func (pme *Explorer) InstallTool(toolRelease *cores.ToolRelease, taskCB rpc.Task
339354
if !skipPostInstall {
340355
log.Info("Running tool post_install script")
341356
taskCB(&rpc.TaskProgress{Message: tr("Configuring tool.")})
342-
stdout, stderr, err := pme.RunPostInstallScript(toolRelease.InstallDir)
357+
stdout, stderr, err := pme.RunPreOrPostScript(toolRelease.InstallDir, "post_install")
343358
skipEmptyMessageTaskProgressCB(taskCB)(&rpc.TaskProgress{Message: string(stdout)})
344359
skipEmptyMessageTaskProgressCB(taskCB)(&rpc.TaskProgress{Message: string(stderr)})
345360
if err != nil {
@@ -373,7 +388,7 @@ func (pme *Explorer) IsManagedToolRelease(toolRelease *cores.ToolRelease) bool {
373388
}
374389

375390
// UninstallTool remove a ToolRelease.
376-
func (pme *Explorer) UninstallTool(toolRelease *cores.ToolRelease, taskCB rpc.TaskProgressCB) error {
391+
func (pme *Explorer) UninstallTool(toolRelease *cores.ToolRelease, taskCB rpc.TaskProgressCB, skipPreUninstallbool) error {
377392
log := pme.log.WithField("Tool", toolRelease)
378393
log.Info("Uninstalling tool")
379394

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

406+
if !skipPreUninstall {
407+
log.Info("Running pre_uninstall script")
408+
taskCB(&rpc.TaskProgress{Message: tr("Running pre_uninstall script.")})
409+
stdout, stderr, err := pme.RunPreOrPostScript(toolRelease.InstallDir, "pre_uninstall")
410+
skipEmptyMessageTaskProgressCB(taskCB)(&rpc.TaskProgress{Message: string(stdout), Completed: true})
411+
skipEmptyMessageTaskProgressCB(taskCB)(&rpc.TaskProgress{Message: string(stderr), Completed: true})
412+
if err != nil {
413+
taskCB(&rpc.TaskProgress{Message: tr("WARNING cannot run pre_uninstall script: %s", err), Completed: true})
414+
}
415+
} else {
416+
log.Info("Skipping pre_uninstall script.")
417+
taskCB(&rpc.TaskProgress{Message: tr("Skipping pre_uninstall script.")})
418+
}
419+
391420
if err := toolRelease.InstallDir.RemoveAll(); err != nil {
392421
err = &arduino.FailedUninstallError{Message: err.Error()}
393422
log.WithError(err).Error("Error uninstalling")

‎arduino/cores/packagemanager/package_manager_test.go‎

Lines changed: 44 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -921,7 +921,7 @@ func TestVariantAndCoreSelection(t *testing.T) {
921921
})
922922
}
923923

924-
func TestRunPostInstall(t *testing.T) {
924+
func TestRunScript(t *testing.T) {
925925
pmb := NewBuilder(nil, nil, nil, nil, "test")
926926
pm := pmb.Build()
927927
pme, release := pm.NewExplorer()
@@ -930,29 +930,49 @@ func TestRunPostInstall(t *testing.T) {
930930
// prepare dummy post install script
931931
dir := paths.New(t.TempDir())
932932

933-
var scriptPath *paths.Path
934-
var err error
935-
if runtime.GOOS == "windows" {
936-
scriptPath = dir.Join("post_install.bat")
937-
938-
err = scriptPath.WriteFile([]byte(
939-
`@echo off
940-
echo sent in stdout
941-
echo sent in stderr 1>&2`))
942-
} else {
943-
scriptPath = dir.Join("post_install.sh")
944-
err = scriptPath.WriteFile([]byte(
945-
`#!/bin/sh
946-
echo "sent in stdout"
947-
echo "sent in stderr" 1>&2`))
933+
type Test struct {
934+
testName string
935+
scriptName string
936+
}
937+
938+
tests := []Test{
939+
{
940+
testName: "PostInstallScript",
941+
scriptName: "post_install",
942+
},
943+
{
944+
testName: "PreUninstallScript",
945+
scriptName: "pre_uninstall",
946+
},
948947
}
949-
require.NoError(t, err)
950-
err = os.Chmod(scriptPath.String(), 0777)
951-
require.NoError(t, err)
952-
stdout, stderr, err := pme.RunPostInstallScript(dir)
953-
require.NoError(t, err)
954948

955-
// `HasPrefix` because windows seem to add a trailing space at the end
956-
require.Equal(t, "sent in stdout", strings.Trim(string(stdout), "\n\r "))
957-
require.Equal(t, "sent in stderr", strings.Trim(string(stderr), "\n\r "))
949+
for _, test := range tests {
950+
t.Run(test.testName, func(t *testing.T) {
951+
var scriptPath *paths.Path
952+
var err error
953+
if runtime.GOOS == "windows" {
954+
scriptPath = dir.Join(test.scriptName + ".bat")
955+
956+
err = scriptPath.WriteFile([]byte(
957+
`@echo off
958+
echo sent in stdout
959+
echo sent in stderr 1>&2`))
960+
} else {
961+
scriptPath = dir.Join(test.scriptName + ".sh")
962+
err = scriptPath.WriteFile([]byte(
963+
`#!/bin/sh
964+
echo "sent in stdout"
965+
echo "sent in stderr" 1>&2`))
966+
}
967+
require.NoError(t, err)
968+
err = os.Chmod(scriptPath.String(), 0777)
969+
require.NoError(t, err)
970+
stdout, stderr, err := pme.RunPreOrPostScript(dir, test.scriptName)
971+
require.NoError(t, err)
972+
973+
// `HasPrefix` because windows seem to add a trailing space at the end
974+
require.Equal(t, "sent in stdout", strings.Trim(string(stdout), "\n\r "))
975+
require.Equal(t, "sent in stderr", strings.Trim(string(stderr), "\n\r "))
976+
})
977+
}
958978
}

‎commands/core/install.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ func PlatformInstall(ctx context.Context, req *rpc.PlatformInstallRequest, downl
6363
}
6464
}
6565

66-
if err := pme.DownloadAndInstallPlatformAndTools(platformRelease, tools, downloadCB, taskCB, req.GetSkipPostInstall()); err != nil {
66+
if err := pme.DownloadAndInstallPlatformAndTools(platformRelease, tools, downloadCB, taskCB, req.GetSkipPostInstall(), req.GetSkipPreUninstall()); err != nil {
6767
return err
6868
}
6969

‎commands/core/uninstall.go‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,14 @@ func platformUninstall(ctx context.Context, req *rpc.PlatformUninstallRequest, t
6464
return &arduino.NotFoundError{Message: tr("Can't find dependencies for platform %s", ref), Cause: err}
6565
}
6666

67-
if err := pme.UninstallPlatform(platform, taskCB); err != nil {
67+
if err := pme.UninstallPlatform(platform, taskCB, req.GetSkipPreUninstall()); err != nil {
6868
return err
6969
}
7070

7171
for _, tool := range tools {
7272
if !pme.IsToolRequired(tool) {
7373
taskCB(&rpc.TaskProgress{Name: tr("Uninstalling %s, tool is no more required", tool)})
74-
pme.UninstallTool(tool, taskCB)
74+
pme.UninstallTool(tool, taskCB, req.GetSkipPreUninstall())
7575
}
7676
}
7777

‎commands/core/upgrade.go‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package core
1717

1818
import (
1919
"context"
20+
2021
"github.com/arduino/arduino-cli/arduino/cores"
2122

2223
"github.com/arduino/arduino-cli/arduino"
@@ -39,7 +40,7 @@ func PlatformUpgrade(ctx context.Context, req *rpc.PlatformUpgradeRequest, downl
3940
Package: req.PlatformPackage,
4041
PlatformArchitecture: req.Architecture,
4142
}
42-
platform, err := pme.DownloadAndInstallPlatformUpgrades(ref, downloadCB, taskCB, req.GetSkipPostInstall())
43+
platform, err := pme.DownloadAndInstallPlatformUpgrades(ref, downloadCB, taskCB, req.GetSkipPostInstall(), req.GetSkipPreUninstall())
4344
if err != nil {
4445
return platform, err
4546
}

‎docs/platform-specification.md‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1559,3 +1559,21 @@ software is in use:
15591559
- **Arduino CLI**: (since 0.12.0) runs the script for any installed platform when Arduino CLI is in "interactive" mode.
15601560
This behavior
15611561
[can be configured](https://arduino.github.io/arduino-cli/latest/commands/arduino-cli_core_install/#options)
1562+
1563+
## Pre-uninstall script
1564+
1565+
Before Boards Manager starts uninstalling a platform, it checks for the presence of a script named:
1566+
1567+
- `pre_uninstall.bat` - when running on Windows
1568+
- `pre_uninstall.sh` - when running on any non-Windows operating system
1569+
1570+
If present, the script is executed.
1571+
1572+
This script may be used to configure the user's system for the removal of drivers, stopping background programs and
1573+
execute any action that should be performed before the platform files are removed.
1574+
1575+
The circumstances under which the pre-uninstall script will run are different depending on which Arduino development
1576+
software is in use:
1577+
1578+
- **Arduino CLI**: runs the script for any installed platform when Arduino CLI is in "interactive" mode. This behavior
1579+
[can be configured](https://arduino.github.io/arduino-cli/latest/commands/arduino-cli_core_install/#options)

‎internal/cli/arguments/post_install.go‎ renamed to ‎internal/cli/arguments/pre_post_script.go‎

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,33 +21,47 @@ import (
2121
"github.com/spf13/cobra"
2222
)
2323

24-
// PostInstallFlags contains flags data used by the core install and the upgrade command
24+
// PrePostScriptsFlags contains flags data used by the core install and the upgrade command
2525
// This is useful so all flags used by commands that need
2626
// this information are consistent with each other.
27-
type PostInstallFlags struct {
28-
runPostInstall bool // force the execution of installation scripts
29-
skipPostInstall bool // skip the execution of installation scripts
27+
type PrePostScriptsFlags struct {
28+
runPostInstall bool // force the execution of installation scripts
29+
skipPostInstall bool // skip the execution of installation scripts
30+
runPreUninstall bool // force the execution of pre uninstall scripts
31+
skipPreUninstall bool // skip the execution of pre uninstall scripts
3032
}
3133

3234
// AddToCommand adds flags that can be used to force running or skipping
3335
// of post installation scripts
34-
func (p *PostInstallFlags) AddToCommand(cmd *cobra.Command) {
36+
func (p *PrePostScriptsFlags) AddToCommand(cmd *cobra.Command) {
3537
cmd.Flags().BoolVar(&p.runPostInstall, "run-post-install", false, tr("Force run of post-install scripts (if the CLI is not running interactively)."))
3638
cmd.Flags().BoolVar(&p.skipPostInstall, "skip-post-install", false, tr("Force skip of post-install scripts (if the CLI is running interactively)."))
39+
cmd.Flags().BoolVar(&p.runPreUninstall, "run-pre-uninstall", false, tr("Force run of pre-uninstall scripts (if the CLI is not running interactively)."))
40+
cmd.Flags().BoolVar(&p.skipPreUninstall, "skip-pre-uninstall", false, tr("Force skip of pre-uninstall scripts (if the CLI is running interactively)."))
3741
}
3842

3943
// GetRunPostInstall returns the run-post-install flag value
40-
func (p *PostInstallFlags) GetRunPostInstall() bool {
44+
func (p *PrePostScriptsFlags) GetRunPostInstall() bool {
4145
return p.runPostInstall
4246
}
4347

4448
// GetSkipPostInstall returns the skip-post-install flag value
45-
func (p *PostInstallFlags) GetSkipPostInstall() bool {
49+
func (p *PrePostScriptsFlags) GetSkipPostInstall() bool {
4650
return p.skipPostInstall
4751
}
4852

53+
// GetRunPreUninstall returns the run-post-install flag value
54+
func (p *PrePostScriptsFlags) GetRunPreUninstall() bool {
55+
return p.runPreUninstall
56+
}
57+
58+
// GetSkipPreUninstall returns the skip-post-install flag value
59+
func (p *PrePostScriptsFlags) GetSkipPreUninstall() bool {
60+
return p.skipPreUninstall
61+
}
62+
4963
// DetectSkipPostInstallValue returns true if a post install script must be run
50-
func (p *PostInstallFlags) DetectSkipPostInstallValue() bool {
64+
func (p *PrePostScriptsFlags) DetectSkipPostInstallValue() bool {
5165
if p.GetRunPostInstall() {
5266
logrus.Info("Will run post-install by user request")
5367
return false
@@ -64,3 +78,22 @@ func (p *PostInstallFlags) DetectSkipPostInstallValue() bool {
6478
logrus.Info("Running from console, will run post-install by default")
6579
return false
6680
}
81+
82+
// DetectSkipPreUninstallValue returns true if a post install script must be run
83+
func (p *PrePostScriptsFlags) DetectSkipPreUninstallValue() bool {
84+
if p.GetRunPreUninstall() {
85+
logrus.Info("Will run pre-uninstall by user request")
86+
return false
87+
}
88+
if p.GetSkipPreUninstall() {
89+
logrus.Info("Will skip pre-uninstall by user request")
90+
return true
91+
}
92+
93+
if !configuration.IsInteractive {
94+
logrus.Info("Not running from console, will skip pre-uninstall by default")
95+
return true
96+
}
97+
logrus.Info("Running from console, will run pre-uninstall by default")
98+
return false
99+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /