Skip to content

Commit 034dd0f

Browse files
authored
Run post_install when needed (#893)
* Refactored executils.Command * Added IsTrusted flag in PlatformReleases This is required for post_install script processing. * Run post_install.sh script on trusted platforms * Small fix on post-install precondition * Use absolute path for running post_install script * Added --skip-post-install flags. Refactored PostInstall function. * Run post-install scripts by default only if the CLI is interactive This should prevent CI-based runs to block waiting for user interaction. * Added post_install run messages
1 parent 319dede commit 034dd0f

26 files changed

+312
-155
lines changed

Diff for: arduino/cores/cores.go

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ type PlatformRelease struct {
4848
Menus *properties.Map `json:"-"`
4949
InstallDir *paths.Path `json:"-"`
5050
IsIDEBundled bool `json:"-"`
51+
IsTrusted bool `json:"-"`
5152
}
5253

5354
// BoardManifest contains information about a board. These metadata are usually

Diff for: arduino/cores/packageindex/index.go

+23-5
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,16 @@ import (
2121

2222
"github.com/arduino/arduino-cli/arduino/cores"
2323
"github.com/arduino/arduino-cli/arduino/resources"
24+
"github.com/arduino/arduino-cli/arduino/security"
2425
"github.com/arduino/go-paths-helper"
26+
"github.com/sirupsen/logrus"
2527
semver "go.bug.st/relaxed-semver"
2628
)
2729

2830
// Index represents Cores and Tools struct as seen from package_index.json file.
2931
type Index struct {
30-
Packages []*indexPackage `json:"packages"`
32+
Packages []*indexPackage `json:"packages"`
33+
IsTrusted bool
3134
}
3235

3336
// indexPackage represents a single entry from package_index.json file.
@@ -98,11 +101,11 @@ type indexHelp struct {
98101
// with the existing contents of the cores.Packages passed as parameter.
99102
func (index Index) MergeIntoPackages(outPackages cores.Packages) {
100103
for _, inPackage := range index.Packages {
101-
inPackage.extractPackageIn(outPackages)
104+
inPackage.extractPackageIn(outPackages, index.IsTrusted)
102105
}
103106
}
104107

105-
func (inPackage indexPackage) extractPackageIn(outPackages cores.Packages) {
108+
func (inPackage indexPackage) extractPackageIn(outPackages cores.Packages, trusted bool) {
106109
outPackage := outPackages.GetOrCreatePackage(inPackage.Name)
107110
outPackage.Maintainer = inPackage.Maintainer
108111
outPackage.WebsiteURL = inPackage.WebsiteURL
@@ -115,11 +118,11 @@ func (inPackage indexPackage) extractPackageIn(outPackages cores.Packages) {
115118
}
116119

117120
for _, inPlatform := range inPackage.Platforms {
118-
inPlatform.extractPlatformIn(outPackage)
121+
inPlatform.extractPlatformIn(outPackage, trusted)
119122
}
120123
}
121124

122-
func (inPlatformRelease indexPlatformRelease) extractPlatformIn(outPackage *cores.Package) error {
125+
func (inPlatformRelease indexPlatformRelease) extractPlatformIn(outPackage *cores.Package, trusted bool) error {
123126
outPlatform := outPackage.GetOrCreatePlatform(inPlatformRelease.Architecture)
124127
// FIXME: shall we use the Name and Category of the latest release? or maybe move Name and Category in PlatformRelease?
125128
outPlatform.Name = inPlatformRelease.Name
@@ -133,6 +136,7 @@ func (inPlatformRelease indexPlatformRelease) extractPlatformIn(outPackage *core
133136
if err != nil {
134137
return fmt.Errorf("creating release: %s", err)
135138
}
139+
outPlatformRelease.IsTrusted = trusted
136140
outPlatformRelease.Resource = &resources.DownloadResource{
137141
ArchiveFileName: inPlatformRelease.ArchiveFileName,
138142
Checksum: inPlatformRelease.Checksum,
@@ -213,5 +217,19 @@ func LoadIndex(jsonIndexFile *paths.Path) (*Index, error) {
213217
return nil, err
214218
}
215219

220+
jsonSignatureFile := jsonIndexFile.Parent().Join(jsonIndexFile.Base() + ".sig")
221+
trusted, _, err := security.VerifyArduinoDetachedSignature(jsonIndexFile, jsonSignatureFile)
222+
if err != nil {
223+
logrus.
224+
WithField("index", jsonIndexFile).
225+
WithField("signatureFile", jsonSignatureFile).
226+
WithError(err).Infof("Checking signature")
227+
} else {
228+
logrus.
229+
WithField("index", jsonIndexFile).
230+
WithField("signatureFile", jsonSignatureFile).
231+
WithField("trusted", trusted).Infof("Checking signature")
232+
index.IsTrusted = trusted
233+
}
216234
return &index, nil
217235
}

Diff for: arduino/cores/packagemanager/install_uninstall.go

+38-1
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@ package packagemanager
1717

1818
import (
1919
"fmt"
20+
"runtime"
2021

2122
"github.com/arduino/arduino-cli/arduino/cores"
23+
"github.com/arduino/arduino-cli/executils"
24+
"github.com/pkg/errors"
2225
)
2326

2427
// InstallPlatform installs a specific release of a platform.
@@ -28,7 +31,41 @@ func (pm *PackageManager) InstallPlatform(platformRelease *cores.PlatformRelease
2831
"hardware",
2932
platformRelease.Platform.Architecture,
3033
platformRelease.Version.String())
31-
return platformRelease.Resource.Install(pm.DownloadDir, pm.TempDir, destDir)
34+
if err := platformRelease.Resource.Install(pm.DownloadDir, pm.TempDir, destDir); err != nil {
35+
return errors.Errorf("installing platform %s: %s", platformRelease, err)
36+
}
37+
if d, err := destDir.Abs(); err == nil {
38+
platformRelease.InstallDir = d
39+
} else {
40+
return err
41+
}
42+
return nil
43+
}
44+
45+
// RunPostInstallScript runs the post_install.sh (or post_install.bat) script for the
46+
// specified platformRelease.
47+
func (pm *PackageManager) RunPostInstallScript(platformRelease *cores.PlatformRelease) error {
48+
if !platformRelease.IsInstalled() {
49+
return errors.New("platform not installed")
50+
}
51+
postInstallFilename := "post_install.sh"
52+
if runtime.GOOS == "windows" {
53+
postInstallFilename = "post_install.bat"
54+
}
55+
postInstall := platformRelease.InstallDir.Join(postInstallFilename)
56+
if postInstall.Exist() && postInstall.IsNotDir() {
57+
cmd, err := executils.Command(postInstall.String())
58+
if err != nil {
59+
return err
60+
}
61+
cmd.Dir = platformRelease.InstallDir.String()
62+
cmd.Stdout = nil
63+
cmd.Stderr = nil
64+
if err := cmd.Run(); err != nil {
65+
return err
66+
}
67+
}
68+
return nil
3269
}
3370

3471
// IsManagedPlatformRelease returns true if the PlatforRelease is managed by the PackageManager

Diff for: arduino/security/signatures.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,11 @@ import (
2323
"golang.org/x/crypto/openpgp"
2424
)
2525

26-
// VerifyArduinoDetachedSignature that give signaturePath GPG signature match the given targetPath file
27-
// ant the is an authentic signature from Arduino.
26+
// VerifyArduinoDetachedSignature checks that the detached GPG signature (in the
27+
// signaturePath file) matches the given targetPath file and is an authentic
28+
// signature from the bundled trusted keychain. If any of the above conditions
29+
// fails this function returns false. The PGP entity in the trusted keychain that
30+
// produced the signature is returned too.
2831
func VerifyArduinoDetachedSignature(targetPath *paths.Path, signaturePath *paths.Path) (bool, *openpgp.Entity, error) {
2932
keysBox, err := rice.FindBox("keys")
3033
if err != nil {

Diff for: cli/core/install.go

+35
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/arduino/arduino-cli/cli/instance"
2626
"github.com/arduino/arduino-cli/cli/output"
2727
"github.com/arduino/arduino-cli/commands/core"
28+
"github.com/arduino/arduino-cli/configuration"
2829
rpc "github.com/arduino/arduino-cli/rpc/commands"
2930
"github.com/sirupsen/logrus"
3031
"github.com/spf13/cobra"
@@ -42,9 +43,42 @@ func initInstallCommand() *cobra.Command {
4243
Args: cobra.MinimumNArgs(1),
4344
Run: runInstallCommand,
4445
}
46+
addPostInstallFlagsToCommand(installCommand)
4547
return installCommand
4648
}
4749

50+
var postInstallFlags struct {
51+
runPostInstall bool
52+
skipPostInstall bool
53+
}
54+
55+
func addPostInstallFlagsToCommand(cmd *cobra.Command) {
56+
cmd.Flags().BoolVar(&postInstallFlags.runPostInstall, "run-post-install", false, "Force run of post-install scripts (if the CLI is not running interactively).")
57+
cmd.Flags().BoolVar(&postInstallFlags.skipPostInstall, "skip-post-install", false, "Force skip of post-install scripts (if the CLI is running interactively).")
58+
}
59+
60+
func detectSkipPostInstallValue() bool {
61+
if postInstallFlags.runPostInstall && postInstallFlags.skipPostInstall {
62+
feedback.Errorf("The flags --run-post-install and --skip-post-install can't be both set at the same time.")
63+
os.Exit(errorcodes.ErrBadArgument)
64+
}
65+
if postInstallFlags.runPostInstall {
66+
logrus.Info("Will run post-install by user request")
67+
return false
68+
}
69+
if postInstallFlags.skipPostInstall {
70+
logrus.Info("Will skip post-install by user request")
71+
return true
72+
}
73+
74+
if !configuration.IsInteractive {
75+
logrus.Info("Not running from console, will skip post-install by default")
76+
return true
77+
}
78+
logrus.Info("Running from console, will run post-install by default")
79+
return false
80+
}
81+
4882
func runInstallCommand(cmd *cobra.Command, args []string) {
4983
inst, err := instance.CreateInstance()
5084
if err != nil {
@@ -66,6 +100,7 @@ func runInstallCommand(cmd *cobra.Command, args []string) {
66100
PlatformPackage: platformRef.PackageName,
67101
Architecture: platformRef.Architecture,
68102
Version: platformRef.Version,
103+
SkipPostInstall: detectSkipPostInstallValue(),
69104
}
70105
_, err := core.PlatformInstall(context.Background(), platformInstallReq, output.ProgressBar(), output.TaskProgress())
71106
if err != nil {

Diff for: cli/core/upgrade.go

+2
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ func initUpgradeCommand() *cobra.Command {
4242
" " + os.Args[0] + " core upgrade arduino:samd",
4343
Run: runUpgradeCommand,
4444
}
45+
addPostInstallFlagsToCommand(upgradeCommand)
4546
return upgradeCommand
4647
}
4748

@@ -91,6 +92,7 @@ func runUpgradeCommand(cmd *cobra.Command, args []string) {
9192
Instance: inst,
9293
PlatformPackage: platformRef.PackageName,
9394
Architecture: platformRef.Architecture,
95+
SkipPostInstall: detectSkipPostInstallValue(),
9496
}
9597

9698
_, err := core.PlatformUpgrade(context.Background(), r, output.ProgressBar(), output.TaskProgress())

Diff for: commands/bundled_tools_serial_discovery.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,7 @@ func ListBoards(pm *packagemanager.PackageManager) ([]*BoardPort, error) {
131131
}
132132

133133
// build the command to be executed
134-
args := []string{t.InstallDir.Join("serial-discovery").String()}
135-
cmd, err := executils.Command(args)
134+
cmd, err := executils.Command(t.InstallDir.Join("serial-discovery").String())
136135
if err != nil {
137136
return nil, errors.Wrap(err, "creating discovery process")
138137
}

Diff for: commands/core/install.go

+16-3
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ package core
1717

1818
import (
1919
"context"
20-
"errors"
2120
"fmt"
2221

2322
"github.com/arduino/arduino-cli/arduino/cores"
2423
"github.com/arduino/arduino-cli/arduino/cores/packagemanager"
2524
"github.com/arduino/arduino-cli/commands"
2625
rpc "github.com/arduino/arduino-cli/rpc/commands"
26+
"github.com/pkg/errors"
2727
)
2828

2929
// PlatformInstall FIXMEDOC
@@ -49,7 +49,7 @@ func PlatformInstall(ctx context.Context, req *rpc.PlatformInstallReq,
4949
return nil, fmt.Errorf("finding platform dependencies: %s", err)
5050
}
5151

52-
err = installPlatform(pm, platform, tools, downloadCB, taskCB)
52+
err = installPlatform(pm, platform, tools, downloadCB, taskCB, req.GetSkipPostInstall())
5353
if err != nil {
5454
return nil, err
5555
}
@@ -64,7 +64,8 @@ func PlatformInstall(ctx context.Context, req *rpc.PlatformInstallReq,
6464

6565
func installPlatform(pm *packagemanager.PackageManager,
6666
platformRelease *cores.PlatformRelease, requiredTools []*cores.ToolRelease,
67-
downloadCB commands.DownloadProgressCB, taskCB commands.TaskProgressCB) error {
67+
downloadCB commands.DownloadProgressCB, taskCB commands.TaskProgressCB,
68+
skipPostInstall bool) error {
6869
log := pm.Log.WithField("platform", platformRelease)
6970

7071
// Prerequisite checks before install
@@ -138,6 +139,18 @@ func installPlatform(pm *packagemanager.PackageManager,
138139
}
139140
}
140141

142+
// Perform post install
143+
if !skipPostInstall && platformRelease.IsTrusted {
144+
log.Info("Running post_install script")
145+
taskCB(&rpc.TaskProgress{Message: "Configuring platform (post_install run)"})
146+
if err := pm.RunPostInstallScript(platformRelease); err != nil {
147+
return errors.Errorf("running post install: %s", err)
148+
}
149+
} else if skipPostInstall {
150+
log.Info("Skipping platform configuration (post_install run).")
151+
taskCB(&rpc.TaskProgress{Message: "Skipping platform configuration (post_install run)"})
152+
}
153+
141154
log.Info("Platform installed")
142155
taskCB(&rpc.TaskProgress{Message: platformRelease.String() + " installed", Completed: true})
143156
return nil

Diff for: commands/core/upgrade.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ func PlatformUpgrade(ctx context.Context, req *rpc.PlatformUpgradeReq,
4545
Package: req.PlatformPackage,
4646
PlatformArchitecture: req.Architecture,
4747
}
48-
if err := upgradePlatform(pm, ref, downloadCB, taskCB); err != nil {
48+
if err := upgradePlatform(pm, ref, downloadCB, taskCB, req.GetSkipPostInstall()); err != nil {
4949
return nil, err
5050
}
5151

@@ -57,7 +57,8 @@ func PlatformUpgrade(ctx context.Context, req *rpc.PlatformUpgradeReq,
5757
}
5858

5959
func upgradePlatform(pm *packagemanager.PackageManager, platformRef *packagemanager.PlatformReference,
60-
downloadCB commands.DownloadProgressCB, taskCB commands.TaskProgressCB) error {
60+
downloadCB commands.DownloadProgressCB, taskCB commands.TaskProgressCB,
61+
skipPostInstall bool) error {
6162
if platformRef.PlatformVersion != nil {
6263
return fmt.Errorf("upgrade doesn't accept parameters with version")
6364
}
@@ -84,7 +85,7 @@ func upgradePlatform(pm *packagemanager.PackageManager, platformRef *packagemana
8485
if err != nil {
8586
return fmt.Errorf("platform %s is not installed", platformRef)
8687
}
87-
err = installPlatform(pm, platform, tools, downloadCB, taskCB)
88+
err = installPlatform(pm, platform, tools, downloadCB, taskCB, skipPostInstall)
8889
if err != nil {
8990
return err
9091
}

Diff for: commands/debug/debug.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ func Debug(ctx context.Context, req *dbg.DebugConfigReq, inStream io.Reader, out
6464
}
6565
entry.Debug("Executing debugger")
6666

67-
cmd, err := executils.Command(commandLine)
67+
cmd, err := executils.Command(commandLine...)
6868
if err != nil {
6969
return nil, errors.Wrap(err, "Cannot execute debug tool")
7070
}

Diff for: commands/upload/upload.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ func runTool(recipeID string, props *properties.Map, outStream, errStream io.Wri
372372
if verbose {
373373
outStream.Write([]byte(fmt.Sprintln(cmdLine)))
374374
}
375-
cmd, err := executils.Command(cmdArgs)
375+
cmd, err := executils.Command(cmdArgs...)
376376
if err != nil {
377377
return fmt.Errorf("cannot execute upload tool: %s", err)
378378
}

Diff for: configuration/term.go

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// This file is part of arduino-cli.
2+
//
3+
// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This software is released under the GNU General Public License version 3,
6+
// which covers the main part of arduino-cli.
7+
// The terms of this license can be found at:
8+
// https://www.gnu.org/licenses/gpl-3.0.en.html
9+
//
10+
// You can be released from the requirements of the above licenses by purchasing
11+
// a commercial license. Buying such a license is mandatory if you want to
12+
// modify or otherwise use the software for commercial activities involving the
13+
// Arduino software without disclosing the source code of your own applications.
14+
// To purchase a commercial license, send an email to [email protected].
15+
16+
package configuration
17+
18+
import (
19+
"os"
20+
21+
"github.com/mattn/go-isatty"
22+
)
23+
24+
// IsInteractive is set to true if the CLI is interactive (it can receive inputs from terminal/console)
25+
var IsInteractive = isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd())
26+
27+
// HasConsole is set to true if the CLI outputs to a terminal/console
28+
var HasConsole = isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd())

Diff for: executils/executils.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ func TellCommandNotToSpawnShell(cmd *exec.Cmd) {
7575
// Command creates a command with the provided command line arguments.
7676
// The first argument is the path to the executable, the remainder are the
7777
// arguments to the command.
78-
func Command(args []string) (*exec.Cmd, error) {
78+
func Command(args ...string) (*exec.Cmd, error) {
7979
if args == nil || len(args) == 0 {
8080
return nil, fmt.Errorf("no executable specified")
8181
}

Diff for: go.mod

+2-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ require (
2929
github.com/leonelquinteros/gotext v1.4.0
3030
github.com/marcinbor85/gohex v0.0.0-20200531163658-baab2527a9a2
3131
github.com/mattn/go-colorable v0.1.2
32-
github.com/mattn/go-runewidth v0.0.2 // indirect
32+
github.com/mattn/go-isatty v0.0.8
33+
github.com/mattn/go-runewidth v0.0.9 // indirect
3334
github.com/miekg/dns v1.0.5 // indirect
3435
github.com/oleksandr/bonjour v0.0.0-20160508152359-5dcf00d8b228 // indirect
3536
github.com/pkg/errors v0.9.1

0 commit comments

Comments
 (0)