diff --git a/arduino/cores/packagemanager/download.go b/arduino/cores/packagemanager/download.go index 90ace62b9b6..6dfda64fbe6 100644 --- a/arduino/cores/packagemanager/download.go +++ b/arduino/cores/packagemanager/download.go @@ -21,6 +21,7 @@ import ( "github.com/arduino/arduino-cli/arduino" "github.com/arduino/arduino-cli/arduino/cores" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" + "github.com/pkg/errors" "go.bug.st/downloader/v2" semver "go.bug.st/relaxed-semver" ) @@ -118,13 +119,20 @@ func (pm *PackageManager) FindPlatformReleaseDependencies(item *PlatformReferenc } // DownloadToolRelease downloads a ToolRelease. If the tool is already downloaded a nil Downloader -// is returned. -func (pm *PackageManager) DownloadToolRelease(tool *cores.ToolRelease, config *downloader.Config, label string, progressCB rpc.DownloadProgressCB) error { +// is returned. Uses the given downloader configuration for download, or the default config if nil. +func (pm *PackageManager) DownloadToolRelease(tool *cores.ToolRelease, config *downloader.Config, progressCB rpc.DownloadProgressCB) error { resource := tool.GetCompatibleFlavour() if resource == nil { - return fmt.Errorf(tr("tool not available for your OS")) + return &arduino.FailedDownloadError{ + Message: tr("Error downloading tool %s", tool), + Cause: errors.New(tr("no versions available for the current OS", tool))} } - return resource.Download(pm.DownloadDir, config, label, progressCB) + if err := resource.Download(pm.DownloadDir, config, tool.String(), progressCB); err != nil { + return &arduino.FailedDownloadError{ + Message: tr("Error downloading tool %s", tool), + Cause: err} + } + return nil } // DownloadPlatformRelease downloads a PlatformRelease. If the platform is already downloaded a diff --git a/arduino/cores/packagemanager/install_uninstall.go b/arduino/cores/packagemanager/install_uninstall.go index 17ed7ca7f98..001c4885700 100644 --- a/arduino/cores/packagemanager/install_uninstall.go +++ b/arduino/cores/packagemanager/install_uninstall.go @@ -20,13 +20,170 @@ import ( "fmt" "runtime" + "github.com/arduino/arduino-cli/arduino" "github.com/arduino/arduino-cli/arduino/cores" "github.com/arduino/arduino-cli/arduino/cores/packageindex" "github.com/arduino/arduino-cli/executils" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/go-paths-helper" "github.com/pkg/errors" ) +// DownloadAndInstallPlatformUpgrades runs a full installation process to upgrade the given platform. +// This method takes care of downloading missing archives, upgrading platforms and tools, and +// removing the previously installed platform/tools that are no longer needed after the upgrade. +func (pm *PackageManager) DownloadAndInstallPlatformUpgrades( + platformRef *PlatformReference, + downloadCB rpc.DownloadProgressCB, + taskCB rpc.TaskProgressCB, + skipPostInstall bool, +) error { + if platformRef.PlatformVersion != nil { + return &arduino.InvalidArgumentError{Message: tr("Upgrade doesn't accept parameters with version")} + } + + // Search the latest version for all specified platforms + platform := pm.FindPlatform(platformRef) + if platform == nil { + return &arduino.PlatformNotFoundError{Platform: platformRef.String()} + } + installed := pm.GetInstalledPlatformRelease(platform) + if installed == nil { + return &arduino.PlatformNotFoundError{Platform: platformRef.String()} + } + latest := platform.GetLatestRelease() + if !latest.Version.GreaterThan(installed.Version) { + return &arduino.PlatformAlreadyAtTheLatestVersionError{} + } + platformRef.PlatformVersion = latest.Version + + platformRelease, tools, err := pm.FindPlatformReleaseDependencies(platformRef) + if err != nil { + return &arduino.PlatformNotFoundError{Platform: platformRef.String()} + } + if err := pm.DownloadAndInstallPlatformAndTools(platformRelease, tools, downloadCB, taskCB, skipPostInstall); err != nil { + return err + } + + return nil +} + +// DownloadAndInstallPlatformAndTools runs a full installation process for the given platform and tools. +// This method takes care of downloading missing archives, installing/upgrading platforms and tools, and +// removing the previously installed platform/tools that are no longer needed after the upgrade. +func (pm *PackageManager) DownloadAndInstallPlatformAndTools( + platformRelease *cores.PlatformRelease, requiredTools []*cores.ToolRelease, + downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB, + skipPostInstall bool) error { + log := pm.log.WithField("platform", platformRelease) + + // Prerequisite checks before install + toolsToInstall := []*cores.ToolRelease{} + for _, tool := range requiredTools { + if tool.IsInstalled() { + log.WithField("tool", tool).Warn("Tool already installed") + taskCB(&rpc.TaskProgress{Name: tr("Tool %s already installed", tool), Completed: true}) + } else { + toolsToInstall = append(toolsToInstall, tool) + } + } + + // Package download + taskCB(&rpc.TaskProgress{Name: tr("Downloading packages")}) + for _, tool := range toolsToInstall { + if err := pm.DownloadToolRelease(tool, nil, downloadCB); err != nil { + return err + } + } + if err := pm.DownloadPlatformRelease(platformRelease, nil, downloadCB); err != nil { + return err + } + taskCB(&rpc.TaskProgress{Completed: true}) + + // Install tools first + for _, tool := range toolsToInstall { + if err := pm.InstallTool(tool, taskCB); err != nil { + return err + } + } + + installed := pm.GetInstalledPlatformRelease(platformRelease.Platform) + installedTools := []*cores.ToolRelease{} + if installed == nil { + // No version of this platform is installed + log.Info("Installing platform") + taskCB(&rpc.TaskProgress{Name: tr("Installing platform %s", platformRelease)}) + } else { + // A platform with a different version is already installed + log.Info("Replacing platform " + installed.String()) + taskCB(&rpc.TaskProgress{Name: tr("Replacing platform %[1]s with %[2]s", installed, platformRelease)}) + platformRef := &PlatformReference{ + Package: platformRelease.Platform.Package.Name, + PlatformArchitecture: platformRelease.Platform.Architecture, + PlatformVersion: installed.Version, + } + + // Get a list of tools used by the currently installed platform version. + // This must be done so tools used by the currently installed version are + // removed if not used also by the newly installed version. + var err error + _, installedTools, err = pm.FindPlatformReleaseDependencies(platformRef) + if err != nil { + return &arduino.NotFoundError{Message: tr("Can't find dependencies for platform %s", platformRef), Cause: err} + } + } + + // Install + if err := pm.InstallPlatform(platformRelease); err != nil { + log.WithError(err).Error("Cannot install platform") + return &arduino.FailedInstallError{Message: tr("Cannot install platform"), Cause: err} + } + + // If upgrading remove previous release + if installed != nil { + uninstallErr := pm.UninstallPlatform(installed, taskCB) + + // 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 := pm.UninstallPlatform(platformRelease, taskCB); err != nil { + log.WithError(err).Error("Error rolling-back changes.") + taskCB(&rpc.TaskProgress{Message: tr("Error rolling-back changes: %s", err)}) + } + + return &arduino.FailedInstallError{Message: tr("Cannot upgrade platform"), Cause: uninstallErr} + } + + // Uninstall unused tools + for _, tool := range installedTools { + taskCB(&rpc.TaskProgress{Name: tr("Uninstalling %s, tool is no more required", tool)}) + if !pm.IsToolRequired(tool) { + pm.UninstallTool(tool, taskCB) + } + } + + } + + // Perform post install + if !skipPostInstall { + log.Info("Running post_install script") + taskCB(&rpc.TaskProgress{Message: tr("Configuring platform.")}) + if err := pm.RunPostInstallScript(platformRelease); err != nil { + taskCB(&rpc.TaskProgress{Message: tr("WARNING cannot configure platform: %s", err)}) + } + } else { + log.Info("Skipping platform configuration.") + taskCB(&rpc.TaskProgress{Message: tr("Skipping platform configuration.")}) + } + + log.Info("Platform installed") + taskCB(&rpc.TaskProgress{Message: tr("Platform %s installed", platformRelease), Completed: true}) + return nil +} + // InstallPlatform installs a specific release of a platform. func (pm *PackageManager) InstallPlatform(platformRelease *cores.PlatformRelease) error { destDir := pm.PackagesDir.Join( @@ -39,7 +196,7 @@ func (pm *PackageManager) InstallPlatform(platformRelease *cores.PlatformRelease // InstallPlatformInDirectory installs a specific release of a platform in a specific directory. func (pm *PackageManager) InstallPlatformInDirectory(platformRelease *cores.PlatformRelease, destDir *paths.Path) error { - if err := platformRelease.Resource.Install(pm.DownloadDir, pm.TempDir, destDir); err != nil { + if err := platformRelease.Resource.Install(pm.DownloadDir, pm.tempDir, destDir); err != nil { return errors.Errorf(tr("installing platform %[1]s: %[2]s"), platformRelease, err) } if d, err := destDir.Abs(); err == nil { @@ -109,25 +266,51 @@ func (pm *PackageManager) IsManagedPlatformRelease(platformRelease *cores.Platfo } // UninstallPlatform remove a PlatformRelease. -func (pm *PackageManager) UninstallPlatform(platformRelease *cores.PlatformRelease) error { +func (pm *PackageManager) UninstallPlatform(platformRelease *cores.PlatformRelease, taskCB rpc.TaskProgressCB) error { + log := pm.log.WithField("platform", platformRelease) + + log.Info("Uninstalling platform") + taskCB(&rpc.TaskProgress{Name: tr("Uninstalling %s", platformRelease)}) + if platformRelease.InstallDir == nil { - return fmt.Errorf(tr("platform not installed")) + err := fmt.Errorf(tr("platform not installed")) + log.WithError(err).Error("Error uninstalling") + return &arduino.FailedUninstallError{Message: err.Error()} } // Safety measure if !pm.IsManagedPlatformRelease(platformRelease) { - return fmt.Errorf(tr("%s is not managed by package manager"), platformRelease) + err := fmt.Errorf(tr("%s is not managed by package manager"), platformRelease) + log.WithError(err).Error("Error uninstalling") + return &arduino.FailedUninstallError{Message: err.Error()} } if err := platformRelease.InstallDir.RemoveAll(); err != nil { - return fmt.Errorf(tr("removing platform files: %s"), err) + err = fmt.Errorf(tr("removing platform files: %s"), err) + log.WithError(err).Error("Error uninstalling") + return &arduino.FailedUninstallError{Message: err.Error()} } + platformRelease.InstallDir = nil + + log.Info("Platform uninstalled") + taskCB(&rpc.TaskProgress{Message: tr("Platform %s uninstalled", platformRelease), Completed: true}) return nil } // InstallTool installs a specific release of a tool. -func (pm *PackageManager) InstallTool(toolRelease *cores.ToolRelease) error { +func (pm *PackageManager) InstallTool(toolRelease *cores.ToolRelease, taskCB rpc.TaskProgressCB) error { + log := pm.log.WithField("Tool", toolRelease) + + if toolRelease.IsInstalled() { + log.Warn("Tool already installed") + taskCB(&rpc.TaskProgress{Name: tr("Tool %s already installed", toolRelease), Completed: true}) + return nil + } + + log.Info("Installing tool") + taskCB(&rpc.TaskProgress{Name: tr("Installing %s", toolRelease)}) + toolResource := toolRelease.GetCompatibleFlavour() if toolResource == nil { return fmt.Errorf(tr("no compatible version of %s tools found for the current os"), toolRelease.Tool.Name) @@ -137,7 +320,15 @@ func (pm *PackageManager) InstallTool(toolRelease *cores.ToolRelease) error { "tools", toolRelease.Tool.Name, toolRelease.Version.String()) - return toolResource.Install(pm.DownloadDir, pm.TempDir, destDir) + err := toolResource.Install(pm.DownloadDir, pm.tempDir, destDir) + if err != nil { + log.WithError(err).Warn("Cannot install tool") + return &arduino.FailedInstallError{Message: tr("Cannot install tool %s", toolRelease), Cause: err} + } + log.Info("Tool installed") + taskCB(&rpc.TaskProgress{Message: tr("%s installed", toolRelease), Completed: true}) + + return nil } // IsManagedToolRelease returns true if the ToolRelease is managed by the PackageManager @@ -161,20 +352,31 @@ func (pm *PackageManager) IsManagedToolRelease(toolRelease *cores.ToolRelease) b } // UninstallTool remove a ToolRelease. -func (pm *PackageManager) UninstallTool(toolRelease *cores.ToolRelease) error { +func (pm *PackageManager) UninstallTool(toolRelease *cores.ToolRelease, taskCB rpc.TaskProgressCB) error { + log := pm.log.WithField("Tool", toolRelease) + log.Info("Uninstalling tool") + if toolRelease.InstallDir == nil { return fmt.Errorf(tr("tool not installed")) } // Safety measure if !pm.IsManagedToolRelease(toolRelease) { - return fmt.Errorf(tr("tool %s is not managed by package manager"), toolRelease) + err := &arduino.FailedUninstallError{Message: tr("tool %s is not managed by package manager", toolRelease)} + log.WithError(err).Error("Error uninstalling") + return err } if err := toolRelease.InstallDir.RemoveAll(); err != nil { - return fmt.Errorf(tr("removing tool files: %s"), err) + err = &arduino.FailedUninstallError{Message: err.Error()} + log.WithError(err).Error("Error uninstalling") + return err } + toolRelease.InstallDir = nil + + log.Info("Tool uninstalled") + taskCB(&rpc.TaskProgress{Message: tr("Tool %s uninstalled", toolRelease), Completed: true}) return nil } diff --git a/arduino/cores/packagemanager/loader.go b/arduino/cores/packagemanager/loader.go index e517fcfd5af..b48fac48f1b 100644 --- a/arduino/cores/packagemanager/loader.go +++ b/arduino/cores/packagemanager/loader.go @@ -55,7 +55,7 @@ func (pm *PackageManager) LoadHardwareFromDirectories(hardwarePaths paths.PathLi // LoadHardwareFromDirectory read a plaform from the path passed as parameter func (pm *PackageManager) LoadHardwareFromDirectory(path *paths.Path) []error { var merr []error - pm.Log.Infof("Loading hardware from: %s", path) + pm.log.Infof("Loading hardware from: %s", path) if err := path.ToAbs(); err != nil { return append(merr, fmt.Errorf("%s: %w", tr("finding absolute path of %s", path), err)) } @@ -76,9 +76,9 @@ func (pm *PackageManager) LoadHardwareFromDirectory(path *paths.Path) []error { // "Global" platform.txt used to overwrite all installed platforms. // For more info: https://arduino.github.io/arduino-cli/latest/platform-specification/#global-platformtxt if globalPlatformTxt := path.Join("platform.txt"); globalPlatformTxt.Exist() { - pm.Log.Infof("Loading custom platform properties: %s", globalPlatformTxt) + pm.log.Infof("Loading custom platform properties: %s", globalPlatformTxt) if p, err := properties.LoadFromPath(globalPlatformTxt); err != nil { - pm.Log.WithError(err).Errorf("Error loading properties.") + pm.log.WithError(err).Errorf("Error loading properties.") } else { pm.CustomGlobalProperties.Merge(p) } @@ -89,7 +89,7 @@ func (pm *PackageManager) LoadHardwareFromDirectory(path *paths.Path) []error { // Skip tools, they're not packages and don't contain Platforms if packager == "tools" { - pm.Log.Infof("Excluding directory: %s", packagerPath) + pm.log.Infof("Excluding directory: %s", packagerPath) continue } @@ -127,7 +127,7 @@ func (pm *PackageManager) LoadHardwareFromDirectory(path *paths.Path) []error { // - PACKAGER/tools/TOOL-NAME/TOOL-VERSION/... (ex: arduino/tools/bossac/1.7.0/...) toolsSubdirPath := packagerPath.Join("tools") if toolsSubdirPath.IsDir() { - pm.Log.Infof("Checking existence of 'tools' path: %s", toolsSubdirPath) + pm.log.Infof("Checking existence of 'tools' path: %s", toolsSubdirPath) merr = append(merr, pm.LoadToolsFromPackageDir(targetPackage, toolsSubdirPath)...) } // If the Package does not contain Platforms or Tools we remove it since does not contain anything valuable @@ -143,7 +143,7 @@ func (pm *PackageManager) LoadHardwareFromDirectory(path *paths.Path) []error { // to the targetPackage object passed as parameter. // A list of gRPC Status error is returned for each Platform failed to load. func (pm *PackageManager) loadPlatforms(targetPackage *cores.Package, packageDir *paths.Path) []error { - pm.Log.Infof("Loading package %s from: %s", targetPackage.Name, packageDir) + pm.log.Infof("Loading package %s from: %s", targetPackage.Name, packageDir) var merr []error @@ -220,11 +220,11 @@ func (pm *PackageManager) loadPlatform(targetPackage *cores.Package, architectur tmp := cores.NewPackages() index.MergeIntoPackages(tmp) if tmpPackage := tmp.GetOrCreatePackage(targetPackage.Name); tmpPackage == nil { - pm.Log.Warnf("Can't determine bundle platform version for %s", targetPackage.Name) + pm.log.Warnf("Can't determine bundle platform version for %s", targetPackage.Name) } else if tmpPlatform := tmpPackage.GetOrCreatePlatform(architecture); tmpPlatform == nil { - pm.Log.Warnf("Can't determine bundle platform version for %s:%s", targetPackage.Name, architecture) + pm.log.Warnf("Can't determine bundle platform version for %s:%s", targetPackage.Name, architecture) } else if tmpPlatformRelease := tmpPlatform.GetLatestRelease(); tmpPlatformRelease == nil { - pm.Log.Warnf("Can't determine bundle platform version for %s:%s, no valid release found", targetPackage.Name, architecture) + pm.log.Warnf("Can't determine bundle platform version for %s:%s, no valid release found", targetPackage.Name, architecture) } else { version = tmpPlatformRelease.Version } @@ -239,12 +239,12 @@ func (pm *PackageManager) loadPlatform(targetPackage *cores.Package, architectur release := platform.GetOrCreateRelease(version) release.IsIDEBundled = isIDEBundled if isIDEBundled { - pm.Log.Infof("Package is built-in") + pm.log.Infof("Package is built-in") } if err := pm.loadPlatformRelease(release, platformPath); err != nil { return fmt.Errorf("%s: %w", tr("loading platform release %s", release), err) } - pm.Log.WithField("platform", release).Infof("Loaded platform") + pm.log.WithField("platform", release).Infof("Loaded platform") } else { // case: ARCHITECTURE/VERSION/boards.txt @@ -272,7 +272,7 @@ func (pm *PackageManager) loadPlatform(targetPackage *cores.Package, architectur if err := pm.loadPlatformRelease(release, versionDir); err != nil { return fmt.Errorf("%s: %w", tr("loading platform release %s", release), err) } - pm.Log.WithField("platform", release).Infof("Loaded platform") + pm.log.WithField("platform", release).Infof("Loaded platform") } } @@ -357,7 +357,7 @@ func (pm *PackageManager) loadPlatformRelease(platform *cores.PlatformRelease, p if len(split) != 2 { return fmt.Errorf(tr("invalid pluggable monitor reference: %s"), ref) } - pm.Log.WithField("protocol", protocol).WithField("tool", ref).Info("Adding monitor tool") + pm.log.WithField("protocol", protocol).WithField("tool", ref).Info("Adding monitor tool") platform.Monitors[protocol] = &cores.MonitorDependency{ Packager: split[0], Name: split[1], @@ -367,7 +367,7 @@ func (pm *PackageManager) loadPlatformRelease(platform *cores.PlatformRelease, p // Support for pluggable monitors in debugging/development environments platform.MonitorsDevRecipes = map[string]string{} for protocol, recipe := range platform.Properties.SubTree("pluggable_monitor.pattern").AsMap() { - pm.Log.WithField("protocol", protocol).WithField("recipe", recipe).Info("Adding monitor recipe") + pm.log.WithField("protocol", protocol).WithField("recipe", recipe).Info("Adding monitor recipe") platform.MonitorsDevRecipes[protocol] = recipe } @@ -592,7 +592,7 @@ func convertUploadToolsToPluggableDiscovery(props *properties.Map) { // LoadToolsFromPackageDir loads a set of tools from the given toolsPath. The tools will be loaded // in the given *Package. func (pm *PackageManager) LoadToolsFromPackageDir(targetPackage *cores.Package, toolsPath *paths.Path) []error { - pm.Log.Infof("Loading tools from dir: %s", toolsPath) + pm.log.Infof("Loading tools from dir: %s", toolsPath) var merr []error @@ -637,7 +637,7 @@ func (pm *PackageManager) loadToolReleaseFromDirectory(tool *cores.Tool, version } else { toolRelease := tool.GetOrCreateRelease(version) toolRelease.InstallDir = absToolReleasePath - pm.Log.WithField("tool", toolRelease).Infof("Loaded tool") + pm.log.WithField("tool", toolRelease).Infof("Loaded tool") return nil } } @@ -655,7 +655,7 @@ func (pm *PackageManager) LoadToolsFromBundleDirectories(dirs paths.PathList) [] // LoadToolsFromBundleDirectory FIXMEDOC func (pm *PackageManager) LoadToolsFromBundleDirectory(toolsPath *paths.Path) error { - pm.Log.Infof("Loading tools from bundle dir: %s", toolsPath) + pm.log.Infof("Loading tools from bundle dir: %s", toolsPath) // We scan toolsPath content to find a "builtin_tools_versions.txt", if such file exists // then the all the tools are available in the same directory, mixed together, and their @@ -689,7 +689,7 @@ func (pm *PackageManager) LoadToolsFromBundleDirectory(toolsPath *paths.Path) er if builtinToolsVersionsTxtPath != "" { // If builtin_tools_versions.txt is found create tools based on the info // contained in that file - pm.Log.Infof("Found builtin_tools_versions.txt") + pm.log.Infof("Found builtin_tools_versions.txt") toolPath, err := paths.New(builtinToolsVersionsTxtPath).Parent().Abs() if err != nil { return fmt.Errorf(tr("getting parent dir of %[1]s: %[2]s"), builtinToolsVersionsTxtPath, err) @@ -708,7 +708,7 @@ func (pm *PackageManager) LoadToolsFromBundleDirectory(toolsPath *paths.Path) er version := semver.ParseRelaxed(toolVersion) release := tool.GetOrCreateRelease(version) release.InstallDir = toolPath - pm.Log.WithField("tool", release).Infof("Loaded tool") + pm.log.WithField("tool", release).Infof("Loaded tool") } } } else { diff --git a/arduino/cores/packagemanager/package_manager.go b/arduino/cores/packagemanager/package_manager.go index dd98284b17f..401c2407dfb 100644 --- a/arduino/cores/packagemanager/package_manager.go +++ b/arduino/cores/packagemanager/package_manager.go @@ -38,12 +38,12 @@ import ( // The manager also keeps track of the status of the Packages (their Platform Releases, actually) // installed in the system. type PackageManager struct { - Log logrus.FieldLogger + log logrus.FieldLogger Packages cores.Packages IndexDir *paths.Path PackagesDir *paths.Path DownloadDir *paths.Path - TempDir *paths.Path + tempDir *paths.Path CustomGlobalProperties *properties.Map profile *sketch.Profile discoveryManager *discoverymanager.DiscoveryManager @@ -55,12 +55,12 @@ var tr = i18n.Tr // NewPackageManager returns a new instance of the PackageManager func NewPackageManager(indexDir, packagesDir, downloadDir, tempDir *paths.Path, userAgent string) *PackageManager { return &PackageManager{ - Log: logrus.StandardLogger(), + log: logrus.StandardLogger(), Packages: cores.NewPackages(), IndexDir: indexDir, PackagesDir: packagesDir, DownloadDir: downloadDir, - TempDir: tempDir, + tempDir: tempDir, CustomGlobalProperties: properties.NewMap(), discoveryManager: discoverymanager.New(), userAgent: userAgent, @@ -386,7 +386,7 @@ func (pm *PackageManager) GetInstalledPlatformRelease(platform *cores.Platform) } debug := func(msg string, pl *cores.PlatformRelease) { - pm.Log.WithField("bundle", pl.IsIDEBundled). + pm.log.WithField("bundle", pl.IsIDEBundled). WithField("version", pl.Version). WithField("managed", pm.IsManagedPlatformRelease(pl)). Debugf("%s: %s", msg, pl) @@ -465,7 +465,7 @@ func (pm *PackageManager) InstalledBoards() []*cores.Board { // FindToolsRequiredFromPlatformRelease returns a list of ToolReleases needed by the specified PlatformRelease. // If a ToolRelease is not found return an error func (pm *PackageManager) FindToolsRequiredFromPlatformRelease(platform *cores.PlatformRelease) ([]*cores.ToolRelease, error) { - pm.Log.Infof("Searching tools required for platform %s", platform) + pm.log.Infof("Searching tools required for platform %s", platform) // maps "PACKAGER:TOOL" => ToolRelease foundTools := map[string]*cores.ToolRelease{} @@ -483,7 +483,7 @@ func (pm *PackageManager) FindToolsRequiredFromPlatformRelease(platform *cores.P requiredTools := []*cores.ToolRelease{} platform.ToolDependencies.Sort() for _, toolDep := range platform.ToolDependencies { - pm.Log.WithField("tool", toolDep).Infof("Required tool") + pm.log.WithField("tool", toolDep).Infof("Required tool") tool := pm.FindToolDependency(toolDep) if tool == nil { return nil, fmt.Errorf(tr("tool release not found: %s"), toolDep) @@ -494,7 +494,7 @@ func (pm *PackageManager) FindToolsRequiredFromPlatformRelease(platform *cores.P platform.DiscoveryDependencies.Sort() for _, discoveryDep := range platform.DiscoveryDependencies { - pm.Log.WithField("discovery", discoveryDep).Infof("Required discovery") + pm.log.WithField("discovery", discoveryDep).Infof("Required discovery") tool := pm.FindDiscoveryDependency(discoveryDep) if tool == nil { return nil, fmt.Errorf(tr("discovery release not found: %s"), discoveryDep) @@ -505,7 +505,7 @@ func (pm *PackageManager) FindToolsRequiredFromPlatformRelease(platform *cores.P platform.MonitorDependencies.Sort() for _, monitorDep := range platform.MonitorDependencies { - pm.Log.WithField("monitor", monitorDep).Infof("Required monitor") + pm.log.WithField("monitor", monitorDep).Infof("Required monitor") tool := pm.FindMonitorDependency(monitorDep) if tool == nil { return nil, fmt.Errorf(tr("monitor release not found: %s"), monitorDep) @@ -537,7 +537,7 @@ func (pm *PackageManager) GetTool(toolID string) *cores.Tool { // FindToolsRequiredForBoard FIXMEDOC func (pm *PackageManager) FindToolsRequiredForBoard(board *cores.Board) ([]*cores.ToolRelease, error) { - pm.Log.Infof("Searching tools required for board %s", board) + pm.log.Infof("Searching tools required for board %s", board) // core := board.Properties["build.core"] platform := board.PlatformRelease @@ -560,7 +560,7 @@ func (pm *PackageManager) FindToolsRequiredForBoard(board *cores.Board) ([]*core requiredTools := []*cores.ToolRelease{} platform.ToolDependencies.Sort() for _, toolDep := range platform.ToolDependencies { - pm.Log.WithField("tool", toolDep).Infof("Required tool") + pm.log.WithField("tool", toolDep).Infof("Required tool") tool := pm.FindToolDependency(toolDep) if tool == nil { return nil, fmt.Errorf(tr("tool release not found: %s"), toolDep) diff --git a/arduino/cores/packagemanager/profiles.go b/arduino/cores/packagemanager/profiles.go index abe74a36860..a0be13ab946 100644 --- a/arduino/cores/packagemanager/profiles.go +++ b/arduino/cores/packagemanager/profiles.go @@ -86,8 +86,8 @@ func (pm *PackageManager) loadProfilePlatform(platformRef *sketch.ProfilePlatfor func (pm *PackageManager) installMissingProfilePlatform(platformRef *sketch.ProfilePlatformReference, destDir *paths.Path, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) error { // Instantiate a temporary package manager only for platform installation - _ = pm.TempDir.MkdirAll() - tmp, err := paths.MkTempDir(pm.TempDir.String(), "") + _ = pm.tempDir.MkdirAll() + tmp, err := paths.MkTempDir(pm.tempDir.String(), "") if err != nil { return fmt.Errorf("installing missing platform: could not create temp dir %s", err) } diff --git a/cli/outdated/outdated.go b/cli/outdated/outdated.go index 15f91edbae2..7938cc9c91e 100644 --- a/cli/outdated/outdated.go +++ b/cli/outdated/outdated.go @@ -21,7 +21,7 @@ import ( "github.com/arduino/arduino-cli/cli/feedback" "github.com/arduino/arduino-cli/cli/instance" - "github.com/arduino/arduino-cli/commands" + "github.com/arduino/arduino-cli/commands/outdated" "github.com/arduino/arduino-cli/i18n" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/arduino-cli/table" @@ -50,7 +50,7 @@ func runOutdatedCommand(cmd *cobra.Command, args []string) { inst := instance.CreateAndInit() logrus.Info("Executing `arduino-cli outdated`") - outdatedResp, err := commands.Outdated(context.Background(), &rpc.OutdatedRequest{ + outdatedResp, err := outdated.Outdated(context.Background(), &rpc.OutdatedRequest{ Instance: inst, }) if err != nil { diff --git a/cli/update/update.go b/cli/update/update.go index 8ce69ae75d7..b6fed03f1a5 100644 --- a/cli/update/update.go +++ b/cli/update/update.go @@ -24,6 +24,7 @@ import ( "github.com/arduino/arduino-cli/cli/instance" "github.com/arduino/arduino-cli/cli/output" "github.com/arduino/arduino-cli/commands" + "github.com/arduino/arduino-cli/commands/outdated" "github.com/arduino/arduino-cli/i18n" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/arduino-cli/table" @@ -70,7 +71,7 @@ func runUpdateCommand(cmd *cobra.Command, args []string) { feedback.Errorf(tr("Error initializing instance: %v"), err) } - outdatedResp, err := commands.Outdated(context.Background(), &rpc.OutdatedRequest{ + outdatedResp, err := outdated.Outdated(context.Background(), &rpc.OutdatedRequest{ Instance: inst, }) if err != nil { diff --git a/cli/upgrade/upgrade.go b/cli/upgrade/upgrade.go index 5134774dbb4..bbe001ff72e 100644 --- a/cli/upgrade/upgrade.go +++ b/cli/upgrade/upgrade.go @@ -23,7 +23,7 @@ import ( "github.com/arduino/arduino-cli/cli/feedback" "github.com/arduino/arduino-cli/cli/instance" "github.com/arduino/arduino-cli/cli/output" - "github.com/arduino/arduino-cli/commands" + "github.com/arduino/arduino-cli/commands/upgrade" "github.com/arduino/arduino-cli/i18n" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/sirupsen/logrus" @@ -54,7 +54,7 @@ func runUpgradeCommand(cmd *cobra.Command, args []string) { inst := instance.CreateAndInit() logrus.Info("Executing `arduino-cli upgrade`") - err := commands.Upgrade(context.Background(), &rpc.UpgradeRequest{ + err := upgrade.Upgrade(context.Background(), &rpc.UpgradeRequest{ Instance: inst, SkipPostInstall: postInstallFlags.DetectSkipPostInstallValue(), }, output.NewDownloadProgressBarCB(), output.TaskProgress()) diff --git a/commands/bundled_tools.go b/commands/bundled_tools.go deleted file mode 100644 index e55af7ae21a..00000000000 --- a/commands/bundled_tools.go +++ /dev/null @@ -1,56 +0,0 @@ -// 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 license@arduino.cc. - -package commands - -import ( - "github.com/arduino/arduino-cli/arduino" - "github.com/arduino/arduino-cli/arduino/cores" - "github.com/arduino/arduino-cli/arduino/cores/packagemanager" - "github.com/arduino/arduino-cli/arduino/httpclient" - rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" -) - -// DownloadToolRelease downloads a ToolRelease -func DownloadToolRelease(pm *packagemanager.PackageManager, toolRelease *cores.ToolRelease, downloadCB rpc.DownloadProgressCB) error { - config, err := httpclient.GetDownloaderConfig() - if err != nil { - return err - } - return pm.DownloadToolRelease(toolRelease, config, toolRelease.String(), downloadCB) -} - -// InstallToolRelease installs a ToolRelease -func InstallToolRelease(pm *packagemanager.PackageManager, toolRelease *cores.ToolRelease, taskCB rpc.TaskProgressCB) error { - log := pm.Log.WithField("Tool", toolRelease) - - if toolRelease.IsInstalled() { - log.Warn("Tool already installed") - taskCB(&rpc.TaskProgress{Name: tr("Tool %s already installed", toolRelease), Completed: true}) - return nil - } - - log.Info("Installing tool") - taskCB(&rpc.TaskProgress{Name: tr("Installing %s", toolRelease)}) - err := pm.InstallTool(toolRelease) - if err != nil { - log.WithError(err).Warn("Cannot install tool") - return &arduino.FailedInstallError{Message: tr("Cannot install tool %s", toolRelease), Cause: err} - } - log.Info("Tool installed") - taskCB(&rpc.TaskProgress{Message: tr("%s installed", toolRelease), Completed: true}) - - return nil -} diff --git a/commands/core/download.go b/commands/core/download.go index b1ef76395df..4da671ed837 100644 --- a/commands/core/download.go +++ b/commands/core/download.go @@ -17,12 +17,9 @@ package core import ( "context" - "errors" "github.com/arduino/arduino-cli/arduino" - "github.com/arduino/arduino-cli/arduino/cores" "github.com/arduino/arduino-cli/arduino/cores/packagemanager" - "github.com/arduino/arduino-cli/arduino/httpclient" "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/i18n" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" @@ -52,39 +49,15 @@ func PlatformDownload(ctx context.Context, req *rpc.PlatformDownloadRequest, dow return nil, &arduino.PlatformNotFoundError{Platform: ref.String(), Cause: err} } - if err := downloadPlatform(pm, platform, downloadCB); err != nil { + if err := pm.DownloadPlatformRelease(platform, nil, downloadCB); err != nil { return nil, err } for _, tool := range tools { - if err := downloadTool(pm, tool, downloadCB); err != nil { + if err := pm.DownloadToolRelease(tool, nil, downloadCB); err != nil { return nil, err } } return &rpc.PlatformDownloadResponse{}, nil } - -func downloadPlatform(pm *packagemanager.PackageManager, platformRelease *cores.PlatformRelease, downloadCB rpc.DownloadProgressCB) error { - // Download platform - config, err := httpclient.GetDownloaderConfig() - if err != nil { - return &arduino.FailedDownloadError{Message: tr("Error downloading platform %s", platformRelease), Cause: err} - } - return pm.DownloadPlatformRelease(platformRelease, config, downloadCB) -} - -func downloadTool(pm *packagemanager.PackageManager, tool *cores.ToolRelease, downloadCB rpc.DownloadProgressCB) error { - // Check if tool has a flavor available for the current OS - if tool.GetCompatibleFlavour() == nil { - return &arduino.FailedDownloadError{ - Message: tr("Error downloading tool %s", tool), - Cause: errors.New(tr("no versions available for the current OS", tool))} - } - - if err := commands.DownloadToolRelease(pm, tool, downloadCB); err != nil { - return &arduino.FailedDownloadError{Message: tr("Error downloading tool %s", tool), Cause: err} - } - - return nil -} diff --git a/commands/core/install.go b/commands/core/install.go index 42c4db2935d..21c9f0c9c46 100644 --- a/commands/core/install.go +++ b/commands/core/install.go @@ -20,7 +20,6 @@ import ( "fmt" "github.com/arduino/arduino-cli/arduino" - "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/cc/arduino/cli/commands/v1" @@ -64,7 +63,7 @@ func PlatformInstall(ctx context.Context, req *rpc.PlatformInstallRequest, } } - if err := installPlatform(pm, platformRelease, tools, downloadCB, taskCB, req.GetSkipPostInstall()); err != nil { + if err := pm.DownloadAndInstallPlatformAndTools(platformRelease, tools, downloadCB, taskCB, req.GetSkipPostInstall()); err != nil { return nil, err } @@ -74,115 +73,3 @@ func PlatformInstall(ctx context.Context, req *rpc.PlatformInstallRequest, return &rpc.PlatformInstallResponse{}, nil } - -func installPlatform(pm *packagemanager.PackageManager, - platformRelease *cores.PlatformRelease, requiredTools []*cores.ToolRelease, - downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB, - skipPostInstall bool) error { - log := pm.Log.WithField("platform", platformRelease) - - // Prerequisite checks before install - toolsToInstall := []*cores.ToolRelease{} - for _, tool := range requiredTools { - if tool.IsInstalled() { - log.WithField("tool", tool).Warn("Tool already installed") - taskCB(&rpc.TaskProgress{Name: tr("Tool %s already installed", tool), Completed: true}) - } else { - toolsToInstall = append(toolsToInstall, tool) - } - } - - // Package download - taskCB(&rpc.TaskProgress{Name: tr("Downloading packages")}) - for _, tool := range toolsToInstall { - if err := downloadTool(pm, tool, downloadCB); err != nil { - return err - } - } - if err := downloadPlatform(pm, platformRelease, downloadCB); err != nil { - return err - } - taskCB(&rpc.TaskProgress{Completed: true}) - - // Install tools first - for _, tool := range toolsToInstall { - if err := commands.InstallToolRelease(pm, tool, taskCB); err != nil { - return err - } - } - - installed := pm.GetInstalledPlatformRelease(platformRelease.Platform) - installedTools := []*cores.ToolRelease{} - if installed == nil { - // No version of this platform is installed - log.Info("Installing platform") - taskCB(&rpc.TaskProgress{Name: tr("Installing platform %s", platformRelease)}) - } else { - // A platform with a different version is already installed - log.Info("Replacing platform " + installed.String()) - taskCB(&rpc.TaskProgress{Name: tr("Replacing platform %[1]s with %[2]s", installed, platformRelease)}) - platformRef := &packagemanager.PlatformReference{ - Package: platformRelease.Platform.Package.Name, - PlatformArchitecture: platformRelease.Platform.Architecture, - PlatformVersion: installed.Version, - } - - // Get a list of tools used by the currently installed platform version. - // This must be done so tools used by the currently installed version are - // removed if not used also by the newly installed version. - var err error - _, installedTools, err = pm.FindPlatformReleaseDependencies(platformRef) - if err != nil { - return &arduino.NotFoundError{Message: tr("Can't find dependencies for platform %s", platformRef), Cause: err} - } - } - - // Install - if err := pm.InstallPlatform(platformRelease); err != nil { - log.WithError(err).Error("Cannot install platform") - return &arduino.FailedInstallError{Message: tr("Cannot install platform"), Cause: err} - } - - // If upgrading remove previous release - if installed != nil { - uninstallErr := pm.UninstallPlatform(installed) - - // 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 := pm.UninstallPlatform(platformRelease); err != nil { - log.WithError(err).Error("Error rolling-back changes.") - taskCB(&rpc.TaskProgress{Message: tr("Error rolling-back changes: %s", err)}) - } - - return &arduino.FailedInstallError{Message: tr("Cannot upgrade platform"), Cause: uninstallErr} - } - - // Uninstall unused tools - for _, tool := range installedTools { - if !pm.IsToolRequired(tool) { - uninstallToolRelease(pm, tool, taskCB) - } - } - - } - - // Perform post install - if !skipPostInstall { - log.Info("Running post_install script") - taskCB(&rpc.TaskProgress{Message: tr("Configuring platform.")}) - if err := pm.RunPostInstallScript(platformRelease); err != nil { - taskCB(&rpc.TaskProgress{Message: tr("WARNING cannot configure platform: %s", err)}) - } - } else { - log.Info("Skipping platform configuration.") - taskCB(&rpc.TaskProgress{Message: tr("Skipping platform configuration.")}) - } - - log.Info("Platform installed") - taskCB(&rpc.TaskProgress{Message: tr("Platform %s installed", platformRelease), Completed: true}) - return nil -} diff --git a/commands/core/uninstall.go b/commands/core/uninstall.go index bbd9a0347d5..b5127e3cd37 100644 --- a/commands/core/uninstall.go +++ b/commands/core/uninstall.go @@ -19,7 +19,6 @@ import ( "context" "github.com/arduino/arduino-cli/arduino" - "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/cc/arduino/cli/commands/v1" @@ -53,13 +52,14 @@ func PlatformUninstall(ctx context.Context, req *rpc.PlatformUninstallRequest, t return nil, &arduino.NotFoundError{Message: tr("Can't find dependencies for platform %s", ref), Cause: err} } - if err := uninstallPlatformRelease(pm, platform, taskCB); err != nil { + if err := pm.UninstallPlatform(platform, taskCB); err != nil { return nil, err } for _, tool := range tools { if !pm.IsToolRequired(tool) { - uninstallToolRelease(pm, tool, taskCB) + taskCB(&rpc.TaskProgress{Name: tr("Uninstalling %s, tool is no more required", tool)}) + pm.UninstallTool(tool, taskCB) } } @@ -69,35 +69,3 @@ func PlatformUninstall(ctx context.Context, req *rpc.PlatformUninstallRequest, t return &rpc.PlatformUninstallResponse{}, nil } - -func uninstallPlatformRelease(pm *packagemanager.PackageManager, platformRelease *cores.PlatformRelease, taskCB rpc.TaskProgressCB) error { - log := pm.Log.WithField("platform", platformRelease) - - log.Info("Uninstalling platform") - taskCB(&rpc.TaskProgress{Name: tr("Uninstalling %s", platformRelease)}) - - if err := pm.UninstallPlatform(platformRelease); err != nil { - log.WithError(err).Error("Error uninstalling") - return &arduino.FailedUninstallError{Message: tr("Error uninstalling platform %s", platformRelease), Cause: err} - } - - log.Info("Platform uninstalled") - taskCB(&rpc.TaskProgress{Message: tr("Platform %s uninstalled", platformRelease), Completed: true}) - return nil -} - -func uninstallToolRelease(pm *packagemanager.PackageManager, toolRelease *cores.ToolRelease, taskCB rpc.TaskProgressCB) error { - log := pm.Log.WithField("Tool", toolRelease) - - log.Info("Uninstalling tool") - taskCB(&rpc.TaskProgress{Name: tr("Uninstalling %s, tool is no more required", toolRelease)}) - - if err := pm.UninstallTool(toolRelease); err != nil { - log.WithError(err).Error("Error uninstalling") - return &arduino.FailedUninstallError{Message: tr("Error uninstalling tool %s", toolRelease), Cause: err} - } - - log.Info("Tool uninstalled") - taskCB(&rpc.TaskProgress{Message: tr("Tool %s uninstalled", toolRelease), Completed: true}) - return nil -} diff --git a/commands/core/upgrade.go b/commands/core/upgrade.go index 20c88e5707b..6f55ab2367e 100644 --- a/commands/core/upgrade.go +++ b/commands/core/upgrade.go @@ -38,7 +38,7 @@ func PlatformUpgrade(ctx context.Context, req *rpc.PlatformUpgradeRequest, Package: req.PlatformPackage, PlatformArchitecture: req.Architecture, } - if err := upgradePlatform(pm, ref, downloadCB, taskCB, req.GetSkipPostInstall()); err != nil { + if err := pm.DownloadAndInstallPlatformUpgrades(ref, downloadCB, taskCB, req.GetSkipPostInstall()); err != nil { return nil, err } @@ -48,35 +48,3 @@ func PlatformUpgrade(ctx context.Context, req *rpc.PlatformUpgradeRequest, return &rpc.PlatformUpgradeResponse{}, nil } - -func upgradePlatform(pm *packagemanager.PackageManager, platformRef *packagemanager.PlatformReference, - downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB, skipPostInstall bool) error { - if platformRef.PlatformVersion != nil { - return &arduino.InvalidArgumentError{Message: tr("Upgrade doesn't accept parameters with version")} - } - - // Search the latest version for all specified platforms - platform := pm.FindPlatform(platformRef) - if platform == nil { - return &arduino.PlatformNotFoundError{Platform: platformRef.String()} - } - installed := pm.GetInstalledPlatformRelease(platform) - if installed == nil { - return &arduino.PlatformNotFoundError{Platform: platformRef.String()} - } - latest := platform.GetLatestRelease() - if !latest.Version.GreaterThan(installed.Version) { - return &arduino.PlatformAlreadyAtTheLatestVersionError{} - } - platformRef.PlatformVersion = latest.Version - - platformRelease, tools, err := pm.FindPlatformReleaseDependencies(platformRef) - if err != nil { - return &arduino.PlatformNotFoundError{Platform: platformRef.String()} - } - if err := installPlatform(pm, platformRelease, tools, downloadCB, taskCB, skipPostInstall); err != nil { - return err - } - - return nil -} diff --git a/commands/daemon/daemon.go b/commands/daemon/daemon.go index 4e84da07324..a82656bbe70 100644 --- a/commands/daemon/daemon.go +++ b/commands/daemon/daemon.go @@ -29,7 +29,9 @@ import ( "github.com/arduino/arduino-cli/commands/core" "github.com/arduino/arduino-cli/commands/lib" "github.com/arduino/arduino-cli/commands/monitor" + "github.com/arduino/arduino-cli/commands/outdated" "github.com/arduino/arduino-cli/commands/sketch" + "github.com/arduino/arduino-cli/commands/upgrade" "github.com/arduino/arduino-cli/commands/upload" "github.com/arduino/arduino-cli/i18n" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" @@ -202,13 +204,13 @@ func (s *ArduinoCoreServerImpl) UpdateCoreLibrariesIndex(req *rpc.UpdateCoreLibr // Outdated FIXMEDOC func (s *ArduinoCoreServerImpl) Outdated(ctx context.Context, req *rpc.OutdatedRequest) (*rpc.OutdatedResponse, error) { - resp, err := commands.Outdated(ctx, req) + resp, err := outdated.Outdated(ctx, req) return resp, convertErrorToRPCStatus(err) } // Upgrade FIXMEDOC func (s *ArduinoCoreServerImpl) Upgrade(req *rpc.UpgradeRequest, stream rpc.ArduinoCoreService_UpgradeServer) error { - err := commands.Upgrade(stream.Context(), req, + err := upgrade.Upgrade(stream.Context(), req, func(p *rpc.DownloadProgress) { stream.Send(&rpc.UpgradeResponse{ Progress: p, diff --git a/commands/instances.go b/commands/instances.go index 1b52fafd040..05b6f99f5db 100644 --- a/commands/instances.go +++ b/commands/instances.go @@ -17,7 +17,6 @@ package commands import ( "context" - "errors" "fmt" "net/url" "os" @@ -28,7 +27,6 @@ import ( "github.com/arduino/arduino-cli/arduino/cores/packageindex" "github.com/arduino/arduino-cli/arduino/cores/packagemanager" "github.com/arduino/arduino-cli/arduino/globals" - "github.com/arduino/arduino-cli/arduino/httpclient" "github.com/arduino/arduino-cli/arduino/libraries" "github.com/arduino/arduino-cli/arduino/libraries/librariesindex" "github.com/arduino/arduino-cli/arduino/libraries/librariesmanager" @@ -95,11 +93,11 @@ func (instance *CoreInstance) installToolIfMissing(tool *cores.ToolRelease, down return false, nil } taskCB(&rpc.TaskProgress{Name: tr("Downloading missing tool %s", tool)}) - if err := DownloadToolRelease(instance.PackageManager, tool, downloadCB); err != nil { + if err := instance.PackageManager.DownloadToolRelease(tool, nil, downloadCB); err != nil { return false, fmt.Errorf(tr("downloading %[1]s tool: %[2]s"), tool, err) } taskCB(&rpc.TaskProgress{Completed: true}) - if err := InstallToolRelease(instance.PackageManager, tool, taskCB); err != nil { + if err := instance.PackageManager.InstallTool(tool, taskCB); err != nil { return false, fmt.Errorf(tr("installing %[1]s tool: %[2]s"), tool, err) } return true, nil @@ -524,307 +522,6 @@ func UpdateCoreLibrariesIndex(ctx context.Context, req *rpc.UpdateCoreLibrariesI return nil } -// Outdated returns a list struct containing both Core and Libraries that can be updated -func Outdated(ctx context.Context, req *rpc.OutdatedRequest) (*rpc.OutdatedResponse, error) { - id := req.GetInstance().GetId() - - lm := GetLibraryManager(id) - if lm == nil { - return nil, &arduino.InvalidInstanceError{} - } - - outdatedLibraries := []*rpc.InstalledLibrary{} - for _, libAlternatives := range lm.Libraries { - for _, library := range libAlternatives.Alternatives { - if library.Location != libraries.User { - continue - } - available := lm.Index.FindLibraryUpdate(library) - if available == nil { - continue - } - - outdatedLibraries = append(outdatedLibraries, &rpc.InstalledLibrary{ - Library: getOutputLibrary(library), - Release: getOutputRelease(available), - }) - } - } - - pm := GetPackageManager(id) - if pm == nil { - return nil, &arduino.InvalidInstanceError{} - } - - outdatedPlatforms := []*rpc.Platform{} - for _, targetPackage := range pm.Packages { - for _, installed := range targetPackage.Platforms { - if installedRelease := pm.GetInstalledPlatformRelease(installed); installedRelease != nil { - latest := installed.GetLatestRelease() - if latest == nil || latest == installedRelease { - continue - } - rpcPlatform := PlatformReleaseToRPC(latest) - rpcPlatform.Installed = installedRelease.Version.String() - - outdatedPlatforms = append( - outdatedPlatforms, - rpcPlatform, - ) - } - } - } - - return &rpc.OutdatedResponse{ - OutdatedLibraries: outdatedLibraries, - OutdatedPlatforms: outdatedPlatforms, - }, nil -} - -func getOutputLibrary(lib *libraries.Library) *rpc.Library { - insdir := "" - if lib.InstallDir != nil { - insdir = lib.InstallDir.String() - } - srcdir := "" - if lib.SourceDir != nil { - srcdir = lib.SourceDir.String() - } - utldir := "" - if lib.UtilityDir != nil { - utldir = lib.UtilityDir.String() - } - cntplat := "" - if lib.ContainerPlatform != nil { - cntplat = lib.ContainerPlatform.String() - } - - return &rpc.Library{ - Name: lib.Name, - Author: lib.Author, - Maintainer: lib.Maintainer, - Sentence: lib.Sentence, - Paragraph: lib.Paragraph, - Website: lib.Website, - Category: lib.Category, - Architectures: lib.Architectures, - Types: lib.Types, - InstallDir: insdir, - SourceDir: srcdir, - UtilityDir: utldir, - Location: lib.Location.ToRPCLibraryLocation(), - ContainerPlatform: cntplat, - Layout: lib.Layout.ToRPCLibraryLayout(), - RealName: lib.RealName, - DotALinkage: lib.DotALinkage, - Precompiled: lib.Precompiled, - LdFlags: lib.LDflags, - IsLegacy: lib.IsLegacy, - Version: lib.Version.String(), - License: lib.License, - } -} - -func getOutputRelease(lib *librariesindex.Release) *rpc.LibraryRelease { - if lib != nil { - return &rpc.LibraryRelease{ - Author: lib.Author, - Version: lib.Version.String(), - Maintainer: lib.Maintainer, - Sentence: lib.Sentence, - Paragraph: lib.Paragraph, - Website: lib.Website, - Category: lib.Category, - Architectures: lib.Architectures, - Types: lib.Types, - } - } - return &rpc.LibraryRelease{} -} - -// Upgrade downloads and installs outdated Cores and Libraries -func Upgrade(ctx context.Context, req *rpc.UpgradeRequest, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) error { - downloaderConfig, err := httpclient.GetDownloaderConfig() - if err != nil { - return err - } - - lm := GetLibraryManager(req.Instance.Id) - if lm == nil { - return &arduino.InvalidInstanceError{} - } - - for _, libAlternatives := range lm.Libraries { - for _, library := range libAlternatives.Alternatives { - if library.Location != libraries.User { - continue - } - available := lm.Index.FindLibraryUpdate(library) - if available == nil { - continue - } - - // Downloads latest library release - taskCB(&rpc.TaskProgress{Name: tr("Downloading %s", available)}) - if err := available.Resource.Download(lm.DownloadsDir, downloaderConfig, available.String(), downloadCB); err != nil { - return &arduino.FailedDownloadError{Message: tr("Error downloading library"), Cause: err} - } - - // Installs downloaded library - taskCB(&rpc.TaskProgress{Name: tr("Installing %s", available)}) - libPath, libReplaced, err := lm.InstallPrerequisiteCheck(available) - if errors.Is(err, librariesmanager.ErrAlreadyInstalled) { - taskCB(&rpc.TaskProgress{Message: tr("Already installed %s", available), Completed: true}) - continue - } else if err != nil { - return &arduino.FailedLibraryInstallError{Cause: err} - } - - if libReplaced != nil { - taskCB(&rpc.TaskProgress{Message: tr("Replacing %[1]s with %[2]s", libReplaced, available)}) - } - - if err := lm.Install(available, libPath); err != nil { - return &arduino.FailedLibraryInstallError{Cause: err} - } - - taskCB(&rpc.TaskProgress{Message: tr("Installed %s", available), Completed: true}) - } - } - - pm := GetPackageManager(req.Instance.Id) - if pm == nil { - return &arduino.InvalidInstanceError{} - } - - for _, targetPackage := range pm.Packages { - for _, installed := range targetPackage.Platforms { - if installedRelease := pm.GetInstalledPlatformRelease(installed); installedRelease != nil { - latest := installed.GetLatestRelease() - if latest == nil || latest == installedRelease { - continue - } - - ref := &packagemanager.PlatformReference{ - Package: installedRelease.Platform.Package.Name, - PlatformArchitecture: installedRelease.Platform.Architecture, - PlatformVersion: installedRelease.Version, - } - // Get list of installed tools needed by the currently installed version - _, installedTools, err := pm.FindPlatformReleaseDependencies(ref) - if err != nil { - return &arduino.NotFoundError{Message: tr("Can't find dependencies for platform %s", ref), Cause: err} - } - - ref = &packagemanager.PlatformReference{ - Package: latest.Platform.Package.Name, - PlatformArchitecture: latest.Platform.Architecture, - PlatformVersion: latest.Version, - } - - taskCB(&rpc.TaskProgress{Name: tr("Downloading %s", latest)}) - _, tools, err := pm.FindPlatformReleaseDependencies(ref) - if err != nil { - return &arduino.NotFoundError{Message: tr("Can't find dependencies for platform %s", ref), Cause: err} - } - - toolsToInstall := []*cores.ToolRelease{} - for _, tool := range tools { - if tool.IsInstalled() { - logrus.WithField("tool", tool).Warn("Tool already installed") - taskCB(&rpc.TaskProgress{Name: tr("Tool %s already installed", tool), Completed: true}) - } else { - toolsToInstall = append(toolsToInstall, tool) - } - } - - // Downloads platform tools - for _, tool := range toolsToInstall { - if err := DownloadToolRelease(pm, tool, downloadCB); err != nil { - taskCB(&rpc.TaskProgress{Message: tr("Error downloading tool %s", tool)}) - return &arduino.FailedDownloadError{Message: tr("Error downloading tool %s", tool), Cause: err} - } - } - - // Downloads platform - if err := pm.DownloadPlatformRelease(latest, downloaderConfig, downloadCB); err != nil { - return &arduino.FailedDownloadError{Message: tr("Error downloading platform %s", latest), Cause: err} - } - - logrus.Info("Updating platform " + installed.String()) - taskCB(&rpc.TaskProgress{Name: tr("Updating platform %s", latest)}) - - // Installs tools - for _, tool := range toolsToInstall { - if err := InstallToolRelease(pm, tool, taskCB); err != nil { - msg := tr("Error installing tool %s", tool) - taskCB(&rpc.TaskProgress{Message: msg}) - return &arduino.FailedInstallError{Message: msg, Cause: err} - } - } - - // Installs platform - err = pm.InstallPlatform(latest) - if err != nil { - logrus.WithError(err).Error("Cannot install platform") - msg := tr("Error installing platform %s", latest) - taskCB(&rpc.TaskProgress{Message: msg}) - return &arduino.FailedInstallError{Message: msg, Cause: err} - } - - // Uninstall previously installed release - err = pm.UninstallPlatform(installedRelease) - - // In case uninstall fails tries to rollback - if err != nil { - logrus.WithError(err).Error("Error updating platform.") - taskCB(&rpc.TaskProgress{Message: tr("Error upgrading platform: %s", err)}) - - // Rollback - if err := pm.UninstallPlatform(latest); err != nil { - logrus.WithError(err).Error("Error rolling-back changes.") - msg := tr("Error rolling-back changes") - taskCB(&rpc.TaskProgress{Message: fmt.Sprintf("%s: %s", msg, err)}) - return &arduino.FailedInstallError{Message: msg, Cause: err} - } - } - - // Uninstall unused tools - for _, toolRelease := range installedTools { - if !pm.IsToolRequired(toolRelease) { - log := pm.Log.WithField("Tool", toolRelease) - - log.Info("Uninstalling tool") - taskCB(&rpc.TaskProgress{Name: tr("Uninstalling %s: tool is no more required", toolRelease)}) - - if err := pm.UninstallTool(toolRelease); err != nil { - log.WithError(err).Error("Error uninstalling") - return &arduino.FailedInstallError{Message: tr("Error uninstalling tool %s", toolRelease), Cause: err} - } - - log.Info("Tool uninstalled") - taskCB(&rpc.TaskProgress{Message: tr("%s uninstalled", toolRelease), Completed: true}) - } - } - - // Perform post install - if !req.SkipPostInstall { - logrus.Info("Running post_install script") - taskCB(&rpc.TaskProgress{Message: tr("Configuring platform")}) - if err := pm.RunPostInstallScript(latest); err != nil { - taskCB(&rpc.TaskProgress{Message: tr("WARNING: cannot run post install: %s", err)}) - } - } else { - logrus.Info("Skipping platform configuration (post_install run).") - taskCB(&rpc.TaskProgress{Message: tr("Skipping platform configuration")}) - } - } - } - } - - return nil -} - // LoadSketch collects and returns all files composing a sketch func LoadSketch(ctx context.Context, req *rpc.LoadSketchRequest) (*rpc.LoadSketchResponse, error) { // TODO: This should be a ToRpc function for the Sketch struct diff --git a/commands/outdated/outdated.go b/commands/outdated/outdated.go new file mode 100644 index 00000000000..3bcf3503bde --- /dev/null +++ b/commands/outdated/outdated.go @@ -0,0 +1,48 @@ +// This file is part of arduino-cli. +// +// Copyright 2022 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 license@arduino.cc. + +package outdated + +import ( + "context" + + "github.com/arduino/arduino-cli/commands/core" + "github.com/arduino/arduino-cli/commands/lib" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" +) + +// Outdated returns a list struct containing both Core and Libraries that can be updated +func Outdated(ctx context.Context, req *rpc.OutdatedRequest) (*rpc.OutdatedResponse, error) { + libraryListResponse, err := lib.LibraryList(ctx, &rpc.LibraryListRequest{ + Instance: req.GetInstance(), + Updatable: true, + }) + if err != nil { + return nil, err + } + + getPlatformsResp, err := core.GetPlatforms(&rpc.PlatformListRequest{ + Instance: req.GetInstance(), + UpdatableOnly: true, + }) + if err != nil { + return nil, err + } + + return &rpc.OutdatedResponse{ + OutdatedLibraries: libraryListResponse.GetInstalledLibraries(), + OutdatedPlatforms: getPlatformsResp, + }, nil +} diff --git a/commands/upgrade/upgrade.go b/commands/upgrade/upgrade.go new file mode 100644 index 00000000000..e30d09f2a27 --- /dev/null +++ b/commands/upgrade/upgrade.go @@ -0,0 +1,59 @@ +// This file is part of arduino-cli. +// +// Copyright 2022 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 license@arduino.cc. + +package upgrade + +import ( + "context" + "strings" + + "github.com/arduino/arduino-cli/commands/core" + "github.com/arduino/arduino-cli/commands/lib" + "github.com/arduino/arduino-cli/commands/outdated" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" +) + +// Upgrade downloads and installs outdated Cores and Libraries +func Upgrade(ctx context.Context, req *rpc.UpgradeRequest, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) error { + outdatedResp, err := outdated.Outdated(ctx, &rpc.OutdatedRequest{Instance: req.GetInstance()}) + if err != nil { + return err + } + + for _, libToUpgrade := range outdatedResp.GetOutdatedLibraries() { + err := lib.LibraryInstall(ctx, &rpc.LibraryInstallRequest{ + Instance: req.GetInstance(), + Name: libToUpgrade.GetLibrary().GetName(), + }, downloadCB, taskCB) + if err != nil { + return err + } + } + + for _, platformToUpgrade := range outdatedResp.GetOutdatedPlatforms() { + split := strings.Split(platformToUpgrade.GetId(), ":") + _, err := core.PlatformUpgrade(ctx, &rpc.PlatformUpgradeRequest{ + Instance: req.GetInstance(), + PlatformPackage: split[0], + Architecture: split[1], + SkipPostInstall: req.GetSkipPostInstall(), + }, downloadCB, taskCB) + if err != nil { + return err + } + } + + return nil +} diff --git a/docs/UPGRADING.md b/docs/UPGRADING.md index 34c00a402ac..80f7f28264e 100644 --- a/docs/UPGRADING.md +++ b/docs/UPGRADING.md @@ -2,6 +2,56 @@ Here you can find a list of migration guides to handle breaking changes between releases of the CLI. +## 0.26.0 + +### `github.com/arduino/arduino-cli/commands.DownloadToolRelease`, and `InstallToolRelease` functions have been removed + +This functionality was duplicated and already available via `PackageManager` methods. + +### `github.com/arduino/arduino-cli/commands.Outdated` and `Upgrade` functions have been moved + +- `github.com/arduino/arduino-cli/commands.Outdated` is now `github.com/arduino/arduino-cli/commands/outdated.Outdated` +- `github.com/arduino/arduino-cli/commands.Upgrade` is now `github.com/arduino/arduino-cli/commands/upgrade.Upgrade` + +Old code must change the imports accordingly. + +### `github.com/arduino-cli/arduino/cores/packagemanager.PackageManager` methods and fields change + +- The `PackageManager.Log` and `TempDir` fields are now private. + +- The `PackageManager.DownloadToolRelease` method has no more the `label` parameter: + + ```go + func (pm *PackageManager) DownloadToolRelease(tool *cores.ToolRelease, config *downloader.Config, label string, progressCB rpc.DownloadProgressCB) error { + ``` + + has been changed to: + + ```go + func (pm *PackageManager) DownloadToolRelease(tool *cores.ToolRelease, config *downloader.Config, progressCB rpc.DownloadProgressCB) error { + ``` + + Old code should remove the `label` parameter. + +- The `PackageManager.UninstallPlatform`, `PackageManager.InstallTool`, and `PackageManager.UninstallTool` methods now + requires a `github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1.TaskProgressCB` + + ```go + func (pm *PackageManager) UninstallPlatform(platformRelease *cores.PlatformRelease) error { + func (pm *PackageManager) InstallTool(toolRelease *cores.ToolRelease) error { + func (pm *PackageManager) UninstallTool(toolRelease *cores.ToolRelease) error { + ``` + + have been changed to: + + ```go + func (pm *PackageManager) UninstallPlatform(platformRelease *cores.PlatformRelease, taskCB rpc.TaskProgressCB) error { + func (pm *PackageManager) InstallTool(toolRelease *cores.ToolRelease, taskCB rpc.TaskProgressCB) error { + func (pm *PackageManager) UninstallTool(toolRelease *cores.ToolRelease, taskCB rpc.TaskProgressCB) error { + ``` + + If you're not interested in getting the task events you can pass an empty callback function. + ## 0.25.0 ### go-lang function `github.com/arduino/arduino-cli/arduino/utils.FeedStreamTo` has been changed