From 010795737c29916ce16f80bdb8e3310d81b12fee Mon Sep 17 00:00:00 2001 From: Silvano Cerza Date: Mon, 16 Nov 2020 12:56:00 +0100 Subject: [PATCH] lib install --git-url and --zip-file must now be esplicitly enabled --- cli/lib/install.go | 108 +++++++++++++++++++++----------------- configuration/defaults.go | 4 ++ test/test_lib.py | 85 ++++++++++++++++++++++++++---- 3 files changed, 139 insertions(+), 58 deletions(-) diff --git a/cli/lib/install.go b/cli/lib/install.go index 75ba089fad5..ef02d3e9e15 100644 --- a/cli/lib/install.go +++ b/cli/lib/install.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/lib" + "github.com/arduino/arduino-cli/configuration" rpc "github.com/arduino/arduino-cli/rpc/commands" "github.com/spf13/cobra" ) @@ -40,8 +41,10 @@ func initInstallCommand() *cobra.Command { Run: runInstallCommand, } installCommand.Flags().BoolVar(&installFlags.noDeps, "no-deps", false, "Do not install dependencies.") - installCommand.Flags().BoolVar(&installFlags.gitURL, "git-url", false, "Enter git url for libraries hosted on repositories") - installCommand.Flags().BoolVar(&installFlags.zipPath, "zip-path", false, "Enter a path to zip file") + if configuration.Settings.GetBool("library.enable_unsafe_install") { + installCommand.Flags().BoolVar(&installFlags.gitURL, "git-url", false, "Enter git url for libraries hosted on repositories") + installCommand.Flags().BoolVar(&installFlags.zipPath, "zip-path", false, "Enter a path to zip file") + } return installCommand } @@ -53,6 +56,11 @@ var installFlags struct { func runInstallCommand(cmd *cobra.Command, args []string) { instance := instance.CreateInstanceIgnorePlatformIndexErrors() + + if installFlags.zipPath || installFlags.gitURL { + feedback.Print("--git-url and --zip-path flags are dangerous, use it at your own risk.") + } + if installFlags.zipPath { ziplibraryInstallReq := &rpc.ZipLibraryInstallReq{ Instance: instance, @@ -63,7 +71,10 @@ func runInstallCommand(cmd *cobra.Command, args []string) { feedback.Errorf("Error installing Zip Library: %v", err) os.Exit(errorcodes.ErrGeneric) } - } else if installFlags.gitURL { + return + } + + if installFlags.gitURL { gitlibraryInstallReq := &rpc.GitLibraryInstallReq{ Instance: instance, Url: args[0], @@ -73,58 +84,59 @@ func runInstallCommand(cmd *cobra.Command, args []string) { feedback.Errorf("Error installing Git Library: %v", err) os.Exit(errorcodes.ErrGeneric) } - } else { - libRefs, err := ParseLibraryReferenceArgsAndAdjustCase(instance, args) - if err != nil { - feedback.Errorf("Arguments error: %v", err) - os.Exit(errorcodes.ErrBadArgument) - } + return + } - toInstall := map[string]*rpc.LibraryDependencyStatus{} - if installFlags.noDeps { - for _, libRef := range libRefs { - toInstall[libRef.Name] = &rpc.LibraryDependencyStatus{ - Name: libRef.Name, - VersionRequired: libRef.Version, - } - } - } else { - for _, libRef := range libRefs { - depsResp, err := lib.LibraryResolveDependencies(context.Background(), &rpc.LibraryResolveDependenciesReq{ - Instance: instance, - Name: libRef.Name, - Version: libRef.Version, - }) - if err != nil { - feedback.Errorf("Error resolving dependencies for %s: %s", libRef, err) - os.Exit(errorcodes.ErrGeneric) - } - for _, dep := range depsResp.GetDependencies() { - feedback.Printf("%s depends on %s@%s", libRef, dep.GetName(), dep.GetVersionRequired()) - if existingDep, has := toInstall[dep.GetName()]; has { - if existingDep.GetVersionRequired() != dep.GetVersionRequired() { - // TODO: make a better error - feedback.Errorf("The library %s is required in two different versions: %s and %s", - dep.GetName(), dep.GetVersionRequired(), existingDep.GetVersionRequired()) - os.Exit(errorcodes.ErrGeneric) - } - } - toInstall[dep.GetName()] = dep - } + libRefs, err := ParseLibraryReferenceArgsAndAdjustCase(instance, args) + if err != nil { + feedback.Errorf("Arguments error: %v", err) + os.Exit(errorcodes.ErrBadArgument) + } + + toInstall := map[string]*rpc.LibraryDependencyStatus{} + if installFlags.noDeps { + for _, libRef := range libRefs { + toInstall[libRef.Name] = &rpc.LibraryDependencyStatus{ + Name: libRef.Name, + VersionRequired: libRef.Version, } } - - for _, library := range toInstall { - libraryInstallReq := &rpc.LibraryInstallReq{ + } else { + for _, libRef := range libRefs { + depsResp, err := lib.LibraryResolveDependencies(context.Background(), &rpc.LibraryResolveDependenciesReq{ Instance: instance, - Name: library.Name, - Version: library.VersionRequired, - } - err := lib.LibraryInstall(context.Background(), libraryInstallReq, output.ProgressBar(), output.TaskProgress()) + Name: libRef.Name, + Version: libRef.Version, + }) if err != nil { - feedback.Errorf("Error installing %s: %v", library, err) + feedback.Errorf("Error resolving dependencies for %s: %s", libRef, err) os.Exit(errorcodes.ErrGeneric) } + for _, dep := range depsResp.GetDependencies() { + feedback.Printf("%s depends on %s@%s", libRef, dep.GetName(), dep.GetVersionRequired()) + if existingDep, has := toInstall[dep.GetName()]; has { + if existingDep.GetVersionRequired() != dep.GetVersionRequired() { + // TODO: make a better error + feedback.Errorf("The library %s is required in two different versions: %s and %s", + dep.GetName(), dep.GetVersionRequired(), existingDep.GetVersionRequired()) + os.Exit(errorcodes.ErrGeneric) + } + } + toInstall[dep.GetName()] = dep + } + } + } + + for _, library := range toInstall { + libraryInstallReq := &rpc.LibraryInstallReq{ + Instance: instance, + Name: library.Name, + Version: library.VersionRequired, + } + err := lib.LibraryInstall(context.Background(), libraryInstallReq, output.ProgressBar(), output.TaskProgress()) + if err != nil { + feedback.Errorf("Error installing %s: %v", library, err) + os.Exit(errorcodes.ErrGeneric) } } } diff --git a/configuration/defaults.go b/configuration/defaults.go index 68bc171fdb2..58eae4921a7 100644 --- a/configuration/defaults.go +++ b/configuration/defaults.go @@ -28,6 +28,9 @@ func SetDefaults(settings *viper.Viper) { settings.SetDefault("logging.level", "info") settings.SetDefault("logging.format", "text") + // Libraries + settings.SetDefault("library.enable_unsafe_install", false) + // Boards Manager settings.SetDefault("board_manager.additional_urls", []string{}) @@ -52,6 +55,7 @@ func SetDefaults(settings *viper.Viper) { settings.AutomaticEnv() // Bind env aliases to keep backward compatibility + settings.BindEnv("library.enable_unsafe_install", "ARDUINO_ENABLE_UNSAFE_LIBRARY_INSTALL") settings.BindEnv("directories.User", "ARDUINO_SKETCHBOOK_DIR") settings.BindEnv("directories.Downloads", "ARDUINO_DOWNLOADS_DIR") settings.BindEnv("directories.Data", "ARDUINO_DATA_DIR") diff --git a/test/test_lib.py b/test/test_lib.py index 4fffa533838..7d396e278c5 100644 --- a/test/test_lib.py +++ b/test/test_lib.py @@ -12,8 +12,8 @@ # 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. -import os import simplejson as json +from pathlib import Path def test_list(run_command): @@ -145,7 +145,7 @@ def test_lib_download(run_command, downloads_dir): # Download a specific lib version assert run_command("lib download AudioZero@1.0.0") - assert os.path.exists(os.path.join(downloads_dir, "libraries", "AudioZero-1.0.0.zip")) + assert Path(downloads_dir, "libraries", "AudioZero-1.0.0.zip").exists() # Wrong lib version result = run_command("lib download AudioZero@69.42.0") @@ -168,23 +168,88 @@ def test_install(run_command): assert "Error resolving dependencies for MD_Parola@3.2.0: dependency 'MD_MAX72xx' is not available" in result.stderr -def test_install_with_git_url(run_command): +def test_install_git_url_and_zip_path_flags_visibility(run_command, data_dir, downloads_dir): + # Verifies installation fail because flags are not found + git_url = "https://github.com/arduino-libraries/WiFi101.git" + res = run_command(f"lib install --git-url {git_url}") + assert res.failed + assert "Error: unknown flag: --git-url" in res.stderr + + assert run_command("lib download AudioZero@1.0.0") + zip_path = Path(downloads_dir, "libraries", "AudioZero-1.0.0.zip") + res = run_command(f"lib install --zip-path {zip_path}") + assert res.failed + assert "Error: unknown flag: --zip-path" in res.stderr + + env = { + "ARDUINO_DATA_DIR": data_dir, + "ARDUINO_DOWNLOADS_DIR": downloads_dir, + "ARDUINO_SKETCHBOOK_DIR": data_dir, + "ARDUINO_ENABLE_UNSAFE_LIBRARY_INSTALL": "true", + } + # Verifies installation is successful when flags are enabled with env var + res = run_command(f"lib install --git-url {git_url}", custom_env=env) + assert res.ok + assert "--git-url and --zip-path flags are dangerous, use it at your own risk." in res.stdout + + res = run_command(f"lib install --zip-path {zip_path}", custom_env=env) + assert res.ok + assert "--git-url and --zip-path flags are dangerous, use it at your own risk." in res.stdout + + # Uninstall libraries to install them again + assert run_command("lib uninstall WiFi101 AudioZero") + + # Verifies installation is successful when flags are enabled with settings file + assert run_command("config init --dest-dir .", custom_env=env) + + res = run_command(f"lib install --git-url {git_url}") + assert res.ok + assert "--git-url and --zip-path flags are dangerous, use it at your own risk." in res.stdout + + res = run_command(f"lib install --zip-path {zip_path}") + assert res.ok + assert "--git-url and --zip-path flags are dangerous, use it at your own risk." in res.stdout + + +def test_install_with_git_url(run_command, data_dir, downloads_dir): + # Initialize configs to enable --git-url flag + env = { + "ARDUINO_DATA_DIR": data_dir, + "ARDUINO_DOWNLOADS_DIR": downloads_dir, + "ARDUINO_SKETCHBOOK_DIR": data_dir, + "ARDUINO_ENABLE_UNSAFE_LIBRARY_INSTALL": "true", + } + assert run_command("config init --dest-dir .", custom_env=env) + # Test git-url library install - assert run_command("lib install --git-url https://github.com/arduino-libraries/WiFi101.git") + res = run_command("lib install --git-url https://github.com/arduino-libraries/WiFi101.git") + assert res.ok + assert "--git-url and --zip-path flags are dangerous, use it at your own risk." in res.stdout # Test failing-install as repository already exists - result = run_command("lib install --git-url https://github.com/arduino-libraries/WiFi101.git") - assert "Error installing Git Library: repository already exists" in result.stderr + res = run_command("lib install --git-url https://github.com/arduino-libraries/WiFi101.git") + assert "--git-url and --zip-path flags are dangerous, use it at your own risk." in res.stdout + assert "Error installing Git Library: repository already exists" in res.stderr -def test_install_with_zip_path(run_command, downloads_dir): +def test_install_with_zip_path(run_command, data_dir, downloads_dir): + # Initialize configs to enable --zip-path flag + env = { + "ARDUINO_DATA_DIR": data_dir, + "ARDUINO_DOWNLOADS_DIR": downloads_dir, + "ARDUINO_SKETCHBOOK_DIR": data_dir, + "ARDUINO_ENABLE_UNSAFE_LIBRARY_INSTALL": "true", + } + assert run_command("config init --dest-dir .", custom_env=env) + # Download a specific lib version assert run_command("lib download AudioZero@1.0.0") - assert os.path.exists(os.path.join(downloads_dir, "libraries", "AudioZero-1.0.0.zip")) - zip_path = os.path.join(downloads_dir, "libraries", "AudioZero-1.0.0.zip") + zip_path = Path(downloads_dir, "libraries", "AudioZero-1.0.0.zip") # Test zip-path install - assert run_command("lib install --zip-path {zip_path}".format(zip_path=zip_path)) + res = run_command(f"lib install --zip-path {zip_path}") + assert res.ok + assert "--git-url and --zip-path flags are dangerous, use it at your own risk." in res.stdout def test_update_index(run_command):