Skip to content

Run post_install when needed #893

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Aug 20, 2020
1 change: 1 addition & 0 deletions arduino/cores/cores.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ type PlatformRelease struct {
Menus *properties.Map `json:"-"`
InstallDir *paths.Path `json:"-"`
IsIDEBundled bool `json:"-"`
IsTrusted bool `json:"-"`
}

// BoardManifest contains information about a board. These metadata are usually
Expand Down
28 changes: 23 additions & 5 deletions arduino/cores/packageindex/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,16 @@ import (

"github.com/arduino/arduino-cli/arduino/cores"
"github.com/arduino/arduino-cli/arduino/resources"
"github.com/arduino/arduino-cli/arduino/security"
"github.com/arduino/go-paths-helper"
"github.com/sirupsen/logrus"
semver "go.bug.st/relaxed-semver"
)

// Index represents Cores and Tools struct as seen from package_index.json file.
type Index struct {
Packages []*indexPackage `json:"packages"`
Packages []*indexPackage `json:"packages"`
IsTrusted bool
}

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

func (inPackage indexPackage) extractPackageIn(outPackages cores.Packages) {
func (inPackage indexPackage) extractPackageIn(outPackages cores.Packages, trusted bool) {
outPackage := outPackages.GetOrCreatePackage(inPackage.Name)
outPackage.Maintainer = inPackage.Maintainer
outPackage.WebsiteURL = inPackage.WebsiteURL
Expand All @@ -115,11 +118,11 @@ func (inPackage indexPackage) extractPackageIn(outPackages cores.Packages) {
}

for _, inPlatform := range inPackage.Platforms {
inPlatform.extractPlatformIn(outPackage)
inPlatform.extractPlatformIn(outPackage, trusted)
}
}

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

jsonSignatureFile := jsonIndexFile.Parent().Join(jsonIndexFile.Base() + ".sig")
trusted, _, err := security.VerifyArduinoDetachedSignature(jsonIndexFile, jsonSignatureFile)
if err != nil {
logrus.
WithField("index", jsonIndexFile).
WithField("signatureFile", jsonSignatureFile).
WithError(err).Infof("Checking signature")
} else {
logrus.
WithField("index", jsonIndexFile).
WithField("signatureFile", jsonSignatureFile).
WithField("trusted", trusted).Infof("Checking signature")
index.IsTrusted = trusted
}
return &index, nil
}
39 changes: 38 additions & 1 deletion arduino/cores/packagemanager/install_uninstall.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ package packagemanager

import (
"fmt"
"runtime"

"github.com/arduino/arduino-cli/arduino/cores"
"github.com/arduino/arduino-cli/executils"
"github.com/pkg/errors"
)

// InstallPlatform installs a specific release of a platform.
Expand All @@ -28,7 +31,41 @@ func (pm *PackageManager) InstallPlatform(platformRelease *cores.PlatformRelease
"hardware",
platformRelease.Platform.Architecture,
platformRelease.Version.String())
return platformRelease.Resource.Install(pm.DownloadDir, pm.TempDir, destDir)
if err := platformRelease.Resource.Install(pm.DownloadDir, pm.TempDir, destDir); err != nil {
return errors.Errorf("installing platform %s: %s", platformRelease, err)
}
if d, err := destDir.Abs(); err == nil {
platformRelease.InstallDir = d
} else {
return err
}
return nil
}

// RunPostInstallScript runs the post_install.sh (or post_install.bat) script for the
// specified platformRelease.
func (pm *PackageManager) RunPostInstallScript(platformRelease *cores.PlatformRelease) error {
if !platformRelease.IsInstalled() {
return errors.New("platform not installed")
}
postInstallFilename := "post_install.sh"
if runtime.GOOS == "windows" {
postInstallFilename = "post_install.bat"
}
postInstall := platformRelease.InstallDir.Join(postInstallFilename)
if postInstall.Exist() && postInstall.IsNotDir() {
cmd, err := executils.Command(postInstall.String())
if err != nil {
return err
}
cmd.Dir = platformRelease.InstallDir.String()
cmd.Stdout = nil
cmd.Stderr = nil
if err := cmd.Run(); err != nil {
return err
}
}
return nil
}

// IsManagedPlatformRelease returns true if the PlatforRelease is managed by the PackageManager
Expand Down
7 changes: 5 additions & 2 deletions arduino/security/signatures.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@ import (
"golang.org/x/crypto/openpgp"
)

// VerifyArduinoDetachedSignature that give signaturePath GPG signature match the given targetPath file
// ant the is an authentic signature from Arduino.
// VerifyArduinoDetachedSignature checks that the detached GPG signature (in the
// signaturePath file) matches the given targetPath file and is an authentic
// signature from the bundled trusted keychain. If any of the above conditions
// fails this function returns false. The PGP entity in the trusted keychain that
// produced the signature is returned too.
func VerifyArduinoDetachedSignature(targetPath *paths.Path, signaturePath *paths.Path) (bool, *openpgp.Entity, error) {
keysBox, err := rice.FindBox("keys")
if err != nil {
Expand Down
35 changes: 35 additions & 0 deletions cli/core/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/arduino/arduino-cli/cli/instance"
"github.com/arduino/arduino-cli/cli/output"
"github.com/arduino/arduino-cli/commands/core"
"github.com/arduino/arduino-cli/configuration"
rpc "github.com/arduino/arduino-cli/rpc/commands"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
Expand All @@ -42,9 +43,42 @@ func initInstallCommand() *cobra.Command {
Args: cobra.MinimumNArgs(1),
Run: runInstallCommand,
}
addPostInstallFlagsToCommand(installCommand)
return installCommand
}

var postInstallFlags struct {
runPostInstall bool
skipPostInstall bool
}

func addPostInstallFlagsToCommand(cmd *cobra.Command) {
cmd.Flags().BoolVar(&postInstallFlags.runPostInstall, "run-post-install", false, "Force run of post-install scripts (if the CLI is not running interactively).")
cmd.Flags().BoolVar(&postInstallFlags.skipPostInstall, "skip-post-install", false, "Force skip of post-install scripts (if the CLI is running interactively).")
}

func detectSkipPostInstallValue() bool {
if postInstallFlags.runPostInstall && postInstallFlags.skipPostInstall {
feedback.Errorf("The flags --run-post-install and --skip-post-install can't be both set at the same time.")
os.Exit(errorcodes.ErrBadArgument)
}
if postInstallFlags.runPostInstall {
logrus.Info("Will run post-install by user request")
return false
}
if postInstallFlags.skipPostInstall {
logrus.Info("Will skip post-install by user request")
return true
}

if !configuration.IsInteractive {
logrus.Info("Not running from console, will skip post-install by default")
return true
}
logrus.Info("Running from console, will run post-install by default")
return false
}

func runInstallCommand(cmd *cobra.Command, args []string) {
inst, err := instance.CreateInstance()
if err != nil {
Expand All @@ -66,6 +100,7 @@ func runInstallCommand(cmd *cobra.Command, args []string) {
PlatformPackage: platformRef.PackageName,
Architecture: platformRef.Architecture,
Version: platformRef.Version,
SkipPostInstall: detectSkipPostInstallValue(),
}
_, err := core.PlatformInstall(context.Background(), platformInstallReq, output.ProgressBar(), output.TaskProgress())
if err != nil {
Expand Down
2 changes: 2 additions & 0 deletions cli/core/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ func initUpgradeCommand() *cobra.Command {
" " + os.Args[0] + " core upgrade arduino:samd",
Run: runUpgradeCommand,
}
addPostInstallFlagsToCommand(upgradeCommand)
return upgradeCommand
}

Expand Down Expand Up @@ -91,6 +92,7 @@ func runUpgradeCommand(cmd *cobra.Command, args []string) {
Instance: inst,
PlatformPackage: platformRef.PackageName,
Architecture: platformRef.Architecture,
SkipPostInstall: detectSkipPostInstallValue(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@silvanocerza could you please check if the --run-post-install and --skip-post-install flags need to be ported also in arduino-cli update and upgrade commands?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure! I'll check it out.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked at it and think it might be necessary to port them on the upgrade command, it don't think it should be necessary for update since it just downloads the index files.

}

_, err := core.PlatformUpgrade(context.Background(), r, output.ProgressBar(), output.TaskProgress())
Expand Down
3 changes: 1 addition & 2 deletions commands/bundled_tools_serial_discovery.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,7 @@ func ListBoards(pm *packagemanager.PackageManager) ([]*BoardPort, error) {
}

// build the command to be executed
args := []string{t.InstallDir.Join("serial-discovery").String()}
cmd, err := executils.Command(args)
cmd, err := executils.Command(t.InstallDir.Join("serial-discovery").String())
if err != nil {
return nil, errors.Wrap(err, "creating discovery process")
}
Expand Down
19 changes: 16 additions & 3 deletions commands/core/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ package core

import (
"context"
"errors"
"fmt"

"github.com/arduino/arduino-cli/arduino/cores"
"github.com/arduino/arduino-cli/arduino/cores/packagemanager"
"github.com/arduino/arduino-cli/commands"
rpc "github.com/arduino/arduino-cli/rpc/commands"
"github.com/pkg/errors"
)

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

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

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

// Prerequisite checks before install
Expand Down Expand Up @@ -138,6 +139,18 @@ func installPlatform(pm *packagemanager.PackageManager,
}
}

// Perform post install
if !skipPostInstall && platformRelease.IsTrusted {
log.Info("Running post_install script")
taskCB(&rpc.TaskProgress{Message: "Configuring platform (post_install run)"})
if err := pm.RunPostInstallScript(platformRelease); err != nil {
return errors.Errorf("running post install: %s", err)
}
} else if skipPostInstall {
log.Info("Skipping platform configuration (post_install run).")
taskCB(&rpc.TaskProgress{Message: "Skipping platform configuration (post_install run)"})
}

log.Info("Platform installed")
taskCB(&rpc.TaskProgress{Message: platformRelease.String() + " installed", Completed: true})
return nil
Expand Down
7 changes: 4 additions & 3 deletions commands/core/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func PlatformUpgrade(ctx context.Context, req *rpc.PlatformUpgradeReq,
Package: req.PlatformPackage,
PlatformArchitecture: req.Architecture,
}
if err := upgradePlatform(pm, ref, downloadCB, taskCB); err != nil {
if err := upgradePlatform(pm, ref, downloadCB, taskCB, req.GetSkipPostInstall()); err != nil {
return nil, err
}

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

func upgradePlatform(pm *packagemanager.PackageManager, platformRef *packagemanager.PlatformReference,
downloadCB commands.DownloadProgressCB, taskCB commands.TaskProgressCB) error {
downloadCB commands.DownloadProgressCB, taskCB commands.TaskProgressCB,
skipPostInstall bool) error {
if platformRef.PlatformVersion != nil {
return fmt.Errorf("upgrade doesn't accept parameters with version")
}
Expand All @@ -84,7 +85,7 @@ func upgradePlatform(pm *packagemanager.PackageManager, platformRef *packagemana
if err != nil {
return fmt.Errorf("platform %s is not installed", platformRef)
}
err = installPlatform(pm, platform, tools, downloadCB, taskCB)
err = installPlatform(pm, platform, tools, downloadCB, taskCB, skipPostInstall)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion commands/debug/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func Debug(ctx context.Context, req *dbg.DebugConfigReq, inStream io.Reader, out
}
entry.Debug("Executing debugger")

cmd, err := executils.Command(commandLine)
cmd, err := executils.Command(commandLine...)
if err != nil {
return nil, errors.Wrap(err, "Cannot execute debug tool")
}
Expand Down
2 changes: 1 addition & 1 deletion commands/upload/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ func runTool(recipeID string, props *properties.Map, outStream, errStream io.Wri
if verbose {
outStream.Write([]byte(fmt.Sprintln(cmdLine)))
}
cmd, err := executils.Command(cmdArgs)
cmd, err := executils.Command(cmdArgs...)
if err != nil {
return fmt.Errorf("cannot execute upload tool: %s", err)
}
Expand Down
28 changes: 28 additions & 0 deletions configuration/term.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// This file is part of arduino-cli.
//
// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
//
// This software is released under the GNU General Public License version 3,
// which covers the main part of arduino-cli.
// The terms of this license can be found at:
// https://www.gnu.org/licenses/gpl-3.0.en.html
//
// You can be released from the requirements of the above licenses by purchasing
// a commercial license. Buying such a license is mandatory if you want to
// modify or otherwise use the software for commercial activities involving the
// Arduino software without disclosing the source code of your own applications.
// To purchase a commercial license, send an email to [email protected].

package configuration

import (
"os"

"github.com/mattn/go-isatty"
)

// IsInteractive is set to true if the CLI is interactive (it can receive inputs from terminal/console)
var IsInteractive = isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd())

// HasConsole is set to true if the CLI outputs to a terminal/console
var HasConsole = isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd())
2 changes: 1 addition & 1 deletion executils/executils.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func TellCommandNotToSpawnShell(cmd *exec.Cmd) {
// Command creates a command with the provided command line arguments.
// The first argument is the path to the executable, the remainder are the
// arguments to the command.
func Command(args []string) (*exec.Cmd, error) {
func Command(args ...string) (*exec.Cmd, error) {
if args == nil || len(args) == 0 {
return nil, fmt.Errorf("no executable specified")
}
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ require (
github.com/leonelquinteros/gotext v1.4.0
github.com/marcinbor85/gohex v0.0.0-20200531163658-baab2527a9a2
github.com/mattn/go-colorable v0.1.2
github.com/mattn/go-runewidth v0.0.2 // indirect
github.com/mattn/go-isatty v0.0.8
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/miekg/dns v1.0.5 // indirect
github.com/oleksandr/bonjour v0.0.0-20160508152359-5dcf00d8b228 // indirect
github.com/pkg/errors v0.9.1
Expand Down
Loading