diff --git a/.github/workflows/check-go-task.yml b/.github/workflows/check-go-task.yml index 6e347253980..7388cc095e1 100644 --- a/.github/workflows/check-go-task.yml +++ b/.github/workflows/check-go-task.yml @@ -237,7 +237,7 @@ jobs: module: - path: internal/arduino/discovery/discovery_client - path: rpc/internal/client_example - - path: commands/daemon/term_example + - path: commands/term_example steps: - name: Checkout repository diff --git a/.golangci.yml b/.golangci.yml index a4057e492c6..a80fe164d7e 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -134,5 +134,7 @@ issues: - path-except: internal/cli/ linters: - forbidigo + - path: internal/cli/.*_test.go + linters: [forbidigo] - path: internal/cli/feedback/ linters: [forbidigo] diff --git a/Taskfile.yml b/Taskfile.yml index 2a9b20fd9ca..d5cbf0131a0 100755 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -95,7 +95,7 @@ tasks: dir: '{{default "./" .GO_MODULE_PATH}}' cmds: - | - go test \ + LANG=en go test \ -v \ -short \ -run '{{default ".*" .GO_TEST_REGEX}}' \ @@ -115,7 +115,7 @@ tasks: - | rm -fr coverage_data mkdir coverage_data - INTEGRATION_GOCOVERDIR={{ .ROOT_DIR }}/coverage_data go test \ + LANG=en INTEGRATION_GOCOVERDIR={{ .ROOT_DIR }}/coverage_data go test \ -v \ -short \ {{ .GO_TEST_PACKAGE }} \ diff --git a/commands/board/testdata/package_index.json b/commands/board/testdata/package_index.json deleted file mode 100644 index e36f45cb5b0..00000000000 --- a/commands/board/testdata/package_index.json +++ /dev/null @@ -1,2523 +0,0 @@ -{ - "packages": [ - { - "name": "arduino", - "maintainer": "Arduino", - "websiteURL": "http://www.arduino.cc/", - "email": "packages@arduino.cc", - "help": { - "online": "http://www.arduino.cc/en/Reference/HomePage" - }, - "platforms": [ - { - "name": "Arduino AVR Boards", - "architecture": "avr", - "version": "1.8.3", - "category": "Arduino", - "help": { - "online": "http://www.arduino.cc/en/Reference/HomePage" - }, - "url": "http://downloads.arduino.cc/cores/avr-1.8.3.tar.bz2", - "archiveFileName": "avr-1.8.3.tar.bz2", - "checksum": "SHA-256:de8a9b982477762d3d3e52fc2b682cdd8ff194dc3f1d46f4debdea6a01b33c14", - "size": "4941548", - "boards": [ - { "name": "Arduino Yún" }, - { "name": "Arduino Uno" }, - { "name": "Arduino Uno WiFi" }, - { "name": "Arduino Diecimila" }, - { "name": "Arduino Nano" }, - { "name": "Arduino Mega" }, - { "name": "Arduino MegaADK" }, - { "name": "Arduino Leonardo" }, - { "name": "Arduino Leonardo Ethernet" }, - { "name": "Arduino Micro" }, - { "name": "Arduino Esplora" }, - { "name": "Arduino Mini" }, - { "name": "Arduino Ethernet" }, - { "name": "Arduino Fio" }, - { "name": "Arduino BT" }, - { "name": "Arduino LilyPadUSB" }, - { "name": "Arduino Lilypad" }, - { "name": "Arduino Pro" }, - { "name": "Arduino ATMegaNG" }, - { "name": "Arduino Robot Control" }, - { "name": "Arduino Robot Motor" }, - { "name": "Arduino Gemma" }, - { "name": "Adafruit Circuit Playground" }, - { "name": "Arduino Yún Mini" }, - { "name": "Arduino Industrial 101" }, - { "name": "Linino One" } - ], - "toolsDependencies": [ - { - "packager": "arduino", - "name": "avr-gcc", - "version": "7.3.0-atmel3.6.1-arduino7" - }, - { - "packager": "arduino", - "name": "avrdude", - "version": "6.3.0-arduino17" - }, - { - "packager": "arduino", - "name": "arduinoOTA", - "version": "1.3.0" - } - ] - } - ], - "tools": [ - { - "name": "arm-none-eabi-gcc", - "version": "4.8.3-2014q1", - "systems": [ - { - "host": "arm-linux-gnueabihf", - "url": "http://downloads.arduino.cc/gcc-arm-none-eabi-4.8.3-2014q1-arm.tar.bz2", - "archiveFileName": "gcc-arm-none-eabi-4.8.3-2014q1-arm.tar.bz2", - "checksum": "SHA-256:ebe96b34c4f434667cab0187b881ed585e7c7eb990fe6b69be3c81ec7e11e845", - "size": "44423906" - }, - { - "host": "i686-mingw32", - "archiveFileName": "gcc-arm-none-eabi-4.8.3-2014q1-windows.tar.gz", - "url": "http://downloads.arduino.cc/gcc-arm-none-eabi-4.8.3-2014q1-windows.tar.gz", - "checksum": "SHA-256:fd8c111c861144f932728e00abd3f7d1107e186eb9cd6083a54c7236ea78b7c2", - "size": "84537449" - }, - { - "host": "x86_64-apple-darwin", - "url": "http://downloads.arduino.cc/gcc-arm-none-eabi-4.8.3-2014q1-mac.tar.gz", - "archiveFileName": "gcc-arm-none-eabi-4.8.3-2014q1-mac.tar.gz", - "checksum": "SHA-256:3598acf21600f17a8e4a4e8e193dc422b894dc09384759b270b2ece5facb59c2", - "size": "52518522" - }, - { - "host": "x86_64-pc-linux-gnu", - "url": "http://downloads.arduino.cc/gcc-arm-none-eabi-4.8.3-2014q1-linux64.tar.gz", - "archiveFileName": "gcc-arm-none-eabi-4.8.3-2014q1-linux64.tar.gz", - "checksum": "SHA-256:d23f6626148396d6ec42a5b4d928955a703e0757829195fa71a939e5b86eecf6", - "size": "51395093" - }, - { - "host": "i686-pc-linux-gnu", - "url": "http://downloads.arduino.cc/gcc-arm-none-eabi-4.8.3-2014q1-linux32.tar.gz", - "archiveFileName": "gcc-arm-none-eabi-4.8.3-2014q1-linux32.tar.gz", - "checksum": "SHA-256:ba1994235f69c526c564f65343f22ddbc9822b2ea8c5ee07dd79d89f6ace2498", - "size": "51029223" - } - ] - }, - { - "name": "arm-none-eabi-gcc", - "version": "7-2017q4", - "systems": [ - { - "host": "arm-linux-gnueabihf", - "url": "http://downloads.arduino.cc/tools/gcc-arm-none-eabi-7-2019-q4-major-linuxarm.tar.bz2", - "archiveFileName": "gcc-arm-none-eabi-7-2019-q4-major-linuxarm.tar.bz2", - "checksum": "SHA-256:34180943d95f759c66444a40b032f7dd9159a562670fc334f049567de140c51b", - "size": "96613739" - }, - { - "host": "aarch64-linux-gnu", - "url": "http://downloads.arduino.cc/tools/gcc-arm-none-eabi-7-2018-q2-update-linuxarm64.tar.bz2", - "archiveFileName": "gcc-arm-none-eabi-7-2018-q2-update-linuxarm64.tar.bz2", - "checksum": "SHA-256:6fb5752fb4d11012bd0a1ceb93a19d0641ff7cf29d289b3e6b86b99768e66f76", - "size": "99558726" - }, - { - "host": "i686-mingw32", - "url": "http://downloads.arduino.cc/tools/gcc-arm-none-eabi-7-2017-q4-major-win32-arduino1.zip", - "archiveFileName": "gcc-arm-none-eabi-7-2017-q4-major-win32-arduino1.zip", - "checksum": "SHA-256:96dd0091856f4d2eb21046eba571321feecf7d50b9c156f708b2a8b683903382", - "size": "131761924" - }, - { - "host": "x86_64-apple-darwin", - "url": "http://downloads.arduino.cc/tools/gcc-arm-none-eabi-7-2017-q4-major-mac.tar.bz2", - "archiveFileName": "gcc-arm-none-eabi-7-2017-q4-major-mac.tar.bz2", - "checksum": "SHA-256:89b776c7cf0591c810b5b60067e4dc113b5b71bc50084a536e71b894a97fdccb", - "size": "104550003" - }, - { - "host": "x86_64-pc-linux-gnu", - "url": "http://downloads.arduino.cc/tools/gcc-arm-none-eabi-7-2017-q4-major-linux64.tar.bz2", - "archiveFileName": "gcc-arm-none-eabi-7-2017-q4-major-linux64.tar.bz2", - "checksum": "SHA-256:96a029e2ae130a1210eaa69e309ea40463028eab18ba19c1086e4c2dafe69a6a", - "size": "99857645" - }, - { - "host": "i686-pc-linux-gnu", - "url": "http://downloads.arduino.cc/tools/gcc-arm-none-eabi-7-2018-q2-update-linux32.tar.bz2", - "archiveFileName": "gcc-arm-none-eabi-7-2018-q2-update-linux32.tar.bz2", - "checksum": "SHA-256:090a0bc2b1956bc49392dff924a6c30fa57c88130097b1972204d67a45ce3cf3", - "size": "97427309" - } - ] - }, - { - "name": "bossac", - "version": "1.3-arduino", - "systems": [ - { - "host": "i686-linux-gnu", - "url": "http://downloads.arduino.cc/tools/bossac-1.3a-arduino-i686-linux-gnu.tar.bz2", - "archiveFileName": "bossac-1.3a-arduino-i686-linux-gnu.tar.bz2", - "checksum": "SHA-256:d6d10362f40729a7877e43474fcf02ad82cf83321cc64ca931f5c82b2d25d24f", - "size": "147359" - }, - { - "host": "x86_64-pc-linux-gnu", - "url": "http://downloads.arduino.cc/tools/bossac-1.3a-arduino-x86_64-pc-linux-gnu.tar.bz2", - "archiveFileName": "bossac-1.3a-arduino-x86_64-pc-linux-gnu.tar.bz2", - "checksum": "SHA-256:c1daed033251296768fa8b63ad283e053da93427c0f3cd476a71a9188e18442c", - "size": "26179" - }, - { - "host": "i686-mingw32", - "url": "http://downloads.arduino.cc/tools/bossac-1.3a-arduino-i686-mingw32.tar.bz2", - "archiveFileName": "bossac-1.3a-arduino-i686-mingw32.tar.bz2", - "checksum": "SHA-256:a37727622e0f86cb4f2856ad0209568a5d804234dba3dc0778829730d61a5ec7", - "size": "265647" - }, - { - "host": "i386-apple-darwin11", - "url": "http://downloads.arduino.cc/tools/bossac-1.3a-arduino-i386-apple-darwin11.tar.bz2", - "archiveFileName": "bossac-1.3a-arduino-i386-apple-darwin11.tar.bz2", - "checksum": "SHA-256:40770b225753e7a52bb165e8f37e6b760364f5c5e96048168d0178945bd96ad6", - "size": "39475" - } - ] - }, - { - "name": "avr-gcc", - "version": "4.8.1-arduino2", - "systems": [ - { - "size": "24443285", - "checksum": "SHA-256:c19a7526235c364d7f62ec1a993d9b495973ba1813869ccf0241c65905896852", - "host": "i386-apple-darwin11", - "archiveFileName": "avr-gcc-4.8.1-arduino2-i386-apple-darwin11.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-4.8.1-arduino2-i386-apple-darwin11.tar.bz2" - }, - { - "size": "27152002", - "checksum": "SHA-256:24a931877bee5f36dc00a88877219a6d2f6a1fb7abb989fd04556b8432d2e14e", - "host": "x86_64-linux-gnu", - "archiveFileName": "avr-gcc-4.8.1-arduino2-x86_64-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-4.8.1-arduino2-x86_64-pc-linux-gnu.tar.bz2" - }, - { - "size": "25876628", - "checksum": "SHA-256:2d701b4efbc8cec62dc299cde01730c5eebcf23d7e4393db8cf7744a9bf1d3de", - "host": "i686-linux-gnu", - "archiveFileName": "avr-gcc-4.8.1-arduino2-i686-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-4.8.1-arduino2-i686-pc-linux-gnu.tar.bz2" - }, - { - "size": "46046691", - "checksum": "SHA-256:2eafb49fb803fa4d2c32d35e24c0b372fcd520ca0a790fa537a847179e382000", - "host": "i686-mingw32", - "archiveFileName": "avr-gcc-4.8.1-arduino2-i686-mingw32.zip", - "url": "http://downloads.arduino.cc/tools/avr-gcc-4.8.1-arduino2-i686-mingw32.zip" - } - ] - }, - { - "name": "avrdude", - "version": "6.0.1-arduino2", - "systems": [ - { - "size": "264965", - "checksum": "SHA-256:71117cce0096dad6c091e2c34eb0b9a3386d3aec7d863d2da733d9e5eac3a6b1", - "host": "i386-apple-darwin11", - "archiveFileName": "avrdude-6.0.1-arduino2-i386-apple-darwin11.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.0.1-arduino2-i386-apple-darwin11.tar.bz2" - }, - { - "size": "292541", - "checksum": "SHA-256:2489004d1d98177eaf69796760451f89224007c98b39ebb5577a9a34f51425f1", - "host": "x86_64-linux-gnu", - "archiveFileName": "avrdude-6.0.1-arduino2-x86_64-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.0.1-arduino2-x86_64-pc-linux-gnu.tar.bz2" - }, - { - "size": "283209", - "checksum": "SHA-256:6f633dd6270ad0d9ef19507bcbf8697b414a15208e4c0f71deec25ef89cdef3f", - "host": "i686-linux-gnu", - "archiveFileName": "avrdude-6.0.1-arduino2-i686-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.0.1-arduino2-i686-pc-linux-gnu.tar.bz2" - }, - { - "size": "241618", - "checksum": "SHA-256:6c5483800ba753c80893607e30cade8ab77b182808fcc5ea15fa3019c63d76ae", - "host": "i686-mingw32", - "archiveFileName": "avrdude-6.0.1-arduino2-i686-mingw32.zip", - "url": "http://downloads.arduino.cc/tools/avrdude-6.0.1-arduino2-i686-mingw32.zip" - } - ] - }, - { - "name": "avr-gcc", - "version": "4.8.1-arduino3", - "systems": [ - { - "size": "24447175", - "checksum": "SHA-256:28e207c66b3dc405367d0c5e68ce3c278e5ec3abb0e4974e7927fe0f9a532c40", - "host": "i386-apple-darwin11", - "archiveFileName": "avr-gcc-4.8.1-arduino3-i386-apple-darwin11.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-4.8.1-arduino3-i386-apple-darwin11.tar.bz2" - }, - { - "size": "30556996", - "checksum": "SHA-256:028340abec6eb3085b82404dfc7ed143e1bb05b2da961b539ddcdba4a6f65533", - "host": "x86_64-linux-gnu", - "archiveFileName": "avr-gcc-4.8.1-arduino3-x86_64-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-4.8.1-arduino3-x86_64-pc-linux-gnu.tar.bz2" - }, - { - "size": "28768022", - "checksum": "SHA-256:37796548ba9653267568f959cd8c7ebfe5b4bce4599898cf9f876d64e616cb87", - "host": "i686-linux-gnu", - "archiveFileName": "avr-gcc-4.8.1-arduino3-i686-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-4.8.1-arduino3-i686-pc-linux-gnu.tar.bz2" - }, - { - "size": "46046917", - "checksum": "SHA-256:d6f0527793f9800f060408392a99eb290ed205730edbae43a1a25cbf6b6b588f", - "host": "i686-mingw32", - "archiveFileName": "avr-gcc-4.8.1-arduino3-i686-mingw32.zip", - "url": "http://downloads.arduino.cc/tools/avr-gcc-4.8.1-arduino3-i686-mingw32.zip" - } - ] - }, - { - "name": "avrdude", - "version": "6.0.1-arduino3", - "systems": [ - { - "size": "264682", - "checksum": "SHA-256:df7cd4a76e45ab3767eb964f845f4d5e9d643df950ec32812923da1e9843d072", - "host": "i386-apple-darwin11", - "archiveFileName": "avrdude-6.0.1-arduino3-i386-apple-darwin11.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.0.1-arduino3-i386-apple-darwin11.tar.bz2" - }, - { - "size": "748634", - "checksum": "SHA-256:bb7bff48f20a68e1fe559c3f3f644574df12ab5c98eb6a1491079f3c760434ad", - "host": "x86_64-linux-gnu", - "archiveFileName": "avrdude-6.0.1-arduino3-x86_64-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.0.1-arduino3-x86_64-pc-linux-gnu.tar.bz2" - }, - { - "size": "495482", - "checksum": "SHA-256:96a0cfb83fe0452366159e3bf4e19ff10906a8957d1feafd3d98b49ab4b14405", - "host": "i686-linux-gnu", - "archiveFileName": "avrdude-6.0.1-arduino3-i686-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.0.1-arduino3-i686-pc-linux-gnu.tar.bz2" - }, - { - "size": "241619", - "checksum": "SHA-256:ea59bfc2ee85039c85318b2ba52c47ef0573513444a785b72f59b22586a950f9", - "host": "i686-mingw32", - "archiveFileName": "avrdude-6.0.1-arduino3-i686-mingw32.zip", - "url": "http://downloads.arduino.cc/tools/avrdude-6.0.1-arduino3-i686-mingw32.zip" - } - ] - }, - { - "name": "avr-gcc", - "version": "4.8.1-arduino5", - "systems": [ - { - "size": "24403768", - "checksum": "SHA-256:c8ffcd2db7a651b48ab4ea19db4b34fbae3e7f0210a0f294592af2bdabf2154b", - "host": "arm-linux-gnueabihf", - "archiveFileName": "avr-gcc-4.8.1-arduino5-armhf-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-4.8.1-arduino5-armhf-pc-linux-gnu.tar.bz2" - }, - { - "size": "24437400", - "checksum": "SHA-256:111b3ef00d737d069eb237a8933406cbb928e4698689e24663cffef07688a901", - "host": "i386-apple-darwin11", - "archiveFileName": "avr-gcc-4.8.1-arduino5-i386-apple-darwin11.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-4.8.1-arduino5-i386-apple-darwin11.tar.bz2" - }, - { - "size": "27093036", - "checksum": "SHA-256:9054fcc174397a419ba56c4ce1bfcbcad275a6a080cc144905acc9b0351ee9cc", - "host": "x86_64-linux-gnu", - "archiveFileName": "avr-gcc-4.8.1-arduino5-x86_64-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-4.8.1-arduino5-x86_64-pc-linux-gnu.tar.bz2" - }, - { - "size": "25882375", - "checksum": "SHA-256:7648b7f549b37191da0b0be53bae791b652f82ac3cb4e7877f85075aaf32141f", - "host": "i686-linux-gnu", - "archiveFileName": "avr-gcc-4.8.1-arduino5-i686-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-4.8.1-arduino5-i686-pc-linux-gnu.tar.bz2" - }, - { - "size": "46044779", - "checksum": "SHA-256:d4303226a7b41d3c445d901b5aa5903458def3fc7b7ff4ffef37cabeb37d424d", - "host": "i686-mingw32", - "archiveFileName": "avr-gcc-4.8.1-arduino5-i686-mingw32.zip", - "url": "http://downloads.arduino.cc/tools/avr-gcc-4.8.1-arduino5-i686-mingw32.zip" - } - ] - }, - { - "name": "avrdude", - "version": "6.0.1-arduino5", - "systems": [ - { - "size": "267095", - "checksum": "SHA-256:23ea1341dbc117ec067f2eb1a498ad2bdd7d11fff0143c00b2e018c39804f6b4", - "host": "arm-linux-gnueabihf", - "archiveFileName": "avrdude-6.0.1-arduino5-armhf-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.0.1-arduino5-armhf-pc-linux-gnu.tar.bz2" - }, - { - "size": "264894", - "checksum": "SHA-256:41af8d3b0a586853c8317b4fb5163ca0db594a1870ddf680fd988c42166fc3e5", - "host": "i386-apple-darwin11", - "archiveFileName": "avrdude-6.0.1-arduino5-i386-apple-darwin11.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.0.1-arduino5-i386-apple-darwin11.tar.bz2" - }, - { - "size": "292629", - "checksum": "SHA-256:d826cca7383461f7e8adde686372cf900e9cb3afd639555cf2d6c645b283a476", - "host": "x86_64-linux-gnu", - "archiveFileName": "avrdude-6.0.1-arduino5-x86_64-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.0.1-arduino5-x86_64-pc-linux-gnu.tar.bz2" - }, - { - "size": "283121", - "checksum": "SHA-256:5933d66927bce46ababa9b68a8b7f1d53f68c4f3ff7a5ce4b85d7cf4e6c6bfee", - "host": "i686-linux-gnu", - "archiveFileName": "avrdude-6.0.1-arduino5-i686-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.0.1-arduino5-i686-pc-linux-gnu.tar.bz2" - }, - { - "size": "241634", - "checksum": "SHA-256:41f667f1f6a0ab8df46b4ffacd023176dcdef331d6db3b74bddd37d18cca0a44", - "host": "i686-mingw32", - "archiveFileName": "avrdude-6.0.1-arduino5-i686-mingw32.zip", - "url": "http://downloads.arduino.cc/tools/avrdude-6.0.1-arduino5-i686-mingw32.zip" - } - ] - }, - { - "name": "avr-gcc", - "version": "4.9.2-atmel3.5.3-arduino", - "systems": [ - { - "size": "27046965", - "checksum": "SHA-256:adeee70be27cc3ee0e4b9e844610d9c534c7b21dae24ec3fa49808c2f04958de", - "host": "i386-apple-darwin11", - "archiveFileName": "avr-gcc-4.9.2-atmel3.5.3-arduino-i386-apple-darwin11.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-4.9.2-atmel3.5.3-arduino-i386-apple-darwin11.tar.bz2" - }, - { - "size": "27400001", - "checksum": "SHA-256:02dba9ee77694c23a4c304416a3808949c8faedf07f25a225a4189d850615ec6", - "host": "arm-linux-gnueabihf", - "archiveFileName": "avr-gcc-4.9.2-atmel3.5.3-arduino-armhf-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-4.9.2-atmel3.5.3-arduino-armhf-pc-linux-gnu.tar.bz2" - }, - { - "size": "29904544", - "checksum": "SHA-256:0711e885c0430859e7fea3831af8c69a0c25f92a90ecfda9281799a0acec7455", - "host": "x86_64-linux-gnu", - "archiveFileName": "avr-gcc-4.9.2-atmel3.5.3-arduino-x86_64-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-4.9.2-atmel3.5.3-arduino-x86_64-pc-linux-gnu.tar.bz2" - }, - { - "size": "29077606", - "checksum": "SHA-256:fe0bb1d6369694779ceb671d457ccadbeafe855a11f6746b7db20055cea4df33", - "host": "i686-linux-gnu", - "archiveFileName": "avr-gcc-4.9.2-atmel3.5.3-arduino-i686-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-4.9.2-atmel3.5.3-arduino-i686-pc-linux-gnu.tar.bz2" - }, - { - "size": "43847566", - "checksum": "SHA-256:445ce3117e87be7e196809fbbea373976160689b6d4b43dbf185eb4c914d1469", - "host": "i686-mingw32", - "archiveFileName": "avr-gcc-4.9.2-atmel3.5.3-arduino-i686-mingw32.zip", - "url": "http://downloads.arduino.cc/tools/avr-gcc-4.9.2-atmel3.5.3-arduino-i686-mingw32.zip" - } - ] - }, - { - "name": "avr-gcc", - "version": "4.9.2-atmel3.5.3-arduino2", - "systems": [ - { - "size": "27400889", - "checksum": "SHA-256:77f300d519bc6b9a25df17b36cb303218e9a258c059b2f6bff8f71a0d8f96821", - "host": "arm-linux-gnueabihf", - "archiveFileName": "avr-gcc-4.9.2-atmel3.5.3-arduino2-armhf-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-4.9.2-atmel3.5.3-arduino2-armhf-pc-linux-gnu.tar.bz2" - }, - { - "size": "27048070", - "checksum": "SHA-256:311258af188defe24a4b341e4e1f4dc93ca6c80516d3e3b55a2fc07a7050248b", - "host": "i386-apple-darwin11", - "archiveFileName": "avr-gcc-4.9.2-atmel3.5.3-arduino2-i386-apple-darwin11.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-4.9.2-atmel3.5.3-arduino2-i386-apple-darwin11.tar.bz2" - }, - { - "size": "43847945", - "checksum": "SHA-256:f8e6ede8746c70be01ec79a30803277cd94360cc5b2e104762da0fbcf536fcc6", - "host": "i686-mingw32", - "archiveFileName": "avr-gcc-4.9.2-atmel3.5.3-arduino2-i686-mingw32.zip", - "url": "http://downloads.arduino.cc/tools/avr-gcc-4.9.2-atmel3.5.3-arduino2-i686-mingw32.zip" - }, - { - "size": "29292729", - "checksum": "SHA-256:f108951e7c4dc90926d1fc76cc27549f6ea63c702a2bb7ff39647a19ae86ec68", - "host": "i686-linux-gnu", - "archiveFileName": "avr-gcc-4.9.2-atmel3.5.3-arduino2-i686-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-4.9.2-atmel3.5.3-arduino2-i686-pc-linux-gnu.tar.bz2" - }, - { - "size": "29882960", - "checksum": "SHA-256:3903a6d1bb9fdd91727e504b5993d5501f119bcb7f99f7aee98a2101e5629188", - "host": "x86_64-linux-gnu", - "archiveFileName": "avr-gcc-4.9.2-atmel3.5.3-arduino2-x86_64-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-4.9.2-atmel3.5.3-arduino2-x86_64-pc-linux-gnu.tar.bz2" - } - ] - }, - { - "name": "avr-gcc", - "version": "4.9.2-atmel3.5.4-arduino2", - "systems": [ - { - "size": "27764772", - "checksum": "SHA-256:ee36009e19bd238d1f6351cbc9aa5db69714761f67dec4c1d69d5d5d7758720c", - "host": "arm-linux-gnueabihf", - "archiveFileName": "avr-gcc-4.9.2-atmel3.5.4-arduino2-armhf-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-4.9.2-atmel3.5.4-arduino2-armhf-pc-linux-gnu.tar.bz2" - }, - { - "size": "28574644", - "checksum": "SHA-256:67b3ed3555eacf0b4fc6f62240773b9f0220171fe4de26bb8d711547fc884730", - "host": "i386-apple-darwin11", - "archiveFileName": "avr-gcc-4.9.2-atmel3.5.4-arduino2-i386-apple-darwin11.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-4.9.2-atmel3.5.4-arduino2-i386-apple-darwin11.tar.bz2" - }, - { - "size": "44386446", - "checksum": "SHA-256:6044551cd729d88ea6ffcccf10aad1934c5b164d61f4f5890b0e78524ffff853", - "host": "i686-mingw32", - "archiveFileName": "avr-gcc-4.9.2-atmel3.5.4-arduino2-i686-mingw32.zip", - "url": "http://downloads.arduino.cc/tools/avr-gcc-4.9.2-atmel3.5.4-arduino2-i686-mingw32.zip" - }, - { - "size": "29723974", - "checksum": "SHA-256:63a9d4cebbac06fd5fa8f48a2e2ba7d513837dcddc97f560129b4e466af901b5", - "host": "i686-linux-gnu", - "archiveFileName": "avr-gcc-4.9.2-atmel3.5.4-arduino2-i686-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-4.9.2-atmel3.5.4-arduino2-i686-pc-linux-gnu.tar.bz2" - }, - { - "size": "30374404", - "checksum": "SHA-256:19480217f1524d78467b83cd742f503182bbcc76b5440093261f146828aa588c", - "host": "x86_64-linux-gnu", - "archiveFileName": "avr-gcc-4.9.2-atmel3.5.4-arduino2-x86_64-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-4.9.2-atmel3.5.4-arduino2-x86_64-pc-linux-gnu.tar.bz2" - } - ] - }, - { - "name": "avr-gcc", - "version": "5.4.0-atmel3.6.1-arduino2", - "systems": [ - { - "size": "31449123", - "checksum": "SHA-256:6741f95cc3182a8729cf9670eb13d8dc5a19e881639ca61e53a2d78346a4e99f", - "host": "arm-linux-gnueabihf", - "archiveFileName": "avr-gcc-5.4.0-atmel3.6.1-arduino2-armhf-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-5.4.0-atmel3.6.1-arduino2-armhf-pc-linux-gnu.tar.bz2" - }, - { - "size": "33141295", - "checksum": "SHA-256:0fa9e4f2d6d09782dbc84dd91a302849cde2f192163cb9f29484c5f32785269a", - "host": "aarch64-linux-gnu", - "archiveFileName": "avr-gcc-5.4.0-atmel3.6.1-arduino2-aarch64-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-5.4.0-atmel3.6.1-arduino2-aarch64-pc-linux-gnu.tar.bz2" - }, - { - "size": "31894498", - "checksum": "SHA-256:abc50137543ba73e227b4d1b8510fff50a474bacd24f2c794f852904963849f8", - "host": "i386-apple-darwin11", - "archiveFileName": "avr-gcc-5.4.0-atmel3.6.1-arduino2-i386-apple-darwin11.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-5.4.0-atmel3.6.1-arduino2-i386-apple-darwin11.tar.bz2" - }, - { - "size": "45923772", - "checksum": "SHA-256:7eb5691a379b547798fae535b05d68bc02d3969f12d051b8a5a5f2f350ab0a7f", - "host": "i686-mingw32", - "archiveFileName": "avr-gcc-5.4.0-atmel3.6.1-arduino2-i686-w64-mingw32.zip", - "url": "http://downloads.arduino.cc/tools/avr-gcc-5.4.0-atmel3.6.1-arduino2-i686-w64-mingw32.zip" - }, - { - "size": "33022916", - "checksum": "SHA-256:51f87e04f3cdaa73565c751051ac118e02904ad8478f1475b300e1bffcd5538f", - "host": "i686-linux-gnu", - "archiveFileName": "avr-gcc-5.4.0-atmel3.6.1-arduino2-i686-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-5.4.0-atmel3.6.1-arduino2-i686-pc-linux-gnu.tar.bz2" - }, - { - "size": "33522375", - "checksum": "SHA-256:05422b0d73b10357c12ea938f02cf50529422b89a4722756e70024aed3e69185", - "host": "x86_64-linux-gnu", - "archiveFileName": "avr-gcc-5.4.0-atmel3.6.1-arduino2-x86_64-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-5.4.0-atmel3.6.1-arduino2-x86_64-pc-linux-gnu.tar.bz2" - } - ] - }, - { - "name": "avr-gcc", - "version": "7.3.0-atmel3.6.1-arduino5", - "systems": [ - { - "size": "34462042", - "checksum": "SHA-256:f4acd5531c6b82c715e2edfa0aadb13fb718b4095b3ea1aa1f7fbde680069639", - "host": "arm-linux-gnueabihf", - "archiveFileName": "avr-gcc-7.3.0-atmel3.6.1-arduino5-arm-linux-gnueabihf.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-7.3.0-atmel3.6.1-arduino5-arm-linux-gnueabihf.tar.bz2" - }, - { - "size": "39381972", - "checksum": "SHA-256:dd9c70190be370a44fb47dab1514de6d8852b861dfa527964b65c740d8d50c10", - "host": "aarch64-linux-gnu", - "archiveFileName": "avr-gcc-7.3.0-atmel3.6.1-arduino5-aarch64-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-7.3.0-atmel3.6.1-arduino5-aarch64-pc-linux-gnu.tar.bz2" - }, - { - "size": "38492678", - "checksum": "SHA-256:f48706317f04452544ab90e75bd1bb193f8af2cb1002f53aa702f27202c1b38f", - "host": "x86_64-apple-darwin14", - "archiveFileName": "avr-gcc-7.3.0-atmel3.6.1-arduino5-x86_64-apple-darwin14.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-7.3.0-atmel3.6.1-arduino5-x86_64-apple-darwin14.tar.bz2" - }, - { - "size": "53727984", - "checksum": "SHA-256:6d4a5d089a36e5b5252befc73da204555b49e376ce7577ee19ca7f028b295830", - "host": "i686-mingw32", - "archiveFileName": "avr-gcc-7.3.0-atmel3.6.1-arduino5-i686-w64-mingw32.zip", - "url": "http://downloads.arduino.cc/tools/avr-gcc-7.3.0-atmel3.6.1-arduino5-i686-w64-mingw32.zip" - }, - { - "size": "38710087", - "checksum": "SHA-256:2ff12739d7ed09688d6b3c2c126e8df69b5bda1a07ab558799f0e576571e0e1d", - "host": "i686-linux-gnu", - "archiveFileName": "avr-gcc-7.3.0-atmel3.6.1-arduino5-i686-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-7.3.0-atmel3.6.1-arduino5-i686-pc-linux-gnu.tar.bz2" - }, - { - "size": "39114120", - "checksum": "SHA-256:3effed8ffa1978b6e4a46f1aa2acc629e440b4d77244f71f9b79a916025206fb", - "host": "x86_64-linux-gnu", - "archiveFileName": "avr-gcc-7.3.0-atmel3.6.1-arduino5-x86_64-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-7.3.0-atmel3.6.1-arduino5-x86_64-pc-linux-gnu.tar.bz2" - } - ] - }, - { - "name": "avr-gcc", - "version": "7.3.0-atmel3.6.1-arduino7", - "systems": [ - { - "size": "34683056", - "checksum": "SHA-256:3903553d035da59e33cff9941b857c3cb379cb0638105dfdf69c97f0acc8e7b5", - "host": "arm-linux-gnueabihf", - "archiveFileName": "avr-gcc-7.3.0-atmel3.6.1-arduino7-arm-linux-gnueabihf.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-7.3.0-atmel3.6.1-arduino7-arm-linux-gnueabihf.tar.bz2" - }, - { - "size": "38045723", - "checksum": "SHA-256:03d322b9df6da17289e9e7c6233c34a8535d9c645c19efc772ba19e56914f339", - "host": "aarch64-linux-gnu", - "archiveFileName": "avr-gcc-7.3.0-atmel3.6.1-arduino7-aarch64-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-7.3.0-atmel3.6.1-arduino7-aarch64-pc-linux-gnu.tar.bz2" - }, - { - "size": "36684546", - "checksum": "SHA-256:f6ed2346953fcf88df223469088633eb86de997fa27ece117fd1ef170d69c1f8", - "host": "x86_64-apple-darwin14", - "archiveFileName": "avr-gcc-7.3.0-atmel3.6.1-arduino7-x86_64-apple-darwin14.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-7.3.0-atmel3.6.1-arduino7-x86_64-apple-darwin14.tar.bz2" - }, - { - "size": "52519412", - "checksum": "SHA-256:a54f64755fff4cb792a1495e5defdd789902a2a3503982e81b898299cf39800e", - "host": "i686-mingw32", - "archiveFileName": "avr-gcc-7.3.0-atmel3.6.1-arduino7-i686-w64-mingw32.zip", - "url": "http://downloads.arduino.cc/tools/avr-gcc-7.3.0-atmel3.6.1-arduino7-i686-w64-mingw32.zip" - }, - { - "size": "37176991", - "checksum": "SHA-256:954bbffb33545bcdcd473af993da2980bf32e8461ff55a18e0eebc7b2ef69a4c", - "host": "i686-linux-gnu", - "archiveFileName": "avr-gcc-7.3.0-atmel3.6.1-arduino7-i686-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-7.3.0-atmel3.6.1-arduino7-i686-pc-linux-gnu.tar.bz2" - }, - { - "size": "37630618", - "checksum": "SHA-256:bd8c37f6952a2130ac9ee32c53f6a660feb79bee8353c8e289eb60fdcefed91e", - "host": "x86_64-linux-gnu", - "archiveFileName": "avr-gcc-7.3.0-atmel3.6.1-arduino7-x86_64-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avr-gcc-7.3.0-atmel3.6.1-arduino7-x86_64-pc-linux-gnu.tar.bz2" - } - ] - }, - { - "name": "avrdude", - "version": "6.3.0-arduino2", - "systems": [ - { - "size": "643484", - "checksum": "SHA-256:26af86137d8a872f64d217cb262734860b36fe26d6d34faf72e951042f187885", - "host": "arm-linux-gnueabihf", - "archiveFileName": "avrdude-6.3.0-arduino2-armhf-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino2-armhf-pc-linux-gnu.tar.bz2" - }, - { - "size": "653968", - "checksum": "SHA-256:32525ea3696c861030e1a6006a5f11971d1dad331e45bfa68dac35126476b04f", - "host": "i386-apple-darwin11", - "archiveFileName": "avrdude-6.3.0-arduino2-i386-apple-darwin11.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino2-i386-apple-darwin11.tar.bz2" - }, - { - "size": "745081", - "checksum": "SHA-256:9635af5a35bdca11804c07582d7beec458140fb6e3308168c3deda18dc6790fa", - "host": "x86_64-linux-gnu", - "archiveFileName": "avrdude-6.3.0-arduino2-x86_64-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino2-x86_64-pc-linux-gnu.tar.bz2" - }, - { - "size": "731802", - "checksum": "SHA-256:790b6cb610c48e73a2a0f65dcee9903d2fd7f1b0a1f75008a9a21f50a60c7251", - "host": "i686-linux-gnu", - "archiveFileName": "avrdude-6.3.0-arduino2-i686-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino2-i686-pc-linux-gnu.tar.bz2" - }, - { - "size": "608496", - "checksum": "SHA-256:8eaf98ea41fbd4450483488ef31710cbcc43c0412dbc8e1e1b582feaab6eca30", - "host": "i686-mingw32", - "archiveFileName": "avrdude-6.3.0-arduino2-i686-w64-mingw32.zip", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino2-i686-w64-mingw32.zip" - } - ] - }, - { - "name": "avrdude", - "version": "6.3.0-arduino6", - "systems": [ - { - "size": "644600", - "checksum": "SHA-256:2426207423d58eb0e5fc4df9493418f1cb54ba3f328fdc7c3bb582f920b9cbe7", - "host": "arm-linux-gnueabihf", - "archiveFileName": "avrdude-6.3.0-arduino6-armhf-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino6-armhf-pc-linux-gnu.tar.bz2" - }, - { - "size": "696273", - "checksum": "SHA-256:d9a039c9e92d3dbb2011e75e6c044a1a4a2789e2fbf8386b1d580994811be084", - "host": "i386-apple-darwin11", - "archiveFileName": "avrdude-6.3.0-arduino6-i386-apple-darwin11.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino6-i386-apple-darwin11.tar.bz2" - }, - { - "size": "746653", - "checksum": "SHA-256:97b4875cad6110c70101bb776f3ac37b64a2e73f036cd0b10afb6f4be96a6621", - "host": "x86_64-linux-gnu", - "archiveFileName": "avrdude-6.3.0-arduino6-x86_64-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino6-x86_64-pc-linux-gnu.tar.bz2" - }, - { - "size": "733127", - "checksum": "SHA-256:5f4bc4b0957b1d34cec9908b7f84a7c297b894b39fe16a4992c284b24c00d6fb", - "host": "i686-linux-gnu", - "archiveFileName": "avrdude-6.3.0-arduino6-i686-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino6-i686-pc-linux-gnu.tar.bz2" - }, - { - "size": "645859", - "checksum": "SHA-256:7468a1bcdfa459d175a095b102c0de28efc466accfb104305fbcad7832659ddc", - "host": "i686-mingw32", - "archiveFileName": "avrdude-6.3.0-arduino6-i686-w64-mingw32.zip", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino6-i686-w64-mingw32.zip" - } - ] - }, - { - "name": "avrdude", - "version": "6.3.0-arduino8", - "systems": [ - { - "size": "644550", - "checksum": "SHA-256:25a6834ae48019fccf37024236a1f79fe21760414292a4f3fa058d937ceee1ce", - "host": "arm-linux-gnueabihf", - "archiveFileName": "avrdude-6.3.0-arduino8-armhf-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino8-armhf-pc-linux-gnu.tar.bz2" - }, - { - "size": "697268", - "checksum": "SHA-256:be8a33a7ec01bb7123279466ffa31371e0aa4fccefffcc23ce71810b59531947", - "host": "i386-apple-darwin11", - "archiveFileName": "avrdude-6.3.0-arduino8-i386-apple-darwin11.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino8-i386-apple-darwin11.tar.bz2" - }, - { - "size": "711544", - "checksum": "SHA-256:85f38d02e2398d3b7f93da2ca8b830ee65bb73f66cc7a7b30c466d3cebf2da6e", - "host": "x86_64-linux-gnu", - "archiveFileName": "avrdude-6.3.0-arduino8-x86_64-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino8-x86_64-pc-linux-gnu.tar.bz2" - }, - { - "size": "701718", - "checksum": "SHA-256:8e2e4bc71d22e9d11ed143763b97f3aa2d164cdeee678a9deaf5b36e245b2d20", - "host": "i686-linux-gnu", - "archiveFileName": "avrdude-6.3.0-arduino8-i686-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino8-i686-pc-linux-gnu.tar.bz2" - }, - { - "size": "645996", - "checksum": "SHA-256:3a7592f6c33efd658b820c73d1058d3c868a297cbddb37da5644973c3b516d5e", - "host": "i686-mingw32", - "archiveFileName": "avrdude-6.3.0-arduino8-i686-w64-mingw32.zip", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino8-i686-w64-mingw32.zip" - } - ] - }, - { - "name": "avrdude", - "version": "6.3.0-arduino9", - "systems": [ - { - "size": "644550", - "checksum": "SHA-256:25a6834ae48019fccf37024236a1f79fe21760414292a4f3fa058d937ceee1ce", - "host": "arm-linux-gnueabihf", - "archiveFileName": "avrdude-6.3.0-arduino9-armhf-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino9-armhf-pc-linux-gnu.tar.bz2" - }, - { - "size": "697309", - "checksum": "SHA-256:bfa06bc042dff252d3a8eded98da159484e75b46d2697da4d9446dcd2aea8465", - "host": "i386-apple-darwin11", - "archiveFileName": "avrdude-6.3.0-arduino9-i386-apple-darwin11.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino9-i386-apple-darwin11.tar.bz2" - }, - { - "size": "711229", - "checksum": "SHA-256:c8cccb84e2fe49ee837b24f0a60a99e9c371dae26e84c5b0b22b6b6aab2f1f6a", - "host": "x86_64-linux-gnu", - "archiveFileName": "avrdude-6.3.0-arduino9-x86_64-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino9-x86_64-pc-linux-gnu.tar.bz2" - }, - { - "size": "701590", - "checksum": "SHA-256:4235a2d58e3c3224c603d6c5f0610507ed6c48ebf4051fdcce9f77a7646e218b", - "host": "i686-linux-gnu", - "archiveFileName": "avrdude-6.3.0-arduino9-i686-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino9-i686-pc-linux-gnu.tar.bz2" - }, - { - "size": "645974", - "checksum": "SHA-256:f3c5cfa8d0b3b0caee81c5b35fb6acff89c342ef609bf4266734c6266a256d4f", - "host": "i686-mingw32", - "archiveFileName": "avrdude-6.3.0-arduino9-i686-w64-mingw32.zip", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino9-i686-w64-mingw32.zip" - } - ] - }, - { - "name": "avrdude", - "version": "6.3.0-arduino14", - "systems": [ - { - "size": "219616", - "checksum": "SHA-256:d1a06275490d59a431c419788bbc53ffd5a79510dac1a35e63cf488621ba5589", - "host": "arm-linux-gnueabihf", - "archiveFileName": "avrdude-6.3.0-arduino14-armhf-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino14-armhf-pc-linux-gnu.tar.bz2" - }, - { - "size": "229688", - "checksum": "SHA-256:439f5de150695e3732dd598bb182dae6ec1e3a5cdb580f855d9b58e485e84e66", - "host": "aarch64-linux-gnu", - "archiveFileName": "avrdude-6.3.0-arduino14-aarch64-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino14-aarch64-pc-linux-gnu.tar.bz2" - }, - { - "size": "256917", - "checksum": "SHA-256:47d03991522722ce92120c60c4118685b7861909d895f34575001137961e4a63", - "host": "i386-apple-darwin11", - "archiveFileName": "avrdude-6.3.0-arduino14-i386-apple-darwin12.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino14-i386-apple-darwin11.tar.bz2" - }, - { - "size": "253366", - "checksum": "SHA-256:7986e8f3059353dc08f9234f7dbc98d9b2fa2242f046f02a8243a060f7358bfc", - "host": "x86_64-linux-gnu", - "archiveFileName": "avrdude-6.3.0-arduino14-x86_64-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino14-x86_64-pc-linux-gnu.tar.bz2" - }, - { - "size": "244293", - "checksum": "SHA-256:4f100e3843c635064997df91d2a079ab15cd30d1d7fa227280abe6a7c3bc74ca", - "host": "i686-linux-gnu", - "archiveFileName": "avrdude-6.3.0-arduino14-i686-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino14-i686-pc-linux-gnu.tar.bz2" - }, - { - "size": "328363", - "checksum": "SHA-256:69293e0de2eff8de89f553477795c25005f674a320bbba4b0222beb0194aa297", - "host": "i686-mingw32", - "archiveFileName": "avrdude-6.3.0-arduino14-i686-w64-mingw32.zip", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino14-i686-w64-mingw32.zip" - } - ] - }, - { - "name": "avrdude", - "version": "6.3.0-arduino16", - "systems": [ - { - "size": "219642", - "checksum": "SHA-256:6fc443445440f0e2d95d70013ed075bceffc2a1babc1e7d4f1ae69c3fe268c57", - "host": "arm-linux-gnueabihf", - "archiveFileName": "avrdude-6.3.0-arduino16-armhf-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino16-armhf-pc-linux-gnu.tar.bz2" - }, - { - "size": "229997", - "checksum": "SHA-256:7a2726ab2fd18b910abc3d9dd33c4b40d18c34cf12c46f3367932e7fd87c0197", - "host": "aarch64-linux-gnu", - "archiveFileName": "avrdude-6.3.0-arduino16-aarch64-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino16-aarch64-pc-linux-gnu.tar.bz2" - }, - { - "size": "279172", - "checksum": "SHA-256:f93dc12a4b30a335ab24b3c628e6cad0ebf2f8adfb7ef50f87c0fc17165b2156", - "host": "x86_64-apple-darwin15", - "archiveFileName": "avrdude-6.3.0-arduino16-i386-apple-darwin11.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino16-i386-apple-darwin11.tar.bz2" - }, - { - "size": "254085", - "checksum": "SHA-256:57856d6e388d333d924afa3e2d5525161dbe0dc670e7caae2720e249606175a7", - "host": "x86_64-linux-gnu", - "archiveFileName": "avrdude-6.3.0-arduino16-x86_64-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino16-x86_64-pc-linux-gnu.tar.bz2" - }, - { - "size": "244393", - "checksum": "SHA-256:bdf73358991243a9a8de11a42c80c4ec4b14c82f2222cb0c3c181f62656c41fb", - "host": "i686-linux-gnu", - "archiveFileName": "avrdude-6.3.0-arduino16-i686-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino16-i686-pc-linux-gnu.tar.bz2" - }, - { - "size": "328456", - "checksum": "SHA-256:781c16a8bf813fa68fc0f47d427279053c9e098c3aed7165449ac4f0137304dd", - "host": "i686-mingw32", - "archiveFileName": "avrdude-6.3.0-arduino16-i686-w64-mingw32.zip", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino16-i686-w64-mingw32.zip" - } - ] - }, - { - "name": "avrdude", - "version": "6.3.0-arduino17", - "systems": [ - { - "size": "219631", - "checksum": "SHA-256:2a8e68c5d803aa6f902ef219f177ec3a4c28275d85cbe272962ad2cd374f50d1", - "host": "arm-linux-gnueabihf", - "archiveFileName": "avrdude-6.3.0-arduino17-armhf-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino17-armhf-pc-linux-gnu.tar.bz2" - }, - { - "size": "229852", - "checksum": "SHA-256:6cf948f751acfe7b96684537f2291c766ec8b54b4f7dc95539864821456fa9fc", - "host": "aarch64-linux-gnu", - "archiveFileName": "avrdude-6.3.0-arduino17-aarch64-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino17-aarch64-pc-linux-gnu.tar.bz2" - }, - { - "size": "279045", - "checksum": "SHA-256:120cc9edaae699e7e9ac50b1b8eb0e7d51fdfa555bac54233c2511e6ee5418c9", - "host": "x86_64-apple-darwin12", - "archiveFileName": "avrdude-6.3.0-arduino17-x86_64-apple-darwin12.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino17-x86_64-apple-darwin12.tar.bz2" - }, - { - "size": "254271", - "checksum": "SHA-256:accdfb920af2aabf4f7461d2ac73c0751760f525216dc4e7657427a78c60d13d", - "host": "x86_64-linux-gnu", - "archiveFileName": "avrdude-6.3.0-arduino17-x86_64-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino17-x86_64-pc-linux-gnu.tar.bz2" - }, - { - "size": "244550", - "checksum": "SHA-256:5c8cc6c17db9300e1451fe41cd7178b0442b4490ee6fdbc0aed9811aef96c05f", - "host": "i686-linux-gnu", - "archiveFileName": "avrdude-6.3.0-arduino17-i686-pc-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino17-i686-pc-linux-gnu.tar.bz2" - }, - { - "size": "328460", - "checksum": "SHA-256:e99188873c7c5ad8f8f906f068c33600e758b2e36cce3adbd518a21bd266749d", - "host": "i686-mingw32", - "archiveFileName": "avrdude-6.3.0-arduino17-i686-w64-mingw32.zip", - "url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino17-i686-w64-mingw32.zip" - } - ] - }, - { - "name": "arduinoOTA", - "version": "1.0.0", - "systems": [ - { - "size": "2044124", - "checksum": "SHA-256:850a86876403cb45c944590a8cc7f9d8ef6d53ed853f7a9593ec395c4c1c6b2d", - "host": "i686-linux-gnu", - "archiveFileName": "arduinoOTA-1.0.0-linux32.tar.bz2", - "url": "http://downloads.arduino.cc/tools/arduinoOTA-1.0.0-linux32.tar.bz2" - }, - { - "size": "2178772", - "checksum": "SHA-256:f01f25e02787492a8a30414230635adae76ed85228045437433892d185991f9e", - "host": "x86_64-linux-gnu", - "archiveFileName": "arduinoOTA-1.0.0-linux64.tar.bz2", - "url": "http://downloads.arduino.cc/tools/arduinoOTA-1.0.0-linux64.tar.bz2" - }, - { - "size": "1961623", - "checksum": "SHA-256:0ca6c0a93bfad50be0b6e62dc51ba6c3267b809bab4ec91ef9606ab7d838e46b", - "host": "arm-linux-gnueabihf", - "archiveFileName": "arduinoOTA-1.0.0-linuxarm.tar.bz2", - "url": "http://downloads.arduino.cc/tools/arduinoOTA-1.0.0-linuxarm.tar.bz2" - }, - { - "size": "2180617", - "checksum": "SHA-256:e63c6034da2c1a7fe453eaa29c22df88627cc0aa3c5cbab7635c19367b74ee59", - "host": "i386-apple-darwin11", - "archiveFileName": "arduinoOTA-1.0.0-osx.tar.bz2", - "url": "http://downloads.arduino.cc/tools/arduinoOTA-1.0.0-osx.tar.bz2" - }, - { - "size": "2247970", - "checksum": "SHA-256:7bced1489217e07661ea1e75702a10a874b54f6146e2414ee47684c7eac014d1", - "host": "i686-mingw32", - "archiveFileName": "arduinoOTA-1.0.0-windows.zip", - "url": "http://downloads.arduino.cc/tools/arduinoOTA-1.0.0-windows.zip" - } - ] - }, - { - "name": "arduinoOTA", - "version": "1.1.1", - "systems": [ - { - "size": "2045036", - "checksum": "SHA-256:7ac91ef1d5b357c0ceb790be02ef54986db598ba5a42fffbd6c8ecbdd6a271ef", - "host": "i686-linux-gnu", - "archiveFileName": "arduinoOTA-1.1.1-linux_386.tar.bz2", - "url": "http://downloads.arduino.cc/tools/arduinoOTA-1.1.1-linux_386.tar.bz2" - }, - { - "size": "2178288", - "checksum": "SHA-256:eb5ad0a457dd7f610f7f9b85454399c36755673d61a16f9d07cdfcbbb32ec277", - "host": "x86_64-linux-gnu", - "archiveFileName": "arduinoOTA-1.1.1-linux_amd64.tar.bz2", - "url": "http://downloads.arduino.cc/tools/arduinoOTA-1.1.1-linux_amd64.tar.bz2" - }, - { - "size": "1962115", - "checksum": "SHA-256:e4880d83df3d3f6f4b7b7bcde161e80a0556877468803a3c6066ee4ad18a374c", - "host": "arm-linux-gnueabihf", - "archiveFileName": "arduinoOTA-1.1.1-linux_arm.tar.bz2", - "url": "http://downloads.arduino.cc/tools/arduinoOTA-1.1.1-linux_arm.tar.bz2" - }, - { - "size": "2181376", - "checksum": "SHA-256:a1ce7cf578982f3af5e4fab6b5839e44830d7a41cb093faba5c4b45952a6fa55", - "host": "i386-apple-darwin11", - "archiveFileName": "arduinoOTA-1.1.1-darwin_amd64.tar.bz2", - "url": "http://downloads.arduino.cc/tools/arduinoOTA-1.1.1-darwin_amd64.tar.bz2" - }, - { - "size": "2248431", - "checksum": "SHA-256:b2d3610c77f969a68cd75b6ea66bf63ec10c263937009d99147fbcd975c90006", - "host": "i686-mingw32", - "archiveFileName": "arduinoOTA-1.1.1-windows_386.zip", - "url": "http://downloads.arduino.cc/tools/arduinoOTA-1.1.1-windows_386.zip" - } - ] - }, - { - "name": "arduinoOTA", - "version": "1.2.0", - "systems": [ - { - "size": "1839854", - "checksum": "SHA-256:7157a0b56620fb43b8dfb4afd958f8b294476a5ce4322c212167ca5d4092f2d9", - "host": "i686-linux-gnu", - "archiveFileName": "arduinoOTA-1.2.0-linux_386.tar.bz2", - "url": "http://downloads.arduino.cc/tools/arduinoOTA-1.2.0-linux_386.tar.bz2" - }, - { - "size": "1974030", - "checksum": "SHA-256:f672c1c407c4cb10729a1d891bdb8b010e2043e5415e1c2559bf39cdeaede78c", - "host": "x86_64-linux-gnu", - "archiveFileName": "arduinoOTA-1.2.0-linux_amd64.tar.bz2", - "url": "http://downloads.arduino.cc/tools/arduinoOTA-1.2.0-linux_amd64.tar.bz2" - }, - { - "size": "1787138", - "checksum": "SHA-256:ac49ffcd3239a6a52215f89dbda012d28f1296e6d79fc0efc3df06f919105744", - "host": "arm-linux-gnueabihf", - "archiveFileName": "arduinoOTA-1.2.0-linux_arm.tar.bz2", - "url": "http://downloads.arduino.cc/tools/arduinoOTA-1.2.0-linux_arm.tar.bz2" - }, - { - "size": "1992476", - "checksum": "SHA-256:160e83e77d7a60514ca40fedf34f539124aac4b9ae0e2bfdf8fda11d958de38f", - "host": "i386-apple-darwin11", - "archiveFileName": "arduinoOTA-1.2.0-darwin_amd64.tar.bz2", - "url": "http://downloads.arduino.cc/tools/arduinoOTA-1.2.0-darwin_amd64.tar.bz2" - }, - { - "size": "2003964", - "checksum": "SHA-256:9d26747093ab7966bfeffced9dbd7def0e164bba0db89f5efb3f7f8011496c8f", - "host": "i686-mingw32", - "archiveFileName": "arduinoOTA-1.2.0-windows_386.zip", - "url": "http://downloads.arduino.cc/tools/arduinoOTA-1.2.0-windows_386.zip" - } - ] - }, - { - "name": "arduinoOTA", - "version": "1.2.1", - "systems": [ - { - "size": "2133779", - "checksum": "SHA-256:2ffdf64b78486c1d0bf28dc23d0ca36ab75ca92e84b9487246da01888abea6d4", - "host": "i686-linux-gnu", - "archiveFileName": "arduinoOTA-1.2.1-linux_386.tar.bz2", - "url": "http://downloads.arduino.cc/tools/arduinoOTA-1.2.1-linux_386.tar.bz2" - }, - { - "size": "2257689", - "checksum": "SHA-256:5b82310d53688480f34a916aac31cd8f2dd2be65dd8fa6c2445262262e1948f9", - "host": "x86_64-linux-gnu", - "archiveFileName": "arduinoOTA-1.2.1-linux_amd64.tar.bz2", - "url": "http://downloads.arduino.cc/tools/arduinoOTA-1.2.1-linux_amd64.tar.bz2" - }, - { - "size": "2093132", - "checksum": "SHA-256:ad54b3dcd586212941fd992bab573b53d13207a419a3f2981c970a085ae0e9e0", - "host": "arm-linux-gnueabihf", - "archiveFileName": "arduinoOTA-1.2.1-linux_arm.tar.bz2", - "url": "http://downloads.arduino.cc/tools/arduinoOTA-1.2.1-linux_arm.tar.bz2" - }, - { - "size": "2093132", - "checksum": "SHA-256:ad54b3dcd586212941fd992bab573b53d13207a419a3f2981c970a085ae0e9e0", - "host": "aarch64-linux-gnu", - "archiveFileName": "arduinoOTA-1.2.1-linux_arm.tar.bz2", - "url": "http://downloads.arduino.cc/tools/arduinoOTA-1.2.1-linux_arm.tar.bz2" - }, - { - "size": "2244088", - "checksum": "SHA-256:93a6d9f9c0c765d237be1665bf7a0a8e2b0b6d2a8531eae92db807f5515088a7", - "host": "i386-apple-darwin11", - "archiveFileName": "arduinoOTA-1.2.1-darwin_amd64.tar.bz2", - "url": "http://downloads.arduino.cc/tools/arduinoOTA-1.2.1-darwin_amd64.tar.bz2" - }, - { - "size": "2237511", - "checksum": "SHA-256:e1ebf21f2c073fce25c09548c656da90d4ef6c078401ec6f323e0c58335115e5", - "host": "i686-mingw32", - "archiveFileName": "arduinoOTA-1.2.1-windows_386.zip", - "url": "http://downloads.arduino.cc/tools/arduinoOTA-1.2.1-windows_386.zip" - } - ] - }, - { - "name": "arduinoOTA", - "version": "1.3.0", - "systems": [ - { - "size": "2633516", - "checksum": "SHA-256:3e7f59d6fbc7a724598303f0d3289d0c4fd137a8973437980658379a024887b2", - "host": "i686-linux-gnu", - "archiveFileName": "arduinoOTA-1.3.0-linux_386.tar.bz2", - "url": "http://downloads.arduino.cc/tools/arduinoOTA-1.3.0-linux_386.tar.bz2" - }, - { - "size": "2716248", - "checksum": "SHA-256:aa45ee2441ffc3a122daec5802941d1fa2ac47adf5c5c481b5e0daa4dc259ffa", - "host": "x86_64-linux-gnu", - "archiveFileName": "arduinoOTA-1.3.0-linux_amd64.tar.bz2", - "url": "http://downloads.arduino.cc/tools/arduinoOTA-1.3.0-linux_amd64.tar.bz2" - }, - { - "size": "2567435", - "checksum": "SHA-256:1888587409b56aef4ba0ab0e6703b3dccba7cc3a022756ba9b908247e5d5a656", - "host": "arm-linux-gnueabihf", - "archiveFileName": "arduinoOTA-1.3.0-linux_arm.tar.bz2", - "url": "http://downloads.arduino.cc/tools/arduinoOTA-1.3.0-linux_arm.tar.bz2" - }, - { - "size": "2472427", - "checksum": "SHA-256:835ed8f37cffac37e979d1b0f6041559592d3d98be52f0e8611b76c4858e4113", - "host": "aarch64-linux-gnu", - "archiveFileName": "arduinoOTA-1.3.0-linux_arm64.tar.bz2", - "url": "http://downloads.arduino.cc/tools/arduinoOTA-1.3.0-linux_arm64.tar.bz2" - }, - { - "size": "2766116", - "checksum": "SHA-256:d5d0f82ff829c0e434d12a2ee640a6fbd78f893ab37782edbb8b5bf2359d119e", - "host": "i386-apple-darwin11", - "archiveFileName": "arduinoOTA-1.3.0-darwin_amd64.tar.bz2", - "url": "http://downloads.arduino.cc/tools/arduinoOTA-1.3.0-darwin_amd64.tar.bz2" - }, - { - "size": "2768948", - "checksum": "SHA-256:051943844eee442460d2c709edefadca184287fffd2b6c100dd53aa742aa05f6", - "host": "i686-mingw32", - "archiveFileName": "arduinoOTA-1.3.0-windows_386.zip", - "url": "http://downloads.arduino.cc/tools/arduinoOTA-1.3.0-windows_386.zip" - } - ] - }, - { - "name": "bossac", - "version": "1.5-arduino", - "systems": [ - { - "host": "arm-linux-gnueabihf", - "url": "http://downloads.arduino.cc/bossac-1.5-arduino2-arm-linux-gnueabihf.tar.bz2", - "archiveFileName": "bossac-1.5-arduino2-arm-linux-gnueabihf.tar.bz2", - "checksum": "SHA-256:7b61b7814e5b57bcbd853439fc9cd3e98af4abfdd369bf039c6917f9599e44b9", - "size": "199550" - }, - { - "host": "i686-mingw32", - "url": "http://downloads.arduino.cc/bossac-1.5-arduino2-mingw32.tar.gz", - "archiveFileName": "bossac-1.5-arduino2-mingw32.tar.gz", - "checksum": "SHA-256:9d849a34f0b26c25c6a8c4d741cd749dea238cade73b57a3048f248c431d9cc9", - "size": "222283" - }, - { - "host": "x86_64-apple-darwin", - "url": "http://downloads.arduino.cc/bossac-1.5-arduino2-i386-apple-darwin14.3.0.tar.gz", - "archiveFileName": "bossac-1.5-arduino2-i386-apple-darwin14.3.0.tar.gz", - "checksum": "SHA-256:8f07e50a1f887cb254092034c6a4482d73209568cd83cb624d6625d66794f607", - "size": "64120" - }, - { - "host": "x86_64-pc-linux-gnu", - "url": "http://downloads.arduino.cc/bossac-1.5-arduino2-x86_64-linux-gnu.tar.gz", - "archiveFileName": "bossac-1.5-arduino2-x86_64-linux-gnu.tar.gz", - "checksum": "SHA-256:42785329155dcb39872d4d30a2a9d31e0f0ce3ae7e34a3ed3d840cbc909c4657", - "size": "30431" - }, - { - "host": "i686-pc-linux-gnu", - "url": "http://downloads.arduino.cc/bossac-1.5-arduino2-i486-linux-gnu.tar.gz", - "archiveFileName": "bossac-1.5-arduino2-i486-linux-gnu.tar.gz", - "checksum": "SHA-256:ac56e553bbd6d992fa5592ace90996806230ab582f2bf9f8590836fec9dabef6", - "size": "29783" - } - ] - }, - { - "name": "bossac", - "version": "1.6-arduino", - "systems": [ - { - "host": "i686-mingw32", - "url": "http://downloads.arduino.cc/tools/bossac-1.6-arduino-mingw32.tar.gz", - "archiveFileName": "bossac-1.6-arduino-mingw32.tar.gz", - "checksum": "SHA-256:b59d64d3f7a43c894d0fba2dd1241bbaeefedf8c902130a24d8ec63b08f9ff6a", - "size": "222517" - }, - { - "host": "x86_64-apple-darwin", - "url": "http://downloads.arduino.cc/tools/bossac-1.6-arduino-i386-apple-darwin14.4.0.tar.gz", - "archiveFileName": "bossac-1.6-arduino-i386-apple-darwin14.4.0.tar.gz", - "checksum": "SHA-256:6b3b686a782b6587c64c85db80085c9089c5ea1b051e49e5af17b3c6109c8efa", - "size": "64538" - }, - { - "host": "x86_64-pc-linux-gnu", - "url": "http://downloads.arduino.cc/tools/bossac-1.6-arduino-x86_64-linux-gnu.tar.gz", - "archiveFileName": "bossac-1.6-arduino-x86_64-linux-gnu.tar.gz", - "checksum": "SHA-256:2ce7a54d609b4ce3b678147202b2556dd1ce5b318de48a018c676521b994c7a7", - "size": "30649" - }, - { - "host": "i686-pc-linux-gnu", - "url": "http://downloads.arduino.cc/tools/bossac-1.6-arduino-i486-linux-gnu.tar.gz", - "archiveFileName": "bossac-1.6-arduino-i486-linux-gnu.tar.gz", - "checksum": "SHA-256:5c320bf5cfdbf03e3f648642e6de325e459a061fcf96b2215cb955263f7467b2", - "size": "30072" - } - ] - }, - { - "name": "bossac", - "version": "1.6.1-arduino", - "systems": [ - { - "host": "arm-linux-gnueabihf", - "url": "http://downloads.arduino.cc/bossac-1.6.1-arduino-arm-linux-gnueabihf.tar.bz2", - "archiveFileName": "bossac-1.6.1-arduino-arm-linux-gnueabihf.tar.bz2", - "checksum": "SHA-256:8c4e63db982178919c824e7a35580dffc95c3426afa7285de3eb583982d4d391", - "size": "201341" - }, - { - "host": "i686-mingw32", - "url": "http://downloads.arduino.cc/bossac-1.6.1-arduino-mingw32.tar.gz", - "archiveFileName": "bossac-1.6.1-arduino-mingw32.tar.gz", - "checksum": "SHA-256:d59f43e2e83a337d04c4ae88b195a4ee175b8d87fff4c43144d23412a4a9513b", - "size": "222918" - }, - { - "host": "x86_64-apple-darwin", - "url": "http://downloads.arduino.cc/bossac-1.6.1-arduino-i386-apple-darwin14.5.0.tar.gz", - "archiveFileName": "bossac-1.6.1-arduino-i386-apple-darwin14.5.0.tar.gz", - "checksum": "SHA-256:2f80ef569a3fb19da60ab3489e49d8fe7d4699876acf30ff4938c632230a09aa", - "size": "64587" - }, - { - "host": "x86_64-pc-linux-gnu", - "url": "http://downloads.arduino.cc/bossac-1.6.1-arduino-x86_64-linux-gnu.tar.gz", - "archiveFileName": "bossac-1.6.1-arduino-x86_64-linux-gnu.tar.gz", - "checksum": "SHA-256:b78afc66c00ccfdd69a08bd3959c260a0c64ccce78a71d5a1135ae4437ff40db", - "size": "30869" - }, - { - "host": "i686-pc-linux-gnu", - "url": "http://downloads.arduino.cc/bossac-1.6.1-arduino-i486-linux-gnu.tar.gz", - "archiveFileName": "bossac-1.6.1-arduino-i486-linux-gnu.tar.gz", - "checksum": "SHA-256:1e211347569d75193b337296a10dd25b0ce04419e3d7dc644355178b6b514f92", - "size": "30320" - } - ] - }, - { - "name": "bossac", - "version": "1.7.0", - "systems": [ - { - "host": "i686-mingw32", - "url": "http://downloads.arduino.cc/tools/bossac-1.7.0-mingw32.tar.gz", - "archiveFileName": "bossac-1.7.0-mingw32.tar.gz", - "checksum": "SHA-256:9ef7d11b4fabca0adc17102a0290957d5cc26ce46b422c3a5344722c80acc7b2", - "size": "243066" - }, - { - "host": "x86_64-apple-darwin", - "url": "http://downloads.arduino.cc/tools/bossac-1.7.0-i386-apple-darwin15.6.0.tar.gz", - "archiveFileName": "bossac-1.7.0-i386-apple-darwin15.6.0.tar.gz", - "checksum": "SHA-256:feac36ab38876c163dcf51bdbcfbed01554eede3d41c59a0e152e170fe5164d2", - "size": "63822" - }, - { - "host": "x86_64-pc-linux-gnu", - "url": "http://downloads.arduino.cc/tools/bossac-1.7.0-x86_64-linux-gnu.tar.gz", - "archiveFileName": "bossac-1.7.0-x86_64-linux-gnu.tar.gz", - "checksum": "SHA-256:9475c0c8596c1ba12dcbce60e48fef7559087fa8eccbea7bab732113f3c181ee", - "size": "31373" - }, - { - "host": "i686-pc-linux-gnu", - "url": "http://downloads.arduino.cc/tools/bossac-1.7.0-i686-linux-gnu.tar.gz", - "archiveFileName": "bossac-1.7.0-i686-linux-gnu.tar.gz", - "checksum": "SHA-256:17003b0bdc698d52eeb91b09c34aec501c6e0285b4aa88659ab7cc407a451a4d", - "size": "31086" - }, - { - "host": "arm-linux-gnueabihf", - "url": "http://downloads.arduino.cc/tools/bossac-1.7.0-arm-linux-gnueabihf.tar.gz", - "archiveFileName": "bossac-1.7.0-arm-linux-gnueabihf.tar.gz", - "checksum": "SHA-256:09e46d0af61b2189caaac0bc6d4dd15cb22c167fdedc56ec98602dd5f10e68e0", - "size": "27382" - } - ] - }, - { - "name": "bossac", - "version": "1.7.0-arduino3", - "systems": [ - { - "host": "i686-mingw32", - "url": "http://downloads.arduino.cc/tools/bossac-1.7.0-arduino3-windows.tar.gz", - "archiveFileName": "bossac-1.7.0-arduino3-windows.tar.gz", - "checksum": "SHA-256:62745cc5a98c26949ec9041ef20420643c561ec43e99dae659debf44e6836526", - "size": "3607421" - }, - { - "host": "x86_64-apple-darwin", - "url": "http://downloads.arduino.cc/tools/bossac-1.7.0-arduino3-osx.tar.gz", - "archiveFileName": "bossac-1.7.0-arduino3-osx.tar.gz", - "checksum": "SHA-256:adb3c14debd397d8135e9e970215c6972f0e592c7af7532fa15f9ce5e64b991f", - "size": "75510" - }, - { - "host": "x86_64-pc-linux-gnu", - "url": "http://downloads.arduino.cc/tools/bossac-1.7.0-arduino3-linux64.tar.gz", - "archiveFileName": "bossac-1.7.0-arduino3-linux64.tar.gz", - "checksum": "SHA-256:1ae54999c1f97234a5c603eb99ad39313b11746a4ca517269a9285afa05f9100", - "size": "207271" - }, - { - "host": "i686-pc-linux-gnu", - "url": "http://downloads.arduino.cc/tools/bossac-1.7.0-arduino3-linux32.tar.gz", - "archiveFileName": "bossac-1.7.0-arduino3-linux32.tar.gz", - "checksum": "SHA-256:4ac4354746d1a09258f49a43ef4d1baf030d81c022f8434774268b00f55d3ec3", - "size": "193577" - }, - { - "host": "arm-linux-gnueabihf", - "url": "http://downloads.arduino.cc/tools/bossac-1.7.0-arduino3-linuxarm.tar.gz", - "archiveFileName": "bossac-1.7.0-arduino3-linuxarm.tar.gz", - "checksum": "SHA-256:626c6cc548046901143037b782bf019af1663bae0d78cf19181a876fb9abbb90", - "size": "193941" - }, - { - "host": "aarch64-linux-gnu", - "url": "http://downloads.arduino.cc/tools/bossac-1.7.0-arduino3-linuxaarch64.tar.gz", - "archiveFileName": "bossac-1.7.0-arduino3-linuxaarch64.tar.gz", - "checksum": "SHA-256:a098b2cc23e29f0dc468416210d097c4a808752cd5da1a7b9b8b7b931a04180b", - "size": "268365" - } - ] - }, - { - "name": "bossac", - "version": "1.8.0-48-gb176eee", - "systems": [ - { - "host": "i686-mingw32", - "url": "http://downloads.arduino.cc/tools/bossac-1.8-48-gb176eee-i686-w64-mingw32.tar.gz", - "archiveFileName": "bossac-1.8-48-gb176eee-i686-w64-mingw32.tar.gz", - "checksum": "SHA-256:4523a6897f3dfd673fe821c5cfbac8d6a12782e7a36b312b9ee7d41deec2a10a", - "size": "91219" - }, - { - "host": "x86_64-apple-darwin", - "url": "http://downloads.arduino.cc/tools/bossac-1.8-48-gb176eee-i386-apple-darwin16.1.0.tar.gz", - "archiveFileName": "bossac-1.8-48-gb176eee-i386-apple-darwin16.1.0.tar.gz", - "checksum": "SHA-256:581ecc16021de36638ae14e9e064ffb4a1d532a11502f4252da8bcdf5ce1d649", - "size": "39150" - }, - { - "host": "x86_64-pc-linux-gnu", - "url": "http://downloads.arduino.cc/tools/bossac-1.8-48-gb176eee-x86_64-linux-gnu.tar.gz", - "archiveFileName": "bossac-1.8-48-gb176eee-x86_64-linux-gnu.tar.gz", - "checksum": "SHA-256:1347eec67f5b90b785abdf6c8a8aa59129d0c016de7ff9b5ac1690378eacca3c", - "size": "37798" - }, - { - "host": "i686-pc-linux-gnu", - "url": "http://downloads.arduino.cc/tools/bossac-1.8-48-gb176eee-i486-linux-gnu.tar.gz", - "archiveFileName": "bossac-1.8-48-gb176eee-i486-linux-gnu.tar.gz", - "checksum": "SHA-256:4c7492f876b8269aa9d8bcaad3aeda31acf1a0292383093b6d9f5f1d23fdafc3", - "size": "37374" - }, - { - "host": "arm-linux-gnueabihf", - "url": "http://downloads.arduino.cc/tools/bossac-1.8-48-gb176eee-arm-linux-gnueabihf.tar.gz", - "archiveFileName": "bossac-1.8-48-gb176eee-arm-linux-gnueabihf.tar.gz", - "checksum": "SHA-256:2001e4a592f3aefd22f213b1ddd6f5d8d5e74bd04080cf1b97c24cbaa81b10ed", - "size": "34825" - } - ] - }, - { - "name": "bossac", - "version": "1.9.1-arduino1", - "systems": [ - { - "host": "i686-mingw32", - "url": "http://downloads.arduino.cc/tools/bossac-1.9.1-arduino1-windows.tar.gz", - "archiveFileName": "bossac-1.9.1-arduino1-windows.tar.gz", - "checksum": "SHA-256:fe2d6ef78ca711c78e104e258357ed06b09e95e9356dc72d8d2c9f6670af4b7a", - "size": "1260118" - }, - { - "host": "x86_64-apple-darwin", - "url": "http://downloads.arduino.cc/tools/bossac-1.9.1-arduino1-osx.tar.gz", - "archiveFileName": "bossac-1.9.1-arduino1-osx.tar.gz", - "checksum": "SHA-256:c356632f98d5bae9b4f5d3ad823a5ee89b23078c2b835e8ac39a208f4855b0e6", - "size": "47835" - }, - { - "host": "x86_64-pc-linux-gnu", - "url": "http://downloads.arduino.cc/tools/bossac-1.9.1-arduino1-linux64.tar.gz", - "archiveFileName": "bossac-1.9.1-arduino1-linux64.tar.gz", - "checksum": "SHA-256:d3d324a3503a8db825c01f3b38519e4d4824c4d0e42cb399a16c1e074f9a9a86", - "size": "399453" - }, - { - "host": "i686-pc-linux-gnu", - "url": "http://downloads.arduino.cc/tools/bossac-1.9.1-arduino1-linux32.tar.gz", - "archiveFileName": "bossac-1.9.1-arduino1-linux32.tar.gz", - "checksum": "SHA-256:eec622b8b5a8642af94ec23febfe14c928edd734f144db73b146bf6708d2057f", - "size": "384792" - }, - { - "host": "arm-linux-gnueabihf", - "url": "http://downloads.arduino.cc/tools/bossac-1.9.1-arduino1-linuxarm.tar.gz", - "archiveFileName": "bossac-1.9.1-arduino1-linuxarm.tar.gz", - "checksum": "SHA-256:b42061d2fa2dbd6910d0d295e024f2cff7bb44f3e2ecc0bc2fe71a1f31b0ecba", - "size": "361799" - }, - { - "host": "aarch64-linux-gnu", - "url": "http://downloads.arduino.cc/tools/bossac-1.9.1-arduino1-linuxaarch64.tar.gz", - "archiveFileName": "bossac-1.9.1-arduino1-linuxaarch64.tar.gz", - "checksum": "SHA-256:b271013841e1e25ee55f241e8c50a56ed775d3b322844e1e3099231ba17f3868", - "size": "442657" - } - ] - }, - { - "name": "bossac", - "version": "1.9.1-arduino2", - "systems": [ - { - "host": "i686-mingw32", - "url": "http://downloads.arduino.cc/tools/bossac-1.9.1-arduino2-windows.tar.gz", - "archiveFileName": "bossac-1.9.1-arduino2-windows.tar.gz", - "checksum": "SHA-256:5c994d04354f0db8e4bea136f49866d2ba537f0af74b2e78026f2d4fc75e3e39", - "size": "1260628" - }, - { - "host": "x86_64-apple-darwin", - "url": "http://downloads.arduino.cc/tools/bossac-1.9.1-arduino2-osx.tar.gz", - "archiveFileName": "bossac-1.9.1-arduino2-osx.tar.gz", - "checksum": "SHA-256:b7732129364a378676604db6579c9b8dab50dd965fb50d7a3afff1839c97ff80", - "size": "47870" - }, - { - "host": "x86_64-pc-linux-gnu", - "url": "http://downloads.arduino.cc/tools/bossac-1.9.1-arduino2-linux64.tar.gz", - "archiveFileName": "bossac-1.9.1-arduino2-linux64.tar.gz", - "checksum": "SHA-256:9eb549874391521999cee13dc823a2cfc8866b8246945339a281808d99c72d2c", - "size": "399532" - }, - { - "host": "i686-pc-linux-gnu", - "url": "http://downloads.arduino.cc/tools/bossac-1.9.1-arduino2-linux32.tar.gz", - "archiveFileName": "bossac-1.9.1-arduino2-linux32.tar.gz", - "checksum": "SHA-256:10d69f53f169f25afee2dd583dfd9dc803c10543e6c5260d106725cb0d174900", - "size": "384951" - }, - { - "host": "arm-linux-gnueabihf", - "url": "http://downloads.arduino.cc/tools/bossac-1.9.1-arduino2-linuxarm.tar.gz", - "archiveFileName": "bossac-1.9.1-arduino2-linuxarm.tar.gz", - "checksum": "SHA-256:c9539d161d23231b5beb1d09a71829744216c7f5bc2857a491999c3e567f5b19", - "size": "361915" - }, - { - "host": "aarch64-linux-gnu", - "url": "http://downloads.arduino.cc/tools/bossac-1.9.1-arduino2-linuxaarch64.tar.gz", - "archiveFileName": "bossac-1.9.1-arduino2-linuxaarch64.tar.gz", - "checksum": "SHA-256:c167fa0ea223966f4d21f5592da3888bcbfbae385be6c5c4e41f8abff35f5cb1", - "size": "442853" - } - ] - }, - { - "name": "openocd", - "version": "0.9.0-arduino", - "systems": [ - { - "host": "arm-linux-gnueabihf", - "url": "http://downloads.arduino.cc/tools/OpenOCD-0.9.0-arduino-arm-linux-gnueabihf.tar.bz2", - "archiveFileName": "OpenOCD-0.9.0-dev-arduino-arm-linux-gnueabihf.tar.bz2", - "checksum": "SHA-256:a84e7c4cba853f2c937d77286f8a0ca317447d3873e51cbd2a2d41424e044a18", - "size": "1402283" - }, - { - "host": "i686-mingw32", - "url": "http://downloads.arduino.cc/tools/OpenOCD-0.9.0-arduino-i686-pc-cygwin.tar.bz2", - "archiveFileName": "OpenOCD-0.9.0-arduino-i686-pc-cygwin.tar.bz2", - "checksum": "SHA-256:5310bdd3730168a33b09b68558e908ca8b2fec25620c488f50a5fb35d0d1effd", - "size": "2360705" - }, - { - "host": "x86_64-apple-darwin", - "url": "http://downloads.arduino.cc/tools/OpenOCD-0.9.0-arduino-x86_64-apple-darwin14.3.0.tar.bz2", - "archiveFileName": "OpenOCD-0.9.0-arduino-x86_64-apple-darwin14.3.0.tar.bz2", - "checksum": "SHA-256:ef90769c07b8018cec3a5054e690ac6c196e03720e102ac5038c3f9da4e44782", - "size": "2275101" - }, - { - "host": "x86_64-pc-linux-gnu", - "url": "http://downloads.arduino.cc/tools/OpenOCD-0.9.0-arduino-x86_64-linux-gnu.tar.bz2", - "archiveFileName": "OpenOCD-0.9.0-arduino-x86_64-linux-gnu.tar.bz2", - "checksum": "SHA-256:c350409f7badf213dfcc516ea34289461ad92d87806e8e33945508a2c6b2c0b3", - "size": "1210796" - }, - { - "host": "i686-pc-linux-gnu", - "url": "http://downloads.arduino.cc/tools/OpenOCD-0.9.0-arduino-i486-linux-gnu.tar.bz2", - "archiveFileName": "OpenOCD-0.9.0-arduino-i486-linux-gnu.tar.bz2", - "checksum": "SHA-256:4c9793dfd7822b0fc959d039e5ecabfa89092ee2911abfdc7b5905deb171499a", - "size": "1129654" - } - ] - }, - { - "name": "openocd", - "version": "0.9.0-arduino5-static", - "systems": [ - { - "host": "arm-linux-gnueabihf", - "url": "http://downloads.arduino.cc/tools/openocd-0.9.0-arduino5-static-arm-linux-gnueabihf.tar.bz2", - "archiveFileName": "openocd-0.9.0-arduino5-static-arm-linux-gnueabihf.tar.bz2", - "checksum": "SHA-256:cef48c1448664612dd25168f0a56962aec4ce2f1d7c06dafd86a1b606dc8ae20", - "size": "1319000" - }, - { - "host": "i686-mingw32", - "url": "http://downloads.arduino.cc/tools/openocd-0.9.0-arduino5-static-i686-w64-mingw32.zip", - "archiveFileName": "openocd-0.9.0-arduino5-static-i686-w64-mingw32.zip", - "checksum": "SHA-256:54c70a0bfa1b0a3a592d6ee9ab532f9715e1dede2e7d46a3232abd72de274c5a", - "size": "1641209" - }, - { - "host": "x86_64-apple-darwin", - "url": "http://downloads.arduino.cc/tools/openocd-0.9.0-arduino5-static-x86_64-apple-darwin15.6.0.tar.bz2", - "archiveFileName": "openocd-0.9.0-arduino5-static-x86_64-apple-darwin15.6.0.tar.bz2", - "checksum": "SHA-256:14be5c5400e1a32c3d6a15f9c8d2f438634974ab263ff437b91b527e5b5d53a4", - "size": "1235752" - }, - { - "host": "x86_64-pc-linux-gnu", - "url": "http://downloads.arduino.cc/tools/openocd-0.9.0-arduino5-static-x86_64-linux-gnu.tar.bz2", - "archiveFileName": "openocd-0.9.0-arduino5-static-x86_64-linux-gnu.tar.bz2", - "checksum": "SHA-256:8e378bdcd71c93a39818c16b49b91128c8028e3d9675551ba7eff39462391ba2", - "size": "1393855" - }, - { - "host": "i686-pc-linux-gnu", - "url": "http://downloads.arduino.cc/tools/openocd-0.9.0-arduino5-static-i686-linux-gnu.tar.bz2", - "archiveFileName": "openocd-0.9.0-arduino5-static-i686-linux-gnu.tar.bz2", - "checksum": "SHA-256:8e0787f54e204fe6e9071b2b7edf8a5e695492696f1182d447647fe5c0bd55bd", - "size": "1341739" - } - ] - }, - { - "name": "openocd", - "version": "0.9.0-arduino6-static", - "systems": [ - { - "host": "arm-linux-gnueabihf", - "url": "http://downloads.arduino.cc/tools/openocd-0.9.0-arduino6-static-arm-linux-gnueabihf.tar.bz2", - "archiveFileName": "openocd-0.9.0-arduino6-static-arm-linux-gnueabihf.tar.bz2", - "checksum": "SHA-256:5d596c90510f80d66f64a3615d74063a6a61f07b79be475592a3c76bf0deb3ca", - "size": "1319020" - }, - { - "host": "i686-mingw32", - "url": "http://downloads.arduino.cc/tools/openocd-0.9.0-arduino6-static-i686-w64-mingw32.zip", - "archiveFileName": "openocd-0.9.0-arduino6-static-i686-w64-mingw32.zip", - "checksum": "SHA-256:dde6c8cd42c179e819eeebee1d09829b0768ecb89b75fb10e1f053c1c65f9cf1", - "size": "1641514" - }, - { - "host": "x86_64-apple-darwin", - "url": "http://downloads.arduino.cc/tools/openocd-0.9.0-arduino6-static-x86_64-apple-darwin15.6.0.tar.bz2", - "archiveFileName": "openocd-0.9.0-arduino6-static-x86_64-apple-darwin15.6.0.tar.bz2", - "checksum": "SHA-256:00cd65339bc981ff0d4ab4876df8f89b1e60e476441fabca31d5fc2968bad9be", - "size": "1222523" - }, - { - "host": "x86_64-pc-linux-gnu", - "url": "http://downloads.arduino.cc/tools/openocd-0.9.0-arduino6-static-x86_64-linux-gnu.tar.bz2", - "archiveFileName": "openocd-0.9.0-arduino6-static-x86_64-linux-gnu.tar.bz2", - "checksum": "SHA-256:d2f58bbd0661b755fdb8a307d197f119d838b066f5510b25ee766e47d1774543", - "size": "1394293" - }, - { - "host": "i686-pc-linux-gnu", - "url": "http://downloads.arduino.cc/tools/openocd-0.9.0-arduino6-static-i686-linux-gnu.tar.bz2", - "archiveFileName": "openocd-0.9.0-arduino6-static-i686-linux-gnu.tar.bz2", - "checksum": "SHA-256:88d948c2062c73c0c93e649e099aaac4b009018cff365f44cfc5b47907043dc9", - "size": "1340444" - } - ] - }, - { - "name": "openocd", - "version": "0.10.0-arduino7", - "systems": [ - { - "size": "1638575", - "checksum": "SHA-256:f8e0d783e80a3d5f75ee82e9542315871d46e1e283a97447735f1cbcd8986b06", - "host": "arm-linux-gnueabihf", - "archiveFileName": "openocd-0.10.0-arduino7-static-arm-linux-gnueabihf.tar.bz2", - "url": "http://downloads.arduino.cc/tools/openocd-0.10.0-arduino7-static-arm-linux-gnueabihf.tar.bz2" - }, - { - "size": "1580739", - "checksum": "SHA-256:d47d728a9a8d98f28dc22e31d7127ced9de0d5e268292bf935e050ef1d2bdfd0", - "host": "aarch64-linux-gnu", - "archiveFileName": "openocd-0.10.0-arduino7-static-aarch64-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/openocd-0.10.0-arduino7-static-aarch64-linux-gnu.tar.bz2" - }, - { - "size": "1498970", - "checksum": "SHA-256:1e539a587a0c54a551ce0dc542af10a2520b1c93bbfe2ca4ebaef4c83411df1a", - "host": "i386-apple-darwin11", - "archiveFileName": "openocd-0.10.0-arduino7-static-x86_64-apple-darwin13.tar.bz2", - "url": "http://downloads.arduino.cc/tools/openocd-0.10.0-arduino7-static-x86_64-apple-darwin13.tar.bz2" - }, - { - "size": "1701581", - "checksum": "SHA-256:91d418bd309ec1e98795c622cd25c936aa537c0b3828fa5bcb191389378a1b27", - "host": "x86_64-linux-gnu", - "archiveFileName": "openocd-0.10.0-arduino7-static-x86_64-ubuntu12.04-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/openocd-0.10.0-arduino7-static-x86_64-ubuntu12.04-linux-gnu.tar.bz2" - }, - { - "size": "1626347", - "checksum": "SHA-256:08a18f39d72a5626383503053a30a5da89eed7fdccb6f514b20b77403eb1b2b4", - "host": "i686-linux-gnu", - "archiveFileName": "openocd-0.10.0-arduino7-static-i686-ubuntu12.04-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/openocd-0.10.0-arduino7-static-i686-ubuntu12.04-linux-gnu.tar.bz2" - }, - { - "size": "2016965", - "checksum": "SHA-256:f251aec5471296e18aa540c3078d66475357a76a77c16c06a2d9345f4e12b3d5", - "host": "i686-mingw32", - "archiveFileName": "openocd-0.10.0-arduino7-static-i686-w64-mingw32.zip", - "url": "http://downloads.arduino.cc/tools/openocd-0.10.0-arduino7-static-i686-w64-mingw32.zip" - } - ] - }, - { - "name": "openocd", - "version": "0.10.0-arduino8", - "systems": [ - { - "size": "1714346", - "checksum": "SHA-256:86c4ea3086b4a1475fd8a1e1daf4585748be093dad4160e816b1bf2502501fb2", - "host": "arm-linux-gnueabihf", - "archiveFileName": "openocd-0.10.0-arduino8-static-arm-linux-gnueabihf.tar.bz2", - "url": "http://downloads.arduino.cc/tools/openocd-0.10.0-arduino8-static-arm-linux-gnueabihf.tar.bz2" - }, - { - "size": "1778371", - "checksum": "SHA-256:500cb112ee92092bbfce69649b90d0284752c5766f5aaf5c24dc754100788ef9", - "host": "aarch64-linux-gnu", - "archiveFileName": "openocd-0.10.0-arduino8-static-aarch64-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/openocd-0.10.0-arduino8-static-aarch64-linux-gnu.tar.bz2" - }, - { - "size": "1653652", - "checksum": "SHA-256:584b513ebbc4a645a68234d964ba56f042aaf7668d84ba47398a07a294516cc4", - "host": "i386-apple-darwin11", - "archiveFileName": "openocd-0.10.0-arduino8-static-x86_64-apple-darwin13.tar.bz2", - "url": "http://downloads.arduino.cc/tools/openocd-0.10.0-arduino8-static-x86_64-apple-darwin13.tar.bz2" - }, - { - "size": "1845547", - "checksum": "SHA-256:455d4123146bf6b4b095de86d3340fd01e39bba9c70b2f0bb8e979ac4dddac39", - "host": "x86_64-linux-gnu", - "archiveFileName": "openocd-0.10.0-arduino8-static-x86_64-ubuntu12.04-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/openocd-0.10.0-arduino8-static-x86_64-ubuntu12.04-linux-gnu.tar.bz2" - }, - { - "size": "1781892", - "checksum": "SHA-256:5b44889984daefa966b8251edb98445169107ca32f974ca598d4c59e7d2c8901", - "host": "i686-linux-gnu", - "archiveFileName": "openocd-0.10.0-arduino8-static-i686-ubuntu12.04-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/openocd-0.10.0-arduino8-static-i686-ubuntu12.04-linux-gnu.tar.bz2" - }, - { - "size": "2190397", - "checksum": "SHA-256:35a92f32f2762ef9405d2c684ec7bea2e70c01068f380251aecd9290f5bd5b24", - "host": "i686-mingw32", - "archiveFileName": "openocd-0.10.0-arduino8-static-i686-w64-mingw32.zip", - "url": "http://downloads.arduino.cc/tools/openocd-0.10.0-arduino8-static-i686-w64-mingw32.zip" - } - ] - }, - { - "name": "openocd", - "version": "0.10.0-arduino9", - "systems": [ - { - "size": "1714657", - "checksum": "SHA-256:b814b16b52cef21eacf456cc7a89d7b5d4cb1385bfb8fb82963b7d8151824d93", - "host": "aarch64-linux-gnu", - "archiveFileName": "openocd-0.10.0-arduino9-static-aarch64-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/openocd-0.10.0-arduino9-static-aarch64-linux-gnu.tar.bz2" - }, - { - "size": "1778177", - "checksum": "SHA-256:f0443e771f5e3a779949498d3c9bfffd1dd27cdf0ad7136a2db5e80447a7175a", - "host": "arm-linux-gnueabihf", - "archiveFileName": "openocd-0.10.0-arduino9-static-arm-linux-gnueabihf.tar.bz2", - "url": "http://downloads.arduino.cc/tools/openocd-0.10.0-arduino9-static-arm-linux-gnueabihf.tar.bz2" - }, - { - "size": "1782958", - "checksum": "SHA-256:a22872918df899cb808f9286141d42438ae5611156c143cfb692069f52a2bddd", - "host": "i686-linux-gnu", - "archiveFileName": "openocd-0.10.0-arduino9-static-i686-ubuntu12.04-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/openocd-0.10.0-arduino9-static-i686-ubuntu12.04-linux-gnu.tar.bz2" - }, - { - "size": "2190484", - "checksum": "SHA-256:f53f9a2b7f48a2ebc00ea9196bf559d15987d3779bcea4118ebec8925da5a1f6", - "host": "i686-mingw32", - "archiveFileName": "openocd-0.10.0-arduino9-static-i686-w64-mingw32.zip", - "url": "http://downloads.arduino.cc/tools/openocd-0.10.0-arduino9-static-i686-w64-mingw32.zip" - }, - { - "size": "1655311", - "checksum": "SHA-256:6d47f97919f317bb6e5f1f903127604271d66d149f4625f29b8e0eb5f6c94c64", - "host": "i386-apple-darwin11", - "archiveFileName": "openocd-0.10.0-arduino9-static-x86_64-apple-darwin13.tar.bz2", - "url": "http://downloads.arduino.cc/tools/openocd-0.10.0-arduino9-static-x86_64-apple-darwin13.tar.bz2" - }, - { - "size": "1844365", - "checksum": "SHA-256:f624552b5ba56aa78d0c1a0e5d18cf2b5694db2ed44080968e22aa1af8f23c1b", - "host": "x86_64-linux-gnu", - "archiveFileName": "openocd-0.10.0-arduino9-static-x86_64-ubuntu12.04-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/openocd-0.10.0-arduino9-static-x86_64-ubuntu12.04-linux-gnu.tar.bz2" - } - ] - }, - { - "name": "openocd", - "version": "0.10.0-arduino12", - "systems": [ - { - "size": "1778706", - "checksum": "SHA-256:86e15186a44b87c00f5ddd9c05849d2ec107783dd18a5ac984ce2a71e34084ed", - "host": "aarch64-linux-gnu", - "archiveFileName": "openocd-0.10.0-arduino12-static-aarch64-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/openocd-0.10.0-arduino12-static-aarch64-linux-gnu.tar.bz2" - }, - { - "size": "1855234", - "checksum": "SHA-256:5c6ca6189f61894ee77b29bc342f96cd14e4d7627fabeb2a8d2e2c534316cc38", - "host": "arm-linux-gnueabihf", - "archiveFileName": "openocd-0.10.0-arduino12-static-arm-linux-gnueabihf.tar.bz2", - "url": "http://downloads.arduino.cc/tools/openocd-0.10.0-arduino12-static-arm-linux-gnueabihf.tar.bz2" - }, - { - "size": "1844359", - "checksum": "SHA-256:4938742d3fec62941187666b8ded44d8f6c7a404920ff49d97fca484b9fd08af", - "host": "i686-linux-gnu", - "archiveFileName": "openocd-0.10.0-arduino12-static-i686-ubuntu12.04-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/openocd-0.10.0-arduino12-static-i686-ubuntu12.04-linux-gnu.tar.bz2" - }, - { - "size": "2276602", - "checksum": "SHA-256:1e23c0f1f809725db3e1f8d1e1f460a37fb7b2cf95e93c6e328e527501ab084c", - "host": "i686-mingw32", - "archiveFileName": "openocd-0.10.0-arduino12-static-i686-w64-mingw32.zip", - "url": "http://downloads.arduino.cc/tools/openocd-0.10.0-arduino12-static-i686-w64-mingw32.zip" - }, - { - "size": "1723600", - "checksum": "SHA-256:b40d135449401870302bec073326d6f1df79da38d9dd21326314a5a90189a06e", - "host": "i386-apple-darwin11", - "archiveFileName": "openocd-0.10.0-arduino12-static-x86_64-apple-darwin13.tar.bz2", - "url": "http://downloads.arduino.cc/tools/openocd-0.10.0-arduino12-static-x86_64-apple-darwin13.tar.bz2" - }, - { - "size": "1918845", - "checksum": "SHA-256:bc48be10916f69f3a4b050f04babc14ee99dad1fc5da55ce606077991edab1d0", - "host": "x86_64-linux-gnu", - "archiveFileName": "openocd-0.10.0-arduino12-static-x86_64-ubuntu12.04-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/openocd-0.10.0-arduino12-static-x86_64-ubuntu12.04-linux-gnu.tar.bz2" - } - ] - }, - { - "name": "openocd", - "version": "0.10.0-arduino13", - "systems": [ - { - "size": "1820630", - "checksum": "SHA-256:47ae7a1a7961ac9daef001b011505b38777baac3c02dd7e673f62601df841427", - "host": "aarch64-linux-gnu", - "archiveFileName": "openocd-0.10.0-arduino13-static-aarch64-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/openocd-0.10.0-arduino13-static-aarch64-linux-gnu.tar.bz2" - }, - { - "size": "1896478", - "checksum": "SHA-256:4dd38b701019ad2fbb58173a3bc6c58effd39501a4a8266256dfe169e7516655", - "host": "arm-linux-gnueabihf", - "archiveFileName": "openocd-0.10.0-arduino13-static-arm-linux-gnueabihf.tar.bz2", - "url": "http://downloads.arduino.cc/tools/openocd-0.10.0-arduino13-static-arm-linux-gnueabihf.tar.bz2" - }, - { - "size": "1883854", - "checksum": "SHA-256:788093504b25d2b9b772657215254ba178ed37773364ce240de68281efe40bd5", - "host": "i686-linux-gnu", - "archiveFileName": "openocd-0.10.0-arduino13-static-i686-ubuntu12.04-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/openocd-0.10.0-arduino13-static-i686-ubuntu12.04-linux-gnu.tar.bz2" - }, - { - "size": "2334654", - "checksum": "SHA-256:2f3b87c644569f47780b16b071cd0929a64a8899ec769f4ca7480d20d5503365", - "host": "i686-mingw32", - "archiveFileName": "openocd-0.10.0-arduino13-static-i686-w64-mingw32.zip", - "url": "http://downloads.arduino.cc/tools/openocd-0.10.0-arduino13-static-i686-w64-mingw32.zip" - }, - { - "size": "1767137", - "checksum": "SHA-256:0f3f6e5e03355ffbbc84c4b4750e63c9315b7638c56d63df1b7795968208e6ba", - "host": "i386-apple-darwin11", - "archiveFileName": "openocd-0.10.0-arduino13-static-x86_64-apple-darwin13.tar.bz2", - "url": "http://downloads.arduino.cc/tools/openocd-0.10.0-arduino13-static-x86_64-apple-darwin13.tar.bz2" - }, - { - "size": "1955043", - "checksum": "SHA-256:e4b2ffbc9a29be21c32c6921c2e7c0ee39c664c4faca28a5f839c1df32d3bd24", - "host": "x86_64-linux-gnu", - "archiveFileName": "openocd-0.10.0-arduino13-static-x86_64-ubuntu12.04-linux-gnu.tar.bz2", - "url": "http://downloads.arduino.cc/tools/openocd-0.10.0-arduino13-static-x86_64-ubuntu12.04-linux-gnu.tar.bz2" - } - ] - }, - { - "name": "CMSIS", - "version": "4.0.0-atmel", - "systems": [ - { - "host": "arm-linux-gnueabihf", - "url": "http://downloads.arduino.cc/CMSIS-4.0.0.tar.bz2", - "archiveFileName": "CMSIS-4.0.0.tar.bz2", - "checksum": "SHA-256:7d637d2d7a0c6bacc22065848a201db2fff124268e4a56868260d0f472b4bbb7", - "size": "17642623" - }, - { - "host": "i686-mingw32", - "url": "http://downloads.arduino.cc/CMSIS-4.0.0.tar.bz2", - "archiveFileName": "CMSIS-4.0.0.tar.bz2", - "checksum": "SHA-256:7d637d2d7a0c6bacc22065848a201db2fff124268e4a56868260d0f472b4bbb7", - "size": "17642623" - }, - { - "host": "x86_64-apple-darwin", - "url": "http://downloads.arduino.cc/CMSIS-4.0.0.tar.bz2", - "archiveFileName": "CMSIS-4.0.0.tar.bz2", - "checksum": "SHA-256:7d637d2d7a0c6bacc22065848a201db2fff124268e4a56868260d0f472b4bbb7", - "size": "17642623" - }, - { - "host": "x86_64-pc-linux-gnu", - "url": "http://downloads.arduino.cc/CMSIS-4.0.0.tar.bz2", - "archiveFileName": "CMSIS-4.0.0.tar.bz2", - "checksum": "SHA-256:7d637d2d7a0c6bacc22065848a201db2fff124268e4a56868260d0f472b4bbb7", - "size": "17642623" - }, - { - "host": "i686-pc-linux-gnu", - "url": "http://downloads.arduino.cc/CMSIS-4.0.0.tar.bz2", - "archiveFileName": "CMSIS-4.0.0.tar.bz2", - "checksum": "SHA-256:7d637d2d7a0c6bacc22065848a201db2fff124268e4a56868260d0f472b4bbb7", - "size": "17642623" - } - ] - }, - { - "name": "CMSIS", - "version": "4.5.0", - "systems": [ - { - "host": "i686-mingw32", - "url": "http://downloads.arduino.cc/CMSIS-4.5.0.tar.bz2", - "archiveFileName": "CMSIS-4.5.0.tar.bz2", - "checksum": "SHA-256:cd8f7eae9fc7c8b4a1b5e40b89b9666d33953b47d3d2eb81844f5af729fa224d", - "size": "31525196" - }, - { - "host": "x86_64-apple-darwin", - "url": "http://downloads.arduino.cc/CMSIS-4.5.0.tar.bz2", - "archiveFileName": "CMSIS-4.5.0.tar.bz2", - "checksum": "SHA-256:cd8f7eae9fc7c8b4a1b5e40b89b9666d33953b47d3d2eb81844f5af729fa224d", - "size": "31525196" - }, - { - "host": "x86_64-pc-linux-gnu", - "url": "http://downloads.arduino.cc/CMSIS-4.5.0.tar.bz2", - "archiveFileName": "CMSIS-4.5.0.tar.bz2", - "checksum": "SHA-256:cd8f7eae9fc7c8b4a1b5e40b89b9666d33953b47d3d2eb81844f5af729fa224d", - "size": "31525196" - }, - { - "host": "i686-pc-linux-gnu", - "url": "http://downloads.arduino.cc/CMSIS-4.5.0.tar.bz2", - "archiveFileName": "CMSIS-4.5.0.tar.bz2", - "checksum": "SHA-256:cd8f7eae9fc7c8b4a1b5e40b89b9666d33953b47d3d2eb81844f5af729fa224d", - "size": "31525196" - }, - { - "host": "arm-linux-gnueabihf", - "url": "http://downloads.arduino.cc/CMSIS-4.5.0.tar.bz2", - "archiveFileName": "CMSIS-4.5.0.tar.bz2", - "checksum": "SHA-256:cd8f7eae9fc7c8b4a1b5e40b89b9666d33953b47d3d2eb81844f5af729fa224d", - "size": "31525196" - }, - { - "host": "aarch64-linux-gnu", - "url": "http://downloads.arduino.cc/CMSIS-4.5.0.tar.bz2", - "archiveFileName": "CMSIS-4.5.0.tar.bz2", - "checksum": "SHA-256:cd8f7eae9fc7c8b4a1b5e40b89b9666d33953b47d3d2eb81844f5af729fa224d", - "size": "31525196" - }, - { - "host": "all", - "url": "http://downloads.arduino.cc/CMSIS-4.5.0.tar.bz2", - "archiveFileName": "CMSIS-4.5.0.tar.bz2", - "checksum": "SHA-256:cd8f7eae9fc7c8b4a1b5e40b89b9666d33953b47d3d2eb81844f5af729fa224d", - "size": "31525196" - } - ] - }, - { - "name": "CMSIS-Atmel", - "version": "1.0.0", - "systems": [ - { - "host": "i686-mingw32", - "url": "http://downloads.arduino.cc/CMSIS-Atmel-1.0.0.tar.bz2", - "archiveFileName": "CMSIS-Atmel-1.0.0.tar.bz2", - "checksum": "SHA-256:b3c954570a2f8d9821c372e0864f5f0b86cfbeab8114ce95821f5c49758c7256", - "size": "1281654" - }, - { - "host": "x86_64-apple-darwin", - "url": "http://downloads.arduino.cc/CMSIS-Atmel-1.0.0.tar.bz2", - "archiveFileName": "CMSIS-Atmel-1.0.0.tar.bz2", - "checksum": "SHA-256:b3c954570a2f8d9821c372e0864f5f0b86cfbeab8114ce95821f5c49758c7256", - "size": "1281654" - }, - { - "host": "x86_64-pc-linux-gnu", - "url": "http://downloads.arduino.cc/CMSIS-Atmel-1.0.0.tar.bz2", - "archiveFileName": "CMSIS-Atmel-1.0.0.tar.bz2", - "checksum": "SHA-256:b3c954570a2f8d9821c372e0864f5f0b86cfbeab8114ce95821f5c49758c7256", - "size": "1281654" - }, - { - "host": "i686-pc-linux-gnu", - "url": "http://downloads.arduino.cc/CMSIS-Atmel-1.0.0.tar.bz2", - "archiveFileName": "CMSIS-Atmel-1.0.0.tar.bz2", - "checksum": "SHA-256:b3c954570a2f8d9821c372e0864f5f0b86cfbeab8114ce95821f5c49758c7256", - "size": "1281654" - }, - { - "host": "arm-linux-gnueabihf", - "url": "http://downloads.arduino.cc/CMSIS-Atmel-1.0.0.tar.bz2", - "archiveFileName": "CMSIS-Atmel-1.0.0.tar.bz2", - "checksum": "SHA-256:b3c954570a2f8d9821c372e0864f5f0b86cfbeab8114ce95821f5c49758c7256", - "size": "1281654" - }, - { - "host": "all", - "url": "http://downloads.arduino.cc/CMSIS-Atmel-1.0.0.tar.bz2", - "archiveFileName": "CMSIS-Atmel-1.0.0.tar.bz2", - "checksum": "SHA-256:b3c954570a2f8d9821c372e0864f5f0b86cfbeab8114ce95821f5c49758c7256", - "size": "1281654" - } - ] - }, - { - "name": "CMSIS-Atmel", - "version": "1.1.0", - "systems": [ - { - "host": "i686-mingw32", - "url": "http://downloads.arduino.cc/CMSIS-Atmel-1.1.0.tar.bz2", - "archiveFileName": "CMSIS-Atmel-1.1.0.tar.bz2", - "checksum": "SHA-256:3ea5ec0451f42dc2b97f869b027a9cf696241cfc927cfc48d74ccc7b396ba41b", - "size": "1659108" - }, - { - "host": "x86_64-apple-darwin", - "url": "http://downloads.arduino.cc/CMSIS-Atmel-1.1.0.tar.bz2", - "archiveFileName": "CMSIS-Atmel-1.1.0.tar.bz2", - "checksum": "SHA-256:3ea5ec0451f42dc2b97f869b027a9cf696241cfc927cfc48d74ccc7b396ba41b", - "size": "1659108" - }, - { - "host": "x86_64-pc-linux-gnu", - "url": "http://downloads.arduino.cc/CMSIS-Atmel-1.1.0.tar.bz2", - "archiveFileName": "CMSIS-Atmel-1.1.0.tar.bz2", - "checksum": "SHA-256:3ea5ec0451f42dc2b97f869b027a9cf696241cfc927cfc48d74ccc7b396ba41b", - "size": "1659108" - }, - { - "host": "i686-pc-linux-gnu", - "url": "http://downloads.arduino.cc/CMSIS-Atmel-1.1.0.tar.bz2", - "archiveFileName": "CMSIS-Atmel-1.1.0.tar.bz2", - "checksum": "SHA-256:3ea5ec0451f42dc2b97f869b027a9cf696241cfc927cfc48d74ccc7b396ba41b", - "size": "1659108" - }, - { - "host": "arm-linux-gnueabihf", - "url": "http://downloads.arduino.cc/CMSIS-Atmel-1.1.0.tar.bz2", - "archiveFileName": "CMSIS-Atmel-1.1.0.tar.bz2", - "checksum": "SHA-256:3ea5ec0451f42dc2b97f869b027a9cf696241cfc927cfc48d74ccc7b396ba41b", - "size": "1659108" - }, - { - "host": "all", - "url": "http://downloads.arduino.cc/CMSIS-Atmel-1.1.0.tar.bz2", - "archiveFileName": "CMSIS-Atmel-1.1.0.tar.bz2", - "checksum": "SHA-256:3ea5ec0451f42dc2b97f869b027a9cf696241cfc927cfc48d74ccc7b396ba41b", - "size": "1659108" - } - ] - }, - { - "name": "CMSIS-Atmel", - "version": "1.2.0", - "systems": [ - { - "host": "i686-mingw32", - "url": "http://downloads.arduino.cc/CMSIS-Atmel-1.2.0.tar.bz2", - "archiveFileName": "CMSIS-Atmel-1.2.0.tar.bz2", - "checksum": "SHA-256:5e02670be7e36be9691d059bee0b04ee8b249404687531f33893922d116b19a5", - "size": "2221805" - }, - { - "host": "x86_64-apple-darwin", - "url": "http://downloads.arduino.cc/CMSIS-Atmel-1.2.0.tar.bz2", - "archiveFileName": "CMSIS-Atmel-1.2.0.tar.bz2", - "checksum": "SHA-256:5e02670be7e36be9691d059bee0b04ee8b249404687531f33893922d116b19a5", - "size": "2221805" - }, - { - "host": "x86_64-pc-linux-gnu", - "url": "https://downloads.arduino.cc/CMSIS-Atmel-1.2.0.tar.bz2", - "archiveFileName": "CMSIS-Atmel-1.2.0.tar.bz2", - "checksum": "SHA-256:5e02670be7e36be9691d059bee0b04ee8b249404687531f33893922d116b19a5", - "size": "2221805" - }, - { - "host": "i686-pc-linux-gnu", - "url": "https://downloads.arduino.cc/CMSIS-Atmel-1.2.0.tar.bz2", - "archiveFileName": "CMSIS-Atmel-1.2.0.tar.bz2", - "checksum": "SHA-256:5e02670be7e36be9691d059bee0b04ee8b249404687531f33893922d116b19a5", - "size": "2221805" - }, - { - "host": "arm-linux-gnueabihf", - "url": "https://downloads.arduino.cc/CMSIS-Atmel-1.2.0.tar.bz2", - "archiveFileName": "CMSIS-Atmel-1.2.0.tar.bz2", - "checksum": "SHA-256:5e02670be7e36be9691d059bee0b04ee8b249404687531f33893922d116b19a5", - "size": "2221805" - }, - { - "host": "aarch64-linux-gnu", - "url": "https://downloads.arduino.cc/CMSIS-Atmel-1.2.0.tar.bz2", - "archiveFileName": "CMSIS-Atmel-1.2.0.tar.bz2", - "checksum": "SHA-256:5e02670be7e36be9691d059bee0b04ee8b249404687531f33893922d116b19a5", - "size": "2221805" - }, - { - "host": "all", - "url": "https://downloads.arduino.cc/CMSIS-Atmel-1.2.0.tar.bz2", - "archiveFileName": "CMSIS-Atmel-1.2.0.tar.bz2", - "checksum": "SHA-256:5e02670be7e36be9691d059bee0b04ee8b249404687531f33893922d116b19a5", - "size": "2221805" - } - ] - }, - { - "name": "dfu-util", - "version": "0.9.0-arduino1", - "systems": [ - { - "host": "i386-apple-darwin11", - "url": "http://downloads.arduino.cc/tools/dfu-util-0.9.0-arduino1-osx.tar.bz2", - "archiveFileName": "dfu-util-0.9.0-arduino1-osx.tar.bz2", - "size": "68361", - "checksum": "SHA-256:ea9216c627b7aa2d3a9bffab97df937e3c580cce66753c428dc697c854a35271" - }, - { - "host": "arm-linux-gnueabihf", - "url": "http://downloads.arduino.cc/tools/dfu-util-0.9.0-arduino1-arm.tar.bz2", - "archiveFileName": "dfu-util-0.9.0-arduino1-arm.tar.bz2", - "size": "194826", - "checksum": "SHA-256:480637bf578e74b19753666a049f267d8ebcd9dfc8660d48f246bb76d5b806f9" - }, - { - "host": "x86_64-linux-gnu", - "url": "http://downloads.arduino.cc/tools/dfu-util-0.9.0-arduino1-linux64.tar.bz2", - "archiveFileName": "dfu-util-0.9.0-arduino1-linux64.tar.bz2", - "size": "66230", - "checksum": "SHA-256:e8a4d5477ab8c44d8528f35bc7dfafa5f3f04dace513906514aea31adc6fd3ba" - }, - { - "host": "i686-linux-gnu", - "url": "http://downloads.arduino.cc/tools/dfu-util-0.9.0-arduino1-linux32.tar.bz2", - "archiveFileName": "dfu-util-0.9.0-arduino1-linux32.tar.bz2", - "size": "62608", - "checksum": "SHA-256:17d69213914da04dadd6464d8adbcd3581dd930eb666b8f3336ab5383ce2127f" - }, - { - "host": "i686-mingw32", - "url": "http://downloads.arduino.cc/tools/dfu-util-0.9.0-arduino1-windows.tar.bz2", - "archiveFileName": "dfu-util-0.9.0-arduino1-windows.tar.bz2", - "size": "377537", - "checksum": "SHA-256:29be01b298348be8b822391be7147b71a969d47bd5457d5b24cfa5981dbce78e" - } - ] - }, - { - "name": "dfu-util", - "version": "0.9.0-arduino2", - "systems": [ - { - "host": "i386-apple-darwin11", - "url": "http://downloads.arduino.cc/tools/dfu-util-0.9.0-arduino2-osx.tar.bz2", - "archiveFileName": "dfu-util-0.9.0-arduino2-osx.tar.bz2", - "size": "65137", - "checksum": "SHA-256:00e87178b81d5721f334d4b688267f19f36eed1d9710a912c9e44bb1a748f766" - }, - { - "host": "arm-linux-gnueabihf", - "url": "http://downloads.arduino.cc/tools/dfu-util-0.9.0-arduino2-arm.tar.bz2", - "archiveFileName": "dfu-util-0.9.0-arduino2-arm.tar.bz2", - "size": "198568", - "checksum": "SHA-256:b364a8fe1de697d7dd6c4135d341ddff6dbda7e33c707321c7dceab85dfc560b" - }, - { - "host": "x86_64-linux-gnu", - "url": "http://downloads.arduino.cc/tools/dfu-util-0.9.0-arduino2-linux64.tar.bz2", - "archiveFileName": "dfu-util-0.9.0-arduino2-linux64.tar.bz2", - "size": "70996", - "checksum": "SHA-256:628e01772007e622dff6af82220c27bcdf1d0726ba886bd2b36807601f66e4e8" - }, - { - "host": "i686-linux-gnu", - "url": "http://downloads.arduino.cc/tools/dfu-util-0.9.0-arduino2-linux32.tar.bz2", - "archiveFileName": "dfu-util-0.9.0-arduino2-linux32.tar.bz2", - "size": "71002", - "checksum": "SHA-256:7a6cec3d89c65119c52b6109cd92a9ec84bdf8a9d12083444d2c89e7ac16c84b" - }, - { - "host": "i686-mingw32", - "url": "http://downloads.arduino.cc/tools/dfu-util-0.9.0-arduino2-windows.tar.bz2", - "archiveFileName": "dfu-util-0.9.0-arduino2-windows.tar.bz2", - "size": "394993", - "checksum": "SHA-256:8ec0e66acdc41941b6e25545f8c12e7eb7ba01a0bafae0b4ab4c99a70deb2ea5" - } - ] - }, - { - "name": "windows-drivers", - "version": "1.6.9", - "systems": [ - { - "host": "i686-mingw32", - "url": "http://downloads.arduino.cc/tools/drivers-arduino-windows-1.6.9.zip", - "archiveFileName": "drivers-arduino-windows-1.6.9.zip", - "checksum": "SHA-256:10d456ab18d164d42545255db8bef4ac9e1bf660cc89acb7a0980b5a486654ac", - "size": "7071714" - } - ] - }, - { - "name": "windows-drivers", - "version": "1.8.0", - "systems": [ - { - "host": "i686-mingw32", - "url": "http://downloads.arduino.cc/tools/drivers-arduino-windows-1.8.0.zip", - "archiveFileName": "drivers-arduino-windows-1.8.0.zip", - "checksum": "SHA-256:60614b326ad6860ed0cb99eb4cb2cb69f9ba6ba3784396d5441fe3f99004f8ec", - "size": "16302148" - } - ] - }, - { - "name": "dfu-util", - "version": "0.8.0-stm32-arduino1", - "systems": [ - { - "archiveFileName": "dfu-util-0.8.0-stm32-arduino1-darwin_amd64.tar.bz2", - "checksum": "SHA-256:bb146803a4152ce2647d72b2cde68ff95eb3017c2460f24c4db922adac1fbd12", - "host": "i386-apple-darwin11", - "size": "68381", - "url": "http://downloads.arduino.cc/arduino.org/dfu-util-0.8.0-stm32-arduino1-darwin_amd64.tar.bz2" - }, - { - "archiveFileName": "dfu-util-0.8.0-stm32-arduino1-linux_arm.tar.bz2", - "checksum": "SHA-256:607e6b0f2d2787ed7837f26da30b100131e3db207f84b8aca94a377db6e9ae50", - "host": "arm-linux-gnueabihf", - "size": "213760", - "url": "http://downloads.arduino.cc/arduino.org/dfu-util-0.8.0-stm32-arduino1-linux_arm.tar.bz2" - }, - { - "archiveFileName": "dfu-util-0.8.0-stm32-arduino1-stm32-linux_amd64.tar.bz2", - "checksum": "SHA-256:e44287494ebd22f59fc79766a94e20306e59c6c799f5bb1cddeed80db95000d9", - "host": "x86_64-linux-gnu", - "size": "68575", - "url": "http://downloads.arduino.cc/arduino.org/dfu-util-0.8.0-stm32-arduino1-linux_amd64.tar.bz2" - }, - { - "archiveFileName": "dfu-util-0.8.0-stm32-arduino1-linux_386.tar.bz2", - "checksum": "SHA-256:58131e35ad5d7053b281bc6176face7b117c5ad63331e43c6801f8ccd57f59a4", - "host": "i686-linux-gnu", - "size": "69097", - "url": "http://downloads.arduino.cc/arduino.org/dfu-util-0.8.0-stm32-arduino1-linux_386.tar.bz2" - }, - { - "archiveFileName": "dfu-util-0.8.0-stm32-arduino1-windows_386.tar.bz2", - "checksum": "SHA-256:25c2f84e1acf1f10fd2aa1afced441366d4545fd41eae56e64f0b990b4ce9f55", - "host": "i686-mingw32", - "size": "159753", - "url": "http://downloads.arduino.cc/arduino.org/dfu-util-0.8.0-stm32-arduino1-windows_386.tar.bz2" - } - ] - }, - { - "name": "arduinoSTM32load", - "version": "2.0.0", - "systems": [ - { - "archiveFileName": "arduinoSTM32load-2.0.0-darwin_amd64.tar.bz2", - "checksum": "SHA-256:92fb9714091850febaa9d159501cbca5ba68d03020e5e2d4eff596154040bfaa", - "host": "i386-apple-darwin11", - "size": "807514", - "url": "http://downloads.arduino.cc/arduino.org/arduinoSTM32load-2.0.0-darwin_amd64.tar.bz2" - }, - { - "archiveFileName": "arduinoSTM32load-2.0.0-linux_arm.tar.bz2", - "checksum": "SHA-256:fc0d8058b57bda849e1ffc849f83f54b0b85f97954176db317da1c745c174e08", - "host": "arm-linux-gnueabihf", - "size": "809480", - "url": "http://downloads.arduino.cc/arduino.org/arduinoSTM32load-2.0.0-linux_arm.tar.bz2" - }, - { - "archiveFileName": "arduinoSTM32load-2.0.0-linux_amd64.tar.bz2", - "checksum": "SHA-256:0ed5cf1ea05fe6c33567817c54daf9c296d058a3607c428e0b0bd9aad89b9809", - "host": "x86_64-linux-gnu", - "size": "818885", - "url": "http://downloads.arduino.cc/arduino.org/arduinoSTM32load-2.0.0-linux_amd64.tar.bz2" - }, - { - "archiveFileName": "arduinoSTM32load-2.0.0-linux_386.tar.bz2", - "checksum": "SHA-256:fad50abaaca034e6d647d09b042291b761982aabfd42b6156411c86e4f873ca7", - "host": "i686-linux-gnu", - "size": "814283", - "url": "http://downloads.arduino.cc/arduino.org/arduinoSTM32load-2.0.0-linux_386.tar.bz2" - }, - { - "archiveFileName": "arduinoSTM32load-2.0.0-windows_386.tar.bz2", - "checksum": "SHA-256:79467c0cde4b88c4884acb09445a2186af4e41f901eee56e99b5d89b7065d085", - "host": "i686-mingw32", - "size": "786335", - "url": "http://downloads.arduino.cc/arduino.org/arduinoSTM32load-2.0.0-windows_386.tar.bz2" - } - ] - }, - { - "name": "openocd", - "version": "0.10.0-arduino1-static", - "systems": [ - { - "host": "i386-apple-darwin11", - "url": "http://downloads.arduino.cc/arduino.org/OpenOCD-0.10.0-nrf52-osx-static.tar.gz", - "archiveFileName": "OpenOCD-0.10.0-nrf52-osx-static.tar.gz", - "size": "1529841", - "checksum": "SHA-256:46bd02c1d42c5d94c4936e4d4a0ff29697b621840be9a6f882e316203122049d" - }, - { - "host": "x86_64-linux-gnu", - "url": "http://downloads.arduino.cc/arduino.org/OpenOCD-0.10.0-nrf52-linux64-static.tar.gz", - "archiveFileName": "OpenOCD-0.10.0-nrf52-linux64-static.tar.gz", - "size": "1777984", - "checksum": "SHA-256:1c9ae77930dd7377d8c13f84abe7307b67fdcd6da74cc1ce269a79e138e7a00a" - }, - { - "host": "i686-linux-gnu", - "url": "http://downloads.arduino.cc/arduino.org/OpenOCD-0.10.0-nrf52-linux32-static.tar.gz", - "archiveFileName": "OpenOCD-0.10.0-nrf52-linux32-static.tar.gz", - "size": "1713236", - "checksum": "SHA-256:777371df34828810e1bea623b0f7c98f28fedf30fd3bc8e7d8f0a5745fb4e258" - }, - { - "host": "i686-mingw32", - "url": "http://downloads.arduino.cc/arduino.org/OpenOCD-0.10.0-nrf52-win32-static.zip", - "archiveFileName": "OpenOCD-0.10.0-nrf52-win32-static.zip", - "size": "1773642", - "checksum": "SHA-256:9371b25d000bd589c058a5bf10720617adb91fd8b8a21d2e887cf45eaa2df93c" - }, - { - "host": "arm-linux-gnueabihf", - "url": "http://downloads.arduino.cc/arduino.org/OpenOCD-0.10.0-nrf52-arm-static.tar.gz", - "archiveFileName": "OpenOCD-0.10.0-nrf52-arm-static.tar.gz", - "size": "1526863", - "checksum": "SHA-256:b5172422077f87ff05b76ff40034979678c9c640e9d08cee15ce55e40dd8c929" - } - ] - }, - { - "name": "fwupdater", - "version": "0.0.6", - "systems": [ - { - "host": "i686-linux-gnu", - "url": "http://downloads.arduino.cc/tools/FirmwareUpdater-0.0.6-linux32.tar.bz2", - "archiveFileName": "FirmwareUpdater-0.0.6-linux32.tar.bz2", - "checksum": "SHA-256:8c4e562a8e6fa3d916c4bf6bb24d7eec0df013d8cc45dff187e5c63086a92c11", - "size": "7334059" - }, - { - "host": "x86_64-pc-linux-gnu", - "url": "http://downloads.arduino.cc/tools/FirmwareUpdater-0.0.6-linux64.tar.bz2", - "archiveFileName": "FirmwareUpdater-0.0.6-linux64.tar.bz2", - "checksum": "SHA-256:0e9132518acfe66e5a4e17ba4b6dd0c89dbd90cc0d9b4a54e007ac24d51fd36a", - "size": "7383853" - }, - { - "host": "i686-mingw32", - "url": "http://downloads.arduino.cc/tools/FirmwareUpdater-0.0.6-windows.zip", - "archiveFileName": "FirmwareUpdater-0.0.6-windows.zip", - "checksum": "SHA-256:33a92661f43b8d025ca5f57be1116537ed153703067d9c86297619d58988feff", - "size": "7580663" - }, - { - "host": "i386-apple-darwin11", - "url": "http://downloads.arduino.cc/tools/FirmwareUpdater-0.0.6-osx.tar.bz2", - "archiveFileName": "FirmwareUpdater-0.0.6-osx.tar.bz2", - "checksum": "SHA-256:3584d7581ff2469bdfde9de6f57d87b2a0370de5c902e9df687b7322a5405018", - "size": "7368819" - }, - { - "host": "arm-linux-gnueabihf", - "url": "http://downloads.arduino.cc/tools/FirmwareUpdater-0.0.6-linuxarm.tar.bz2", - "archiveFileName": "FirmwareUpdater-0.0.6-linuxarm.tar.bz2", - "checksum": "SHA-256:276f027e552eb620064b09591c7a7c79927c93c017428436c81f71bad666803c", - "size": "7180391" - }, - { - "host": "aarch64-linux-gnu", - "url": "http://downloads.arduino.cc/tools/FirmwareUpdater-0.0.6-linuxarm64.tar.bz2", - "archiveFileName": "FirmwareUpdater-0.0.6-linuxarm64.tar.bz2", - "checksum": "SHA-256:d6a2e72b4869c031b434380da6e6a8a4a6269f8ee6312a3d23e85ea133a37ae9", - "size": "7149332" - } - ] - }, - { - "name": "fwupdater", - "version": "0.0.7", - "systems": [ - { - "host": "i686-linux-gnu", - "url": "http://downloads.arduino.cc/tools/FirmwareUpdater-0.0.7-linux32.tar.bz2", - "archiveFileName": "FirmwareUpdater-0.0.7-linux32.tar.bz2", - "checksum": "SHA-256:05e26d5df253246cf68ba8a8f5f2de1032ac6f4d7af5310b8080ef6f54030fb4", - "size": "13077590" - }, - { - "host": "x86_64-pc-linux-gnu", - "url": "http://downloads.arduino.cc/tools/FirmwareUpdater-0.0.7-linux64.tar.bz2", - "archiveFileName": "FirmwareUpdater-0.0.7-linux64.tar.bz2", - "checksum": "SHA-256:e8a36b7ae19060748b8b0540c9c0c9341d8d4c7a630441922a030223f767258d", - "size": "13134534" - }, - { - "host": "i686-mingw32", - "url": "http://downloads.arduino.cc/tools/FirmwareUpdater-0.0.7-windows.zip", - "archiveFileName": "FirmwareUpdater-0.0.7-windows.zip", - "checksum": "SHA-256:e881dd8a4bbb35f7271157e8dd226c2e032ac1115106170b0aaf9c969817df86", - "size": "13337495" - }, - { - "host": "i386-apple-darwin11", - "url": "http://downloads.arduino.cc/tools/FirmwareUpdater-0.0.7-osx.tar.bz2", - "archiveFileName": "FirmwareUpdater-0.0.7-osx.tar.bz2", - "checksum": "SHA-256:647ad713d68a1531ba56c88e5b3db9516cc0072ca1d401de5142d85ffcfd931a", - "size": "13064291" - }, - { - "host": "arm-linux-gnueabihf", - "url": "http://downloads.arduino.cc/tools/FirmwareUpdater-0.0.7-linuxarm.tar.bz2", - "archiveFileName": "FirmwareUpdater-0.0.7-linuxarm.tar.bz2", - "checksum": "SHA-256:b6e09c07e816a35935585db07e07437023d44e66d1d2f05861708ea6ceff7629", - "size": "12929770" - }, - { - "host": "aarch64-linux-gnu", - "url": "http://downloads.arduino.cc/tools/FirmwareUpdater-0.0.7-linuxarm64.tar.bz2", - "archiveFileName": "FirmwareUpdater-0.0.7-linuxarm64.tar.bz2", - "checksum": "SHA-256:620f81148b13f70cdecbe59bbcbf605c7e0aae85f9cbee8ec48202f6018f23ce", - "size": "12897349" - } - ] - }, - { - "name": "nrf5x-cl-tools", - "version": "9.3.1", - "systems": [ - { - "host": "i386-apple-darwin11", - "url": "http://downloads.arduino.cc/arduino.org/nRF5x-Command-Line-Tools_9_3_1_OSX.tar.bz2", - "archiveFileName": "nRF5x-Command-Line-Tools_9_3_1_OSX.tar.bz2", - "size": "341674", - "checksum": "SHA-256:41e4580271b39459a7ef1b078d11ee08d8f4f23fab7ff03f3fe8c3bc986a0ed4" - }, - { - "host": "x86_64-linux-gnu", - "url": "http://downloads.arduino.cc/arduino.org/nRF5x-Command-Line-Tools_9_3_1_Linux-x86_64.tar.bz2", - "archiveFileName": "nRF5x-Command-Line-Tools_9_3_1_Linux-x86_64.tar.bz2", - "size": "167414", - "checksum": "SHA-256:4074fffe678d60968006a72edd182c6506b264472c9957bc3eaa39336bfcf972" - }, - { - "host": "i686-linux-gnu", - "url": "http://downloads.arduino.cc/arduino.org/nRF5x-Command-Line-Tools_9_3_1_Linux-i386.tar.bz2", - "archiveFileName": "nRF5x-Command-Line-Tools_9_3_1_Linux-i386.tar.bz2", - "size": "155680", - "checksum": "SHA-256:e880059b303e5aad3a8b34c83dfd8c22beee77ae2074fbd37511e3baa91464a5" - }, - { - "host": "i686-mingw32", - "url": "http://downloads.arduino.cc/arduino.org/nRF5x-Command-Line-Tools_9_3_1_Win32.tar.bz2", - "archiveFileName": "nRF5x-Command-Line-Tools_9_3_1_Win32.tar.bz2", - "size": "812257", - "checksum": "SHA-256:a4467350e39314690cec2e96b80e7e3cab463c84eff9b81593ad57754d76ee00" - } - ] - } - ] - } - ] -} diff --git a/commands/cmderrors/cmderrors.go b/commands/cmderrors/cmderrors.go index e6794ffdee9..a5024a37a26 100644 --- a/commands/cmderrors/cmderrors.go +++ b/commands/cmderrors/cmderrors.go @@ -37,8 +37,8 @@ func composeErrorMsg(msg string, cause error) string { // CommandError is an error that may be converted into a gRPC status. type CommandError interface { - // ToRPCStatus convertes the error into a *status.Status - ToRPCStatus() *status.Status + // GRPCStatus convertes the error into a *status.Status + GRPCStatus() *status.Status } // InvalidInstanceError is returned if the instance used in the command is not valid. @@ -48,8 +48,8 @@ func (e *InvalidInstanceError) Error() string { return tr("Invalid instance") } -// ToRPCStatus converts the error into a *status.Status -func (e *InvalidInstanceError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *InvalidInstanceError) GRPCStatus() *status.Status { return status.New(codes.InvalidArgument, e.Error()) } @@ -62,8 +62,8 @@ func (e *InvalidFQBNError) Error() string { return composeErrorMsg(tr("Invalid FQBN"), e.Cause) } -// ToRPCStatus converts the error into a *status.Status -func (e *InvalidFQBNError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *InvalidFQBNError) GRPCStatus() *status.Status { return status.New(codes.InvalidArgument, e.Error()) } @@ -80,8 +80,8 @@ func (e *InvalidURLError) Error() string { return composeErrorMsg(tr("Invalid URL"), e.Cause) } -// ToRPCStatus converts the error into a *status.Status -func (e *InvalidURLError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *InvalidURLError) GRPCStatus() *status.Status { return status.New(codes.InvalidArgument, e.Error()) } @@ -98,8 +98,8 @@ func (e *InvalidLibraryError) Error() string { return composeErrorMsg(tr("Invalid library"), e.Cause) } -// ToRPCStatus converts the error into a *status.Status -func (e *InvalidLibraryError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *InvalidLibraryError) GRPCStatus() *status.Status { return status.New(codes.InvalidArgument, e.Error()) } @@ -116,8 +116,8 @@ func (e *InvalidVersionError) Error() string { return composeErrorMsg(tr("Invalid version"), e.Cause) } -// ToRPCStatus converts the error into a *status.Status -func (e *InvalidVersionError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *InvalidVersionError) GRPCStatus() *status.Status { return status.New(codes.InvalidArgument, e.Error()) } @@ -139,8 +139,8 @@ func (e *NoBoardsDetectedError) Error() string { ) } -// ToRPCStatus converts the error into a *status.Status -func (e *NoBoardsDetectedError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *NoBoardsDetectedError) GRPCStatus() *status.Status { return status.New(codes.InvalidArgument, e.Error()) } @@ -159,8 +159,8 @@ func (e *MultipleBoardsDetectedError) Error() string { ) } -// ToRPCStatus converts the error into a *status.Status -func (e *MultipleBoardsDetectedError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *MultipleBoardsDetectedError) GRPCStatus() *status.Status { return status.New(codes.InvalidArgument, e.Error()) } @@ -171,8 +171,8 @@ func (e *MissingFQBNError) Error() string { return tr("Missing FQBN (Fully Qualified Board Name)") } -// ToRPCStatus converts the error into a *status.Status -func (e *MissingFQBNError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *MissingFQBNError) GRPCStatus() *status.Status { return status.New(codes.InvalidArgument, e.Error()) } @@ -189,8 +189,8 @@ func (e *UnknownFQBNError) Unwrap() error { return e.Cause } -// ToRPCStatus converts the error into a *status.Status -func (e *UnknownFQBNError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *UnknownFQBNError) GRPCStatus() *status.Status { return status.New(codes.NotFound, e.Error()) } @@ -208,8 +208,8 @@ func (e *UnknownProfileError) Unwrap() error { return e.Cause } -// ToRPCStatus converts the error into a *status.Status -func (e *UnknownProfileError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *UnknownProfileError) GRPCStatus() *status.Status { return status.New(codes.NotFound, e.Error()) } @@ -226,8 +226,8 @@ func (e *InvalidProfileError) Unwrap() error { return e.Cause } -// ToRPCStatus converts the error into a *status.Status -func (e *InvalidProfileError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *InvalidProfileError) GRPCStatus() *status.Status { return status.New(codes.FailedPrecondition, e.Error()) } @@ -238,8 +238,8 @@ func (e *MissingPortAddressError) Error() string { return tr("Missing port address") } -// ToRPCStatus converts the error into a *status.Status -func (e *MissingPortAddressError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *MissingPortAddressError) GRPCStatus() *status.Status { return status.New(codes.InvalidArgument, e.Error()) } @@ -250,8 +250,8 @@ func (e *MissingPortProtocolError) Error() string { return tr("Missing port protocol") } -// ToRPCStatus converts the error into a *status.Status -func (e *MissingPortProtocolError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *MissingPortProtocolError) GRPCStatus() *status.Status { return status.New(codes.InvalidArgument, e.Error()) } @@ -262,8 +262,8 @@ func (e *MissingPortError) Error() string { return tr("Missing port") } -// ToRPCStatus converts the error into a *status.Status -func (e *MissingPortError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *MissingPortError) GRPCStatus() *status.Status { return status.New(codes.InvalidArgument, e.Error()) } @@ -276,8 +276,8 @@ func (e *NoMonitorAvailableForProtocolError) Error() string { return tr("No monitor available for the port protocol %s", e.Protocol) } -// ToRPCStatus converts the error into a *status.Status -func (e *NoMonitorAvailableForProtocolError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *NoMonitorAvailableForProtocolError) GRPCStatus() *status.Status { return status.New(codes.InvalidArgument, e.Error()) } @@ -288,8 +288,8 @@ func (e *MissingProgrammerError) Error() string { return tr("Missing programmer") } -// ToRPCStatus converts the error into a *status.Status -func (e *MissingProgrammerError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *MissingProgrammerError) GRPCStatus() *status.Status { s, _ := status.New(codes.InvalidArgument, e.Error()).WithDetails(&rpc.MissingProgrammerError{}) return s } @@ -301,8 +301,8 @@ func (e *ProgrammerRequiredForUploadError) Error() string { return tr("A programmer is required to upload") } -// ToRPCStatus converts the error into a *status.Status -func (e *ProgrammerRequiredForUploadError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *ProgrammerRequiredForUploadError) GRPCStatus() *status.Status { st, _ := status. New(codes.InvalidArgument, e.Error()). WithDetails(&rpc.ProgrammerIsRequiredForUploadError{}) @@ -320,8 +320,8 @@ func (ife *InitFailedError) Error() string { return ife.Cause.Error() } -// ToRPCStatus converts the error into a *status.Status -func (ife *InitFailedError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (ife *InitFailedError) GRPCStatus() *status.Status { st, _ := status. New(ife.Code, ife.Cause.Error()). WithDetails(&rpc.FailedInstanceInitError{ @@ -345,8 +345,8 @@ func (e *ProgrammerNotFoundError) Unwrap() error { return e.Cause } -// ToRPCStatus converts the error into a *status.Status -func (e *ProgrammerNotFoundError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *ProgrammerNotFoundError) GRPCStatus() *status.Status { return status.New(codes.NotFound, e.Error()) } @@ -364,8 +364,8 @@ func (e *MonitorNotFoundError) Unwrap() error { return e.Cause } -// ToRPCStatus converts the error into a *status.Status -func (e *MonitorNotFoundError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *MonitorNotFoundError) GRPCStatus() *status.Status { return status.New(codes.NotFound, e.Error()) } @@ -379,8 +379,8 @@ func (e *InvalidPlatformPropertyError) Error() string { return tr("Invalid '%[1]s' property: %[2]s", e.Property, e.Value) } -// ToRPCStatus converts the error into a *status.Status -func (e *InvalidPlatformPropertyError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *InvalidPlatformPropertyError) GRPCStatus() *status.Status { return status.New(codes.FailedPrecondition, e.Error()) } @@ -393,8 +393,8 @@ func (e *MissingPlatformPropertyError) Error() string { return tr("Property '%s' is undefined", e.Property) } -// ToRPCStatus converts the error into a *status.Status -func (e *MissingPlatformPropertyError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *MissingPlatformPropertyError) GRPCStatus() *status.Status { return status.New(codes.FailedPrecondition, e.Error()) } @@ -408,8 +408,8 @@ func (e *PlatformNotFoundError) Error() string { return composeErrorMsg(tr("Platform '%s' not found", e.Platform), e.Cause) } -// ToRPCStatus converts the error into a *status.Status -func (e *PlatformNotFoundError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *PlatformNotFoundError) GRPCStatus() *status.Status { return status.New(codes.FailedPrecondition, e.Error()) } @@ -426,8 +426,8 @@ func (e *PlatformLoadingError) Error() string { return composeErrorMsg(tr("Error loading hardware platform"), e.Cause) } -// ToRPCStatus converts the error into a *status.Status -func (e *PlatformLoadingError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *PlatformLoadingError) GRPCStatus() *status.Status { s, _ := status.New(codes.FailedPrecondition, e.Error()). WithDetails(&rpc.PlatformLoadingError{}) return s @@ -447,8 +447,8 @@ func (e *LibraryNotFoundError) Error() string { return composeErrorMsg(tr("Library '%s' not found", e.Library), e.Cause) } -// ToRPCStatus converts the error into a *status.Status -func (e *LibraryNotFoundError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *LibraryNotFoundError) GRPCStatus() *status.Status { return status.New(codes.FailedPrecondition, e.Error()) } @@ -466,8 +466,8 @@ func (e *LibraryDependenciesResolutionFailedError) Error() string { return composeErrorMsg(tr("No valid dependencies solution found"), e.Cause) } -// ToRPCStatus converts the error into a *status.Status -func (e *LibraryDependenciesResolutionFailedError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *LibraryDependenciesResolutionFailedError) GRPCStatus() *status.Status { return status.New(codes.FailedPrecondition, e.Error()) } @@ -484,8 +484,8 @@ func (e *PlatformAlreadyAtTheLatestVersionError) Error() string { return tr("Platform '%s' is already at the latest version", e.Platform) } -// ToRPCStatus converts the error into a *status.Status -func (e *PlatformAlreadyAtTheLatestVersionError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *PlatformAlreadyAtTheLatestVersionError) GRPCStatus() *status.Status { st, _ := status. New(codes.AlreadyExists, e.Error()). WithDetails(&rpc.AlreadyAtLatestVersionError{}) @@ -499,8 +499,8 @@ func (e *MissingSketchPathError) Error() string { return tr("Missing sketch path") } -// ToRPCStatus converts the error into a *status.Status -func (e *MissingSketchPathError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *MissingSketchPathError) GRPCStatus() *status.Status { return status.New(codes.InvalidArgument, e.Error()) } @@ -543,8 +543,8 @@ func (e *CantOpenSketchError) Unwrap() error { return e.Cause } -// ToRPCStatus converts the error into a *status.Status -func (e *CantOpenSketchError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *CantOpenSketchError) GRPCStatus() *status.Status { return status.New(codes.NotFound, e.Error()) } @@ -562,8 +562,8 @@ func (e *FailedInstallError) Unwrap() error { return e.Cause } -// ToRPCStatus converts the error into a *status.Status -func (e *FailedInstallError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *FailedInstallError) GRPCStatus() *status.Status { return status.New(codes.Internal, e.Error()) } @@ -580,8 +580,8 @@ func (e *FailedLibraryInstallError) Unwrap() error { return e.Cause } -// ToRPCStatus converts the error into a *status.Status -func (e *FailedLibraryInstallError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *FailedLibraryInstallError) GRPCStatus() *status.Status { return status.New(codes.Internal, e.Error()) } @@ -599,8 +599,8 @@ func (e *FailedUninstallError) Unwrap() error { return e.Cause } -// ToRPCStatus converts the error into a *status.Status -func (e *FailedUninstallError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *FailedUninstallError) GRPCStatus() *status.Status { return status.New(codes.Internal, e.Error()) } @@ -618,8 +618,8 @@ func (e *FailedDownloadError) Unwrap() error { return e.Cause } -// ToRPCStatus converts the error into a *status.Status -func (e *FailedDownloadError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *FailedDownloadError) GRPCStatus() *status.Status { return status.New(codes.Internal, e.Error()) } @@ -637,8 +637,8 @@ func (e *FailedUploadError) Unwrap() error { return e.Cause } -// ToRPCStatus converts the error into a *status.Status -func (e *FailedUploadError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *FailedUploadError) GRPCStatus() *status.Status { return status.New(codes.Internal, e.Error()) } @@ -656,8 +656,8 @@ func (e *FailedDebugError) Unwrap() error { return e.Cause } -// ToRPCStatus converts the error into a *status.Status -func (e *FailedDebugError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *FailedDebugError) GRPCStatus() *status.Status { return status.New(codes.Internal, e.Error()) } @@ -674,8 +674,8 @@ func (e *FailedMonitorError) Unwrap() error { return e.Cause } -// ToRPCStatus converts the error into a *status.Status -func (e *FailedMonitorError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *FailedMonitorError) GRPCStatus() *status.Status { return status.New(codes.Internal, e.Error()) } @@ -693,8 +693,8 @@ func (e *CompileFailedError) Unwrap() error { return e.Cause } -// ToRPCStatus converts the error into a *status.Status -func (e *CompileFailedError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *CompileFailedError) GRPCStatus() *status.Status { return status.New(codes.Internal, e.Error()) } @@ -712,8 +712,8 @@ func (e *InvalidArgumentError) Unwrap() error { return e.Cause } -// ToRPCStatus converts the error into a *status.Status -func (e *InvalidArgumentError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *InvalidArgumentError) GRPCStatus() *status.Status { return status.New(codes.InvalidArgument, e.Error()) } @@ -731,8 +731,8 @@ func (e *NotFoundError) Unwrap() error { return e.Cause } -// ToRPCStatus converts the error into a *status.Status -func (e *NotFoundError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *NotFoundError) GRPCStatus() *status.Status { return status.New(codes.NotFound, e.Error()) } @@ -750,8 +750,8 @@ func (e *PermissionDeniedError) Unwrap() error { return e.Cause } -// ToRPCStatus converts the error into a *status.Status -func (e *PermissionDeniedError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *PermissionDeniedError) GRPCStatus() *status.Status { return status.New(codes.PermissionDenied, e.Error()) } @@ -769,8 +769,8 @@ func (e *UnavailableError) Unwrap() error { return e.Cause } -// ToRPCStatus converts the error into a *status.Status -func (e *UnavailableError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *UnavailableError) GRPCStatus() *status.Status { return status.New(codes.Unavailable, e.Error()) } @@ -787,8 +787,8 @@ func (e *TempDirCreationFailedError) Unwrap() error { return e.Cause } -// ToRPCStatus converts the error into a *status.Status -func (e *TempDirCreationFailedError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *TempDirCreationFailedError) GRPCStatus() *status.Status { return status.New(codes.Unavailable, e.Error()) } @@ -805,8 +805,8 @@ func (e *TempFileCreationFailedError) Unwrap() error { return e.Cause } -// ToRPCStatus converts the error into a *status.Status -func (e *TempFileCreationFailedError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *TempFileCreationFailedError) GRPCStatus() *status.Status { return status.New(codes.Unavailable, e.Error()) } @@ -824,8 +824,8 @@ func (e *SignatureVerificationFailedError) Unwrap() error { return e.Cause } -// ToRPCStatus converts the error into a *status.Status -func (e *SignatureVerificationFailedError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *SignatureVerificationFailedError) GRPCStatus() *status.Status { return status.New(codes.Unavailable, e.Error()) } @@ -842,8 +842,8 @@ func (e *MultiplePlatformsError) Error() string { len(e.Platforms), e.UserPlatform, strings.Join(e.Platforms, ", ")) } -// ToRPCStatus converts the error into a *status.Status -func (e *MultiplePlatformsError) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *MultiplePlatformsError) GRPCStatus() *status.Status { return status.New(codes.InvalidArgument, e.Error()) } @@ -865,8 +865,8 @@ func (e *MultipleLibraryInstallDetected) Error() string { return res } -// ToRPCStatus converts the error into a *status.Status -func (e *MultipleLibraryInstallDetected) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *MultipleLibraryInstallDetected) GRPCStatus() *status.Status { return status.New(codes.InvalidArgument, e.Error()) } @@ -878,8 +878,8 @@ func (e *InstanceNeedsReinitialization) Error() string { return tr("The instance is no longer valid and needs to be reinitialized") } -// ToRPCStatus converts the error into a *status.Status -func (e *InstanceNeedsReinitialization) ToRPCStatus() *status.Status { +// GRPCStatus converts the error into a *status.Status +func (e *InstanceNeedsReinitialization) GRPCStatus() *status.Status { st, _ := status. New(codes.InvalidArgument, e.Error()). WithDetails(&rpc.InstanceNeedsReinitializationError{}) diff --git a/commands/daemon/daemon.go b/commands/daemon/daemon.go deleted file mode 100644 index 97a2783e0e6..00000000000 --- a/commands/daemon/daemon.go +++ /dev/null @@ -1,597 +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 daemon - -import ( - "context" - "errors" - "fmt" - "io" - "sync/atomic" - - "github.com/arduino/arduino-cli/commands" - "github.com/arduino/arduino-cli/commands/board" - "github.com/arduino/arduino-cli/commands/cache" - "github.com/arduino/arduino-cli/commands/cmderrors" - "github.com/arduino/arduino-cli/commands/compile" - "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/sketch" - "github.com/arduino/arduino-cli/commands/updatecheck" - "github.com/arduino/arduino-cli/commands/upload" - "github.com/arduino/arduino-cli/internal/i18n" - rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" - "github.com/sirupsen/logrus" - "google.golang.org/grpc/metadata" -) - -// ArduinoCoreServerImpl FIXMEDOC -type ArduinoCoreServerImpl struct { - // Force compile error for unimplemented methods - rpc.UnsafeArduinoCoreServiceServer - - VersionString string -} - -var tr = i18n.Tr - -func convertErrorToRPCStatus(err error) error { - if err == nil { - return nil - } - if cmdErr, ok := err.(cmderrors.CommandError); ok { - return cmdErr.ToRPCStatus().Err() - } - return err -} - -// BoardDetails FIXMEDOC -func (s *ArduinoCoreServerImpl) BoardDetails(ctx context.Context, req *rpc.BoardDetailsRequest) (*rpc.BoardDetailsResponse, error) { - resp, err := board.Details(ctx, req) - return resp, convertErrorToRPCStatus(err) -} - -// BoardList FIXMEDOC -func (s *ArduinoCoreServerImpl) BoardList(ctx context.Context, req *rpc.BoardListRequest) (*rpc.BoardListResponse, error) { - ports, _, err := board.List(req) - if err != nil { - return nil, convertErrorToRPCStatus(err) - } - return &rpc.BoardListResponse{ - Ports: ports, - }, nil -} - -// BoardListAll FIXMEDOC -func (s *ArduinoCoreServerImpl) BoardListAll(ctx context.Context, req *rpc.BoardListAllRequest) (*rpc.BoardListAllResponse, error) { - resp, err := board.ListAll(ctx, req) - return resp, convertErrorToRPCStatus(err) -} - -// BoardSearch exposes to the gRPC interface the board search command -func (s *ArduinoCoreServerImpl) BoardSearch(ctx context.Context, req *rpc.BoardSearchRequest) (*rpc.BoardSearchResponse, error) { - resp, err := board.Search(ctx, req) - return resp, convertErrorToRPCStatus(err) -} - -// BoardListWatch FIXMEDOC -func (s *ArduinoCoreServerImpl) BoardListWatch(req *rpc.BoardListWatchRequest, stream rpc.ArduinoCoreService_BoardListWatchServer) error { - syncSend := NewSynchronizedSend(stream.Send) - if req.GetInstance() == nil { - err := fmt.Errorf(tr("no instance specified")) - syncSend.Send(&rpc.BoardListWatchResponse{ - EventType: "error", - Error: err.Error(), - }) - return err - } - - eventsChan, err := board.Watch(stream.Context(), req) - if err != nil { - return convertErrorToRPCStatus(err) - } - - for event := range eventsChan { - if err := syncSend.Send(event); err != nil { - logrus.Infof("sending board watch message: %v", err) - } - } - - return nil -} - -// Destroy FIXMEDOC -func (s *ArduinoCoreServerImpl) Destroy(ctx context.Context, req *rpc.DestroyRequest) (*rpc.DestroyResponse, error) { - resp, err := commands.Destroy(ctx, req) - return resp, convertErrorToRPCStatus(err) -} - -// UpdateIndex FIXMEDOC -func (s *ArduinoCoreServerImpl) UpdateIndex(req *rpc.UpdateIndexRequest, stream rpc.ArduinoCoreService_UpdateIndexServer) error { - syncSend := NewSynchronizedSend(stream.Send) - res, err := commands.UpdateIndex(stream.Context(), req, - func(p *rpc.DownloadProgress) { - syncSend.Send(&rpc.UpdateIndexResponse{ - Message: &rpc.UpdateIndexResponse_DownloadProgress{DownloadProgress: p}, - }) - }, - ) - if res != nil { - syncSend.Send(&rpc.UpdateIndexResponse{ - Message: &rpc.UpdateIndexResponse_Result_{Result: res}, - }) - } - return convertErrorToRPCStatus(err) -} - -// UpdateLibrariesIndex FIXMEDOC -func (s *ArduinoCoreServerImpl) UpdateLibrariesIndex(req *rpc.UpdateLibrariesIndexRequest, stream rpc.ArduinoCoreService_UpdateLibrariesIndexServer) error { - syncSend := NewSynchronizedSend(stream.Send) - res, err := commands.UpdateLibrariesIndex(stream.Context(), req, - func(p *rpc.DownloadProgress) { - syncSend.Send(&rpc.UpdateLibrariesIndexResponse{ - Message: &rpc.UpdateLibrariesIndexResponse_DownloadProgress{DownloadProgress: p}, - }) - }, - ) - if res != nil { - syncSend.Send(&rpc.UpdateLibrariesIndexResponse{ - Message: &rpc.UpdateLibrariesIndexResponse_Result_{Result: res}, - }) - } - return convertErrorToRPCStatus(err) -} - -// Create FIXMEDOC -func (s *ArduinoCoreServerImpl) Create(ctx context.Context, req *rpc.CreateRequest) (*rpc.CreateResponse, error) { - var userAgent []string - if md, ok := metadata.FromIncomingContext(ctx); ok { - userAgent = md.Get("user-agent") - } - if len(userAgent) == 0 { - userAgent = []string{"gRPCClientUnknown/0.0.0"} - } - res, err := commands.Create(req, userAgent...) - return res, convertErrorToRPCStatus(err) -} - -// Init FIXMEDOC -func (s *ArduinoCoreServerImpl) Init(req *rpc.InitRequest, stream rpc.ArduinoCoreService_InitServer) error { - syncSend := NewSynchronizedSend(stream.Send) - err := commands.Init(req, func(message *rpc.InitResponse) { syncSend.Send(message) }) - return convertErrorToRPCStatus(err) -} - -// Version FIXMEDOC -func (s *ArduinoCoreServerImpl) Version(ctx context.Context, req *rpc.VersionRequest) (*rpc.VersionResponse, error) { - return &rpc.VersionResponse{Version: s.VersionString}, nil -} - -// NewSketch FIXMEDOC -func (s *ArduinoCoreServerImpl) NewSketch(ctx context.Context, req *rpc.NewSketchRequest) (*rpc.NewSketchResponse, error) { - resp, err := sketch.NewSketch(ctx, req) - return resp, convertErrorToRPCStatus(err) -} - -// LoadSketch FIXMEDOC -func (s *ArduinoCoreServerImpl) LoadSketch(ctx context.Context, req *rpc.LoadSketchRequest) (*rpc.LoadSketchResponse, error) { - resp, err := sketch.LoadSketch(ctx, req) - return &rpc.LoadSketchResponse{Sketch: resp}, convertErrorToRPCStatus(err) -} - -// SetSketchDefaults FIXMEDOC -func (s *ArduinoCoreServerImpl) SetSketchDefaults(ctx context.Context, req *rpc.SetSketchDefaultsRequest) (*rpc.SetSketchDefaultsResponse, error) { - resp, err := sketch.SetSketchDefaults(ctx, req) - return resp, convertErrorToRPCStatus(err) -} - -// Compile FIXMEDOC -func (s *ArduinoCoreServerImpl) Compile(req *rpc.CompileRequest, stream rpc.ArduinoCoreService_CompileServer) error { - syncSend := NewSynchronizedSend(stream.Send) - outStream := feedStreamTo(func(data []byte) { - syncSend.Send(&rpc.CompileResponse{ - Message: &rpc.CompileResponse_OutStream{OutStream: data}, - }) - }) - errStream := feedStreamTo(func(data []byte) { - syncSend.Send(&rpc.CompileResponse{ - Message: &rpc.CompileResponse_ErrStream{ErrStream: data}, - }) - }) - progressStream := func(p *rpc.TaskProgress) { - syncSend.Send(&rpc.CompileResponse{ - Message: &rpc.CompileResponse_Progress{Progress: p}, - }) - } - compileRes, compileErr := compile.Compile(stream.Context(), req, outStream, errStream, progressStream) - outStream.Close() - errStream.Close() - var compileRespSendErr error - if compileRes != nil { - compileRespSendErr = syncSend.Send(&rpc.CompileResponse{ - Message: &rpc.CompileResponse_Result{ - Result: compileRes, - }, - }) - } - if compileErr != nil { - return convertErrorToRPCStatus(compileErr) - } - return compileRespSendErr -} - -// PlatformInstall FIXMEDOC -func (s *ArduinoCoreServerImpl) PlatformInstall(req *rpc.PlatformInstallRequest, stream rpc.ArduinoCoreService_PlatformInstallServer) error { - syncSend := NewSynchronizedSend(stream.Send) - resp, err := core.PlatformInstall( - stream.Context(), req, - func(p *rpc.DownloadProgress) { syncSend.Send(&rpc.PlatformInstallResponse{Progress: p}) }, - func(p *rpc.TaskProgress) { syncSend.Send(&rpc.PlatformInstallResponse{TaskProgress: p}) }, - ) - if err != nil { - return convertErrorToRPCStatus(err) - } - return syncSend.Send(resp) -} - -// PlatformDownload FIXMEDOC -func (s *ArduinoCoreServerImpl) PlatformDownload(req *rpc.PlatformDownloadRequest, stream rpc.ArduinoCoreService_PlatformDownloadServer) error { - syncSend := NewSynchronizedSend(stream.Send) - resp, err := core.PlatformDownload( - stream.Context(), req, - func(p *rpc.DownloadProgress) { syncSend.Send(&rpc.PlatformDownloadResponse{Progress: p}) }, - ) - if err != nil { - return convertErrorToRPCStatus(err) - } - return syncSend.Send(resp) -} - -// PlatformUninstall FIXMEDOC -func (s *ArduinoCoreServerImpl) PlatformUninstall(req *rpc.PlatformUninstallRequest, stream rpc.ArduinoCoreService_PlatformUninstallServer) error { - syncSend := NewSynchronizedSend(stream.Send) - resp, err := core.PlatformUninstall( - stream.Context(), req, - func(p *rpc.TaskProgress) { syncSend.Send(&rpc.PlatformUninstallResponse{TaskProgress: p}) }, - ) - if err != nil { - return convertErrorToRPCStatus(err) - } - return syncSend.Send(resp) -} - -// PlatformUpgrade FIXMEDOC -func (s *ArduinoCoreServerImpl) PlatformUpgrade(req *rpc.PlatformUpgradeRequest, stream rpc.ArduinoCoreService_PlatformUpgradeServer) error { - syncSend := NewSynchronizedSend(stream.Send) - resp, err := core.PlatformUpgrade( - stream.Context(), req, - func(p *rpc.DownloadProgress) { syncSend.Send(&rpc.PlatformUpgradeResponse{Progress: p}) }, - func(p *rpc.TaskProgress) { syncSend.Send(&rpc.PlatformUpgradeResponse{TaskProgress: p}) }, - ) - if err2 := syncSend.Send(resp); err2 != nil { - return err2 - } - return convertErrorToRPCStatus(err) -} - -// PlatformSearch FIXMEDOC -func (s *ArduinoCoreServerImpl) PlatformSearch(ctx context.Context, req *rpc.PlatformSearchRequest) (*rpc.PlatformSearchResponse, error) { - resp, err := core.PlatformSearch(req) - return resp, convertErrorToRPCStatus(err) -} - -// Upload FIXMEDOC -func (s *ArduinoCoreServerImpl) Upload(req *rpc.UploadRequest, stream rpc.ArduinoCoreService_UploadServer) error { - syncSend := NewSynchronizedSend(stream.Send) - outStream := feedStreamTo(func(data []byte) { - syncSend.Send(&rpc.UploadResponse{ - Message: &rpc.UploadResponse_OutStream{OutStream: data}, - }) - }) - errStream := feedStreamTo(func(data []byte) { - syncSend.Send(&rpc.UploadResponse{ - Message: &rpc.UploadResponse_ErrStream{ErrStream: data}, - }) - }) - res, err := upload.Upload(stream.Context(), req, outStream, errStream) - outStream.Close() - errStream.Close() - if res != nil { - syncSend.Send(&rpc.UploadResponse{ - Message: &rpc.UploadResponse_Result{ - Result: res, - }, - }) - } - return convertErrorToRPCStatus(err) -} - -// UploadUsingProgrammer FIXMEDOC -func (s *ArduinoCoreServerImpl) UploadUsingProgrammer(req *rpc.UploadUsingProgrammerRequest, stream rpc.ArduinoCoreService_UploadUsingProgrammerServer) error { - syncSend := NewSynchronizedSend(stream.Send) - outStream := feedStreamTo(func(data []byte) { - syncSend.Send(&rpc.UploadUsingProgrammerResponse{ - Message: &rpc.UploadUsingProgrammerResponse_OutStream{ - OutStream: data, - }, - }) - }) - errStream := feedStreamTo(func(data []byte) { - syncSend.Send(&rpc.UploadUsingProgrammerResponse{ - Message: &rpc.UploadUsingProgrammerResponse_ErrStream{ - ErrStream: data, - }, - }) - }) - err := upload.UsingProgrammer(stream.Context(), req, outStream, errStream) - outStream.Close() - errStream.Close() - if err != nil { - return convertErrorToRPCStatus(err) - } - return nil -} - -// SupportedUserFields FIXMEDOC -func (s *ArduinoCoreServerImpl) SupportedUserFields(ctx context.Context, req *rpc.SupportedUserFieldsRequest) (*rpc.SupportedUserFieldsResponse, error) { - res, err := upload.SupportedUserFields(ctx, req) - return res, convertErrorToRPCStatus(err) -} - -// BurnBootloader FIXMEDOC -func (s *ArduinoCoreServerImpl) BurnBootloader(req *rpc.BurnBootloaderRequest, stream rpc.ArduinoCoreService_BurnBootloaderServer) error { - syncSend := NewSynchronizedSend(stream.Send) - outStream := feedStreamTo(func(data []byte) { - syncSend.Send(&rpc.BurnBootloaderResponse{ - Message: &rpc.BurnBootloaderResponse_OutStream{ - OutStream: data, - }, - }) - }) - errStream := feedStreamTo(func(data []byte) { - syncSend.Send(&rpc.BurnBootloaderResponse{ - Message: &rpc.BurnBootloaderResponse_ErrStream{ - ErrStream: data, - }, - }) - }) - resp, err := upload.BurnBootloader(stream.Context(), req, outStream, errStream) - outStream.Close() - errStream.Close() - if err != nil { - return convertErrorToRPCStatus(err) - } - return syncSend.Send(resp) -} - -// ListProgrammersAvailableForUpload FIXMEDOC -func (s *ArduinoCoreServerImpl) ListProgrammersAvailableForUpload(ctx context.Context, req *rpc.ListProgrammersAvailableForUploadRequest) (*rpc.ListProgrammersAvailableForUploadResponse, error) { - resp, err := upload.ListProgrammersAvailableForUpload(ctx, req) - return resp, convertErrorToRPCStatus(err) -} - -// LibraryDownload FIXMEDOC -func (s *ArduinoCoreServerImpl) LibraryDownload(req *rpc.LibraryDownloadRequest, stream rpc.ArduinoCoreService_LibraryDownloadServer) error { - syncSend := NewSynchronizedSend(stream.Send) - resp, err := lib.LibraryDownload( - stream.Context(), req, - func(p *rpc.DownloadProgress) { syncSend.Send(&rpc.LibraryDownloadResponse{Progress: p}) }, - ) - if err != nil { - return convertErrorToRPCStatus(err) - } - return syncSend.Send(resp) -} - -// LibraryInstall FIXMEDOC -func (s *ArduinoCoreServerImpl) LibraryInstall(req *rpc.LibraryInstallRequest, stream rpc.ArduinoCoreService_LibraryInstallServer) error { - syncSend := NewSynchronizedSend(stream.Send) - err := lib.LibraryInstall( - stream.Context(), req, - func(p *rpc.DownloadProgress) { syncSend.Send(&rpc.LibraryInstallResponse{Progress: p}) }, - func(p *rpc.TaskProgress) { syncSend.Send(&rpc.LibraryInstallResponse{TaskProgress: p}) }, - ) - return convertErrorToRPCStatus(err) -} - -// LibraryUpgrade FIXMEDOC -func (s *ArduinoCoreServerImpl) LibraryUpgrade(req *rpc.LibraryUpgradeRequest, stream rpc.ArduinoCoreService_LibraryUpgradeServer) error { - syncSend := NewSynchronizedSend(stream.Send) - err := lib.LibraryUpgrade( - stream.Context(), req, - func(p *rpc.DownloadProgress) { syncSend.Send(&rpc.LibraryUpgradeResponse{Progress: p}) }, - func(p *rpc.TaskProgress) { syncSend.Send(&rpc.LibraryUpgradeResponse{TaskProgress: p}) }, - ) - return convertErrorToRPCStatus(err) -} - -// LibraryUninstall FIXMEDOC -func (s *ArduinoCoreServerImpl) LibraryUninstall(req *rpc.LibraryUninstallRequest, stream rpc.ArduinoCoreService_LibraryUninstallServer) error { - syncSend := NewSynchronizedSend(stream.Send) - err := lib.LibraryUninstall(stream.Context(), req, - func(p *rpc.TaskProgress) { syncSend.Send(&rpc.LibraryUninstallResponse{TaskProgress: p}) }, - ) - return convertErrorToRPCStatus(err) -} - -// LibraryUpgradeAll FIXMEDOC -func (s *ArduinoCoreServerImpl) LibraryUpgradeAll(req *rpc.LibraryUpgradeAllRequest, stream rpc.ArduinoCoreService_LibraryUpgradeAllServer) error { - syncSend := NewSynchronizedSend(stream.Send) - err := lib.LibraryUpgradeAll(req, - func(p *rpc.DownloadProgress) { syncSend.Send(&rpc.LibraryUpgradeAllResponse{Progress: p}) }, - func(p *rpc.TaskProgress) { syncSend.Send(&rpc.LibraryUpgradeAllResponse{TaskProgress: p}) }, - ) - return convertErrorToRPCStatus(err) -} - -// LibraryResolveDependencies FIXMEDOC -func (s *ArduinoCoreServerImpl) LibraryResolveDependencies(ctx context.Context, req *rpc.LibraryResolveDependenciesRequest) (*rpc.LibraryResolveDependenciesResponse, error) { - resp, err := lib.LibraryResolveDependencies(ctx, req) - return resp, convertErrorToRPCStatus(err) -} - -// LibrarySearch FIXMEDOC -func (s *ArduinoCoreServerImpl) LibrarySearch(ctx context.Context, req *rpc.LibrarySearchRequest) (*rpc.LibrarySearchResponse, error) { - resp, err := lib.LibrarySearch(ctx, req) - return resp, convertErrorToRPCStatus(err) -} - -// LibraryList FIXMEDOC -func (s *ArduinoCoreServerImpl) LibraryList(ctx context.Context, req *rpc.LibraryListRequest) (*rpc.LibraryListResponse, error) { - resp, err := lib.LibraryList(ctx, req) - return resp, convertErrorToRPCStatus(err) -} - -// ArchiveSketch FIXMEDOC -func (s *ArduinoCoreServerImpl) ArchiveSketch(ctx context.Context, req *rpc.ArchiveSketchRequest) (*rpc.ArchiveSketchResponse, error) { - resp, err := sketch.ArchiveSketch(ctx, req) - return resp, convertErrorToRPCStatus(err) -} - -// ZipLibraryInstall FIXMEDOC -func (s *ArduinoCoreServerImpl) ZipLibraryInstall(req *rpc.ZipLibraryInstallRequest, stream rpc.ArduinoCoreService_ZipLibraryInstallServer) error { - syncSend := NewSynchronizedSend(stream.Send) - err := lib.ZipLibraryInstall( - stream.Context(), req, - func(p *rpc.TaskProgress) { syncSend.Send(&rpc.ZipLibraryInstallResponse{TaskProgress: p}) }, - ) - return convertErrorToRPCStatus(err) -} - -// GitLibraryInstall FIXMEDOC -func (s *ArduinoCoreServerImpl) GitLibraryInstall(req *rpc.GitLibraryInstallRequest, stream rpc.ArduinoCoreService_GitLibraryInstallServer) error { - syncSend := NewSynchronizedSend(stream.Send) - err := lib.GitLibraryInstall( - stream.Context(), req, - func(p *rpc.TaskProgress) { syncSend.Send(&rpc.GitLibraryInstallResponse{TaskProgress: p}) }, - ) - return convertErrorToRPCStatus(err) -} - -// EnumerateMonitorPortSettings FIXMEDOC -func (s *ArduinoCoreServerImpl) EnumerateMonitorPortSettings(ctx context.Context, req *rpc.EnumerateMonitorPortSettingsRequest) (*rpc.EnumerateMonitorPortSettingsResponse, error) { - resp, err := monitor.EnumerateMonitorPortSettings(ctx, req) - return resp, convertErrorToRPCStatus(err) -} - -// Monitor FIXMEDOC -func (s *ArduinoCoreServerImpl) Monitor(stream rpc.ArduinoCoreService_MonitorServer) error { - syncSend := NewSynchronizedSend(stream.Send) - - // The configuration must be sent on the first message - req, err := stream.Recv() - if err != nil { - return err - } - - openReq := req.GetOpenRequest() - if openReq == nil { - return &cmderrors.InvalidInstanceError{} - } - portProxy, _, err := monitor.Monitor(stream.Context(), openReq) - if err != nil { - return err - } - - // Send a message with Success set to true to notify the caller of the port being now active - _ = syncSend.Send(&rpc.MonitorResponse{Success: true}) - - cancelCtx, cancel := context.WithCancel(stream.Context()) - gracefulCloseInitiated := &atomic.Bool{} - gracefuleCloseCtx, gracefulCloseCancel := context.WithCancel(context.Background()) - - // gRPC stream receiver (gRPC data -> monitor, config, close) - go func() { - defer cancel() - for { - msg, err := stream.Recv() - if errors.Is(err, io.EOF) { - return - } - if err != nil { - syncSend.Send(&rpc.MonitorResponse{Error: err.Error()}) - return - } - if conf := msg.GetUpdatedConfiguration(); conf != nil { - for _, c := range conf.GetSettings() { - if err := portProxy.Config(c.GetSettingId(), c.GetValue()); err != nil { - syncSend.Send(&rpc.MonitorResponse{Error: err.Error()}) - } - } - } - if closeMsg := msg.GetClose(); closeMsg { - gracefulCloseInitiated.Store(true) - if err := portProxy.Close(); err != nil { - logrus.WithError(err).Debug("Error closing monitor port") - } - gracefulCloseCancel() - } - tx := msg.GetTxData() - for len(tx) > 0 { - n, err := portProxy.Write(tx) - if errors.Is(err, io.EOF) { - return - } - if err != nil { - syncSend.Send(&rpc.MonitorResponse{Error: err.Error()}) - return - } - tx = tx[n:] - } - } - }() - - // gRPC stream sender (monitor -> gRPC) - go func() { - defer cancel() // unlock the receiver - buff := make([]byte, 4096) - for { - n, err := portProxy.Read(buff) - if errors.Is(err, io.EOF) { - break - } - if err != nil { - syncSend.Send(&rpc.MonitorResponse{Error: err.Error()}) - break - } - if err := syncSend.Send(&rpc.MonitorResponse{RxData: buff[:n]}); err != nil { - break - } - } - }() - - <-cancelCtx.Done() - if gracefulCloseInitiated.Load() { - // Port closing has been initiated in the receiver - <-gracefuleCloseCtx.Done() - } else { - portProxy.Close() - } - return nil -} - -// CheckForArduinoCLIUpdates FIXMEDOC -func (s *ArduinoCoreServerImpl) CheckForArduinoCLIUpdates(ctx context.Context, req *rpc.CheckForArduinoCLIUpdatesRequest) (*rpc.CheckForArduinoCLIUpdatesResponse, error) { - resp, err := updatecheck.CheckForArduinoCLIUpdates(ctx, req) - return resp, convertErrorToRPCStatus(err) -} - -// CleanDownloadCacheDirectory FIXMEDOC -func (s *ArduinoCoreServerImpl) CleanDownloadCacheDirectory(ctx context.Context, req *rpc.CleanDownloadCacheDirectoryRequest) (*rpc.CleanDownloadCacheDirectoryResponse, error) { - resp, err := cache.CleanDownloadCacheDirectory(ctx, req) - return resp, convertErrorToRPCStatus(err) -} diff --git a/commands/daemon/settings.go b/commands/daemon/settings.go deleted file mode 100644 index 2d8b9ca9039..00000000000 --- a/commands/daemon/settings.go +++ /dev/null @@ -1,178 +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 daemon - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "strings" - - "github.com/arduino/arduino-cli/internal/cli/configuration" - rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" -) - -// SettingsGetAll returns a message with a string field containing all the settings -// currently in use, marshalled in JSON format. -func (s *ArduinoCoreServerImpl) SettingsGetAll(ctx context.Context, req *rpc.SettingsGetAllRequest) (*rpc.SettingsGetAllResponse, error) { - b, err := json.Marshal(configuration.Settings.AllSettings()) - if err == nil { - return &rpc.SettingsGetAllResponse{ - JsonData: string(b), - }, nil - } - - return nil, err -} - -// mapper converts a map of nested maps to a map of scalar values. -// For example: -// -// {"foo": "bar", "daemon":{"port":"420"}, "sketch": {"always_export_binaries": "true"}} -// -// would convert to: -// -// {"foo": "bar", "daemon.port":"420", "sketch.always_export_binaries": "true"} -func mapper(toMap map[string]interface{}) map[string]interface{} { - res := map[string]interface{}{} - for k, v := range toMap { - switch data := v.(type) { - case map[string]interface{}: - for mK, mV := range mapper(data) { - // Concatenate keys - res[fmt.Sprintf("%s.%s", k, mK)] = mV - } - // This is done to avoid skipping keys containing empty maps - if len(data) == 0 { - res[k] = map[string]interface{}{} - } - default: - res[k] = v - } - } - return res -} - -// SettingsMerge applies multiple settings values at once. -func (s *ArduinoCoreServerImpl) SettingsMerge(ctx context.Context, req *rpc.SettingsMergeRequest) (*rpc.SettingsMergeResponse, error) { - var toMerge map[string]interface{} - if err := json.Unmarshal([]byte(req.GetJsonData()), &toMerge); err != nil { - return nil, err - } - - mapped := mapper(toMerge) - - // Set each value individually. - // This is done because Viper ignores empty strings or maps when - // using the MergeConfigMap function. - updatedSettings := configuration.Init("") - for k, v := range mapped { - updatedSettings.Set(k, v) - } - configPath := configuration.Settings.ConfigFileUsed() - updatedSettings.SetConfigFile(configPath) - configuration.Settings = updatedSettings - - return &rpc.SettingsMergeResponse{}, nil -} - -// SettingsGetValue returns a settings value given its key. If the key is not present -// an error will be returned, so that we distinguish empty settings from missing -// ones. -func (s *ArduinoCoreServerImpl) SettingsGetValue(ctx context.Context, req *rpc.SettingsGetValueRequest) (*rpc.SettingsGetValueResponse, error) { - key := req.GetKey() - - // Check if settings key actually existing, we don't use Viper.InConfig() - // since that doesn't check for keys formatted like daemon.port or those set - // with Viper.Set(). This way we check for all existing settings for sure. - keyExists := false - for _, k := range configuration.Settings.AllKeys() { - if k == key || strings.HasPrefix(k, key) { - keyExists = true - break - } - } - if !keyExists { - return nil, errors.New(tr("key not found in settings")) - } - - b, err := json.Marshal(configuration.Settings.Get(key)) - value := &rpc.SettingsGetValueResponse{} - if err == nil { - value.Key = key - value.JsonData = string(b) - } - - return value, err -} - -// SettingsSetValue updates or set a value for a certain key. -func (s *ArduinoCoreServerImpl) SettingsSetValue(ctx context.Context, val *rpc.SettingsSetValueRequest) (*rpc.SettingsSetValueResponse, error) { - key := val.GetKey() - var value interface{} - - err := json.Unmarshal([]byte(val.GetJsonData()), &value) - if err == nil { - configuration.Settings.Set(key, value) - } - - return &rpc.SettingsSetValueResponse{}, err -} - -// SettingsWrite to file set in request the settings currently stored in memory. -// We don't have a Read() function, that's not necessary since we only want one config file to be used -// and that's picked up when the CLI is run as daemon, either using the default path or a custom one -// set with the --config-file flag. -func (s *ArduinoCoreServerImpl) SettingsWrite(ctx context.Context, req *rpc.SettingsWriteRequest) (*rpc.SettingsWriteResponse, error) { - if err := configuration.Settings.WriteConfigAs(req.GetFilePath()); err != nil { - return nil, err - } - return &rpc.SettingsWriteResponse{}, nil -} - -// SettingsDelete removes a key from the config file -func (s *ArduinoCoreServerImpl) SettingsDelete(ctx context.Context, req *rpc.SettingsDeleteRequest) (*rpc.SettingsDeleteResponse, error) { - toDelete := req.GetKey() - - // Check if settings key actually existing, we don't use Viper.InConfig() - // since that doesn't check for keys formatted like daemon.port or those set - // with Viper.Set(). This way we check for all existing settings for sure. - keyExists := false - keys := []string{} - for _, k := range configuration.Settings.AllKeys() { - if !strings.HasPrefix(k, toDelete) { - keys = append(keys, k) - continue - } - keyExists = true - } - - if !keyExists { - return nil, errors.New(tr("key not found in settings")) - } - - // Override current settings to delete the key - updatedSettings := configuration.Init("") - for _, k := range keys { - updatedSettings.Set(k, configuration.Settings.Get(k)) - } - configPath := configuration.Settings.ConfigFileUsed() - updatedSettings.SetConfigFile(configPath) - configuration.Settings = updatedSettings - - return &rpc.SettingsDeleteResponse{}, nil -} diff --git a/commands/daemon/settings_test.go b/commands/daemon/settings_test.go deleted file mode 100644 index 93f49174e50..00000000000 --- a/commands/daemon/settings_test.go +++ /dev/null @@ -1,185 +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 daemon - -import ( - "context" - "encoding/json" - "path/filepath" - "testing" - - "github.com/arduino/arduino-cli/internal/cli/configuration" - rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" - "github.com/arduino/go-paths-helper" - "github.com/stretchr/testify/require" -) - -var svc = ArduinoCoreServerImpl{} - -func init() { - configuration.Settings = configuration.Init(filepath.Join("testdata", "arduino-cli.yaml")) -} - -func reset() { - configuration.Settings = configuration.Init(filepath.Join("testdata", "arduino-cli.yaml")) -} - -func TestGetAll(t *testing.T) { - resp, err := svc.SettingsGetAll(context.Background(), &rpc.SettingsGetAllRequest{}) - require.Nil(t, err) - - content, err := json.Marshal(configuration.Settings.AllSettings()) - require.Nil(t, err) - - require.Equal(t, string(content), resp.GetJsonData()) -} - -func TestMerge(t *testing.T) { - // Verify defaults - require.Equal(t, "50051", configuration.Settings.GetString("daemon.port")) - require.Equal(t, "", configuration.Settings.GetString("foo")) - require.Equal(t, false, configuration.Settings.GetBool("sketch.always_export_binaries")) - - bulkSettings := `{"foo": "bar", "daemon":{"port":"420"}, "sketch": {"always_export_binaries": "true"}}` - res, err := svc.SettingsMerge(context.Background(), &rpc.SettingsMergeRequest{JsonData: bulkSettings}) - require.NotNil(t, res) - require.NoError(t, err) - - require.Equal(t, "420", configuration.Settings.GetString("daemon.port")) - require.Equal(t, "bar", configuration.Settings.GetString("foo")) - require.Equal(t, true, configuration.Settings.GetBool("sketch.always_export_binaries")) - - bulkSettings = `{"foo":"", "daemon": {}, "sketch": {"always_export_binaries": "false"}}` - res, err = svc.SettingsMerge(context.Background(), &rpc.SettingsMergeRequest{JsonData: bulkSettings}) - require.NotNil(t, res) - require.NoError(t, err) - - require.Equal(t, "50051", configuration.Settings.GetString("daemon.port")) - require.Equal(t, "", configuration.Settings.GetString("foo")) - require.Equal(t, false, configuration.Settings.GetBool("sketch.always_export_binaries")) - - bulkSettings = `{"daemon": {"port":""}}` - res, err = svc.SettingsMerge(context.Background(), &rpc.SettingsMergeRequest{JsonData: bulkSettings}) - require.NotNil(t, res) - require.NoError(t, err) - - require.Equal(t, "", configuration.Settings.GetString("daemon.port")) - // Verifies other values are not changed - require.Equal(t, "", configuration.Settings.GetString("foo")) - require.Equal(t, false, configuration.Settings.GetBool("sketch.always_export_binaries")) - - bulkSettings = `{"network": {}}` - res, err = svc.SettingsMerge(context.Background(), &rpc.SettingsMergeRequest{JsonData: bulkSettings}) - require.NotNil(t, res) - require.NoError(t, err) - - require.Equal(t, "", configuration.Settings.GetString("proxy")) - - reset() -} - -func TestGetValue(t *testing.T) { - key := &rpc.SettingsGetValueRequest{Key: "daemon"} - resp, err := svc.SettingsGetValue(context.Background(), key) - require.NoError(t, err) - require.Equal(t, `{"port":"50051"}`, resp.GetJsonData()) - - key = &rpc.SettingsGetValueRequest{Key: "daemon.port"} - resp, err = svc.SettingsGetValue(context.Background(), key) - require.NoError(t, err) - require.Equal(t, `"50051"`, resp.GetJsonData()) -} - -func TestGetMergedValue(t *testing.T) { - // Verifies value is not set - key := &rpc.SettingsGetValueRequest{Key: "foo"} - res, err := svc.SettingsGetValue(context.Background(), key) - require.Nil(t, res) - require.Error(t, err, "Error getting settings value") - - // Merge value - bulkSettings := `{"foo": "bar"}` - _, err = svc.SettingsMerge(context.Background(), &rpc.SettingsMergeRequest{JsonData: bulkSettings}) - require.NoError(t, err) - - // Verifies value is correctly returned - key = &rpc.SettingsGetValueRequest{Key: "foo"} - res, err = svc.SettingsGetValue(context.Background(), key) - require.NoError(t, err) - require.Equal(t, `"bar"`, res.GetJsonData()) - - reset() -} - -func TestGetValueNotFound(t *testing.T) { - key := &rpc.SettingsGetValueRequest{Key: "DOESNTEXIST"} - _, err := svc.SettingsGetValue(context.Background(), key) - require.NotNil(t, err) - require.Equal(t, `key not found in settings`, err.Error()) -} - -func TestSetValue(t *testing.T) { - val := &rpc.SettingsSetValueRequest{ - Key: "foo", - JsonData: `"bar"`, - } - _, err := svc.SettingsSetValue(context.Background(), val) - require.Nil(t, err) - require.Equal(t, "bar", configuration.Settings.GetString("foo")) -} - -func TestWrite(t *testing.T) { - // Writes some settings - val := &rpc.SettingsSetValueRequest{ - Key: "foo", - JsonData: `"bar"`, - } - _, err := svc.SettingsSetValue(context.Background(), val) - require.NoError(t, err) - - tempDir := paths.TempDir() - testFolder, err := tempDir.MkTempDir("testdata") - require.NoError(t, err) - defer testFolder.RemoveAll() - - // Verifies config files doesn't exist - configFile := testFolder.Join("arduino-cli.yml") - require.True(t, configFile.NotExist()) - - _, err = svc.SettingsWrite(context.Background(), &rpc.SettingsWriteRequest{ - FilePath: configFile.String(), - }) - require.NoError(t, err) - - // Verifies config file is created. - // We don't verify the content since we expect config library, Viper, to work - require.True(t, configFile.Exist()) -} - -func TestDelete(t *testing.T) { - _, err := svc.SettingsDelete(context.Background(), &rpc.SettingsDeleteRequest{ - Key: "doesnotexist", - }) - require.Error(t, err) - - _, err = svc.SettingsDelete(context.Background(), &rpc.SettingsDeleteRequest{ - Key: "network", - }) - require.NoError(t, err) - - _, err = svc.SettingsGetValue(context.Background(), &rpc.SettingsGetValueRequest{Key: "network"}) - require.Error(t, err) -} diff --git a/commands/instances.go b/commands/instances.go index df1d96f4c41..30fe31ba783 100644 --- a/commands/instances.go +++ b/commands/instances.go @@ -36,22 +36,21 @@ import ( "github.com/arduino/arduino-cli/internal/arduino/resources" "github.com/arduino/arduino-cli/internal/arduino/sketch" "github.com/arduino/arduino-cli/internal/arduino/utils" - "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/arduino/arduino-cli/internal/i18n" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" paths "github.com/arduino/go-paths-helper" "github.com/sirupsen/logrus" "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" ) -var tr = i18n.Tr - func installTool(pm *packagemanager.PackageManager, tool *cores.ToolRelease, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) error { pme, release := pm.NewExplorer() defer release() + taskCB(&rpc.TaskProgress{Name: tr("Downloading missing tool %s", tool)}) - if err := pme.DownloadToolRelease(tool, nil, downloadCB); err != nil { + if err := pme.DownloadToolRelease(tool, downloadCB); err != nil { return fmt.Errorf(tr("downloading %[1]s tool: %[2]s"), tool, err) } taskCB(&rpc.TaskProgress{Completed: true}) @@ -61,10 +60,18 @@ func installTool(pm *packagemanager.PackageManager, tool *cores.ToolRelease, dow return nil } -// Create a new CoreInstance ready to be initialized, supporting directories are also created. -func Create(req *rpc.CreateRequest, extraUserAgent ...string) (*rpc.CreateResponse, error) { +// Create a new Instance ready to be initialized, supporting directories are also created. +func (s *arduinoCoreServerImpl) Create(ctx context.Context, req *rpc.CreateRequest) (*rpc.CreateResponse, error) { + var userAgent string + if md, ok := metadata.FromIncomingContext(ctx); ok { + userAgent = strings.Join(md.Get("user-agent"), " ") + } + if userAgent == "" { + userAgent = "gRPCClientUnknown/0.0.0" + } + // Setup downloads directory - downloadsDir := configuration.DownloadsDir(configuration.Settings) + downloadsDir := s.settings.DownloadsDir() if downloadsDir.NotExist() { err := downloadsDir.MkdirAll() if err != nil { @@ -73,8 +80,9 @@ func Create(req *rpc.CreateRequest, extraUserAgent ...string) (*rpc.CreateRespon } // Setup data directory - dataDir := configuration.DataDir(configuration.Settings) - packagesDir := configuration.PackagesDir(configuration.Settings) + dataDir := s.settings.DataDir() + userPackagesDir := s.settings.UserDir().Join("hardware") + packagesDir := s.settings.PackagesDir() if packagesDir.NotExist() { err := packagesDir.MkdirAll() if err != nil { @@ -82,30 +90,43 @@ func Create(req *rpc.CreateRequest, extraUserAgent ...string) (*rpc.CreateRespon } } - inst, err := instances.Create(dataDir, packagesDir, downloadsDir, extraUserAgent...) + config, err := s.settings.DownloaderConfig() + if err != nil { + return nil, err + } + inst, err := instances.Create(dataDir, packagesDir, userPackagesDir, downloadsDir, userAgent, config) if err != nil { return nil, err } return &rpc.CreateResponse{Instance: inst}, nil } +// InitStreamResponseToCallbackFunction returns a gRPC stream to be used in Init that sends +// all responses to the callback function. +func InitStreamResponseToCallbackFunction(ctx context.Context, cb func(r *rpc.InitResponse) error) rpc.ArduinoCoreService_InitServer { + return streamResponseToCallback(ctx, cb) +} + // Init loads installed libraries and Platforms in CoreInstance with specified ID, // a gRPC status error is returned if the CoreInstance doesn't exist. // All responses are sent through responseCallback, can be nil to ignore all responses. // Failures don't stop the loading process, in case of loading failure the Platform or library // is simply skipped and an error gRPC status is sent to responseCallback. -func Init(req *rpc.InitRequest, responseCallback func(r *rpc.InitResponse)) error { - if responseCallback == nil { - responseCallback = func(r *rpc.InitResponse) {} - } +func (s *arduinoCoreServerImpl) Init(req *rpc.InitRequest, stream rpc.ArduinoCoreService_InitServer) error { + ctx := stream.Context() + instance := req.GetInstance() if !instances.IsValid(instance) { return &cmderrors.InvalidInstanceError{} } // Setup callback functions - if responseCallback == nil { - responseCallback = func(r *rpc.InitResponse) {} + var responseCallback func(*rpc.InitResponse) error + if stream != nil { + syncSend := NewSynchronizedSend(stream.Send) + responseCallback = syncSend.Send + } else { + responseCallback = func(*rpc.InitResponse) error { return nil } } responseError := func(st *status.Status) { responseCallback(&rpc.InitResponse{ @@ -156,7 +177,7 @@ func Init(req *rpc.InitRequest, responseCallback func(r *rpc.InitResponse)) erro defaultIndexURL, _ := utils.URLParse(globals.DefaultIndexURL) allPackageIndexUrls := []*url.URL{defaultIndexURL} if profile == nil { - for _, u := range configuration.Settings.GetStringSlice("board_manager.additional_urls") { + for _, u := range s.settings.BoardManagerAdditionalUrls() { URL, err := utils.URLParse(u) if err != nil { e := &cmderrors.InitFailedError{ @@ -164,19 +185,20 @@ func Init(req *rpc.InitRequest, responseCallback func(r *rpc.InitResponse)) erro Cause: fmt.Errorf(tr("Invalid additional URL: %v", err)), Reason: rpc.FailedInstanceInitReason_FAILED_INSTANCE_INIT_REASON_INVALID_INDEX_URL, } - responseError(e.ToRPCStatus()) + responseError(e.GRPCStatus()) continue } allPackageIndexUrls = append(allPackageIndexUrls, URL) } } - if err := firstUpdate(context.Background(), req.GetInstance(), downloadCallback, allPackageIndexUrls); err != nil { + + if err := firstUpdate(ctx, s, req.GetInstance(), s.settings.DataDir(), downloadCallback, allPackageIndexUrls); err != nil { e := &cmderrors.InitFailedError{ Code: codes.InvalidArgument, Cause: err, Reason: rpc.FailedInstanceInitReason_FAILED_INSTANCE_INIT_REASON_INDEX_DOWNLOAD_ERROR, } - responseError(e.ToRPCStatus()) + responseError(e.GRPCStatus()) } { @@ -201,7 +223,7 @@ func Init(req *rpc.InitRequest, responseCallback func(r *rpc.InitResponse)) erro Cause: fmt.Errorf(tr("Loading index file: %v", err)), Reason: rpc.FailedInstanceInitReason_FAILED_INSTANCE_INIT_REASON_INDEX_LOAD_ERROR, } - responseError(e.ToRPCStatus()) + responseError(e.GRPCStatus()) } continue } @@ -212,7 +234,7 @@ func Init(req *rpc.InitRequest, responseCallback func(r *rpc.InitResponse)) erro Cause: fmt.Errorf(tr("Loading index file: %v", err)), Reason: rpc.FailedInstanceInitReason_FAILED_INSTANCE_INIT_REASON_INDEX_LOAD_ERROR, } - responseError(e.ToRPCStatus()) + responseError(e.GRPCStatus()) } } @@ -225,16 +247,14 @@ func Init(req *rpc.InitRequest, responseCallback func(r *rpc.InitResponse)) erro if profile == nil { for _, err := range pmb.LoadHardware() { s := &cmderrors.PlatformLoadingError{Cause: err} - responseError(s.ToRPCStatus()) + responseError(s.GRPCStatus()) } } else { // Load platforms from profile - errs := pmb.LoadHardwareForProfile( - profile, true, downloadCallback, taskCallback, - ) + errs := pmb.LoadHardwareForProfile(ctx, profile, true, downloadCallback, taskCallback, s.settings) for _, err := range errs { s := &cmderrors.PlatformLoadingError{Cause: err} - responseError(s.ToRPCStatus()) + responseError(s.GRPCStatus()) } // Load "builtin" tools @@ -254,7 +274,7 @@ func Init(req *rpc.InitRequest, responseCallback func(r *rpc.InitResponse)) erro Cause: fmt.Errorf(tr("can't find latest release of tool %s", name)), Reason: rpc.FailedInstanceInitReason_FAILED_INSTANCE_INIT_REASON_TOOL_LOAD_ERROR, } - responseError(e.ToRPCStatus()) + responseError(e.GRPCStatus()) } else if !latest.IsInstalled() { builtinToolsToInstall = append(builtinToolsToInstall, latest) } @@ -269,7 +289,7 @@ func Init(req *rpc.InitRequest, responseCallback func(r *rpc.InitResponse)) erro Cause: err, Reason: rpc.FailedInstanceInitReason_FAILED_INSTANCE_INIT_REASON_TOOL_LOAD_ERROR, } - responseError(e.ToRPCStatus()) + responseError(e.GRPCStatus()) } } @@ -277,7 +297,7 @@ func Init(req *rpc.InitRequest, responseCallback func(r *rpc.InitResponse)) erro // so we must reload again otherwise we would never found them. for _, err := range loadBuiltinTools() { s := &cmderrors.PlatformLoadingError{Cause: err} - responseError(s.ToRPCStatus()) + responseError(s.GRPCStatus()) } } @@ -292,7 +312,7 @@ func Init(req *rpc.InitRequest, responseCallback func(r *rpc.InitResponse)) erro for _, err := range pme.LoadDiscoveries() { s := &cmderrors.PlatformLoadingError{Cause: err} - responseError(s.ToRPCStatus()) + responseError(s.GRPCStatus()) } // Create library manager and add libraries directories @@ -329,7 +349,7 @@ func Init(req *rpc.InitRequest, responseCallback func(r *rpc.InitResponse)) erro if profile == nil { // Add directories of libraries bundled with IDE - if bundledLibsDir := configuration.IDEBuiltinLibrariesDir(configuration.Settings); bundledLibsDir != nil { + if bundledLibsDir := s.settings.IDEBuiltinLibrariesDir(); bundledLibsDir != nil { lmb.AddLibrariesDir(librariesmanager.LibrariesDir{ Path: bundledLibsDir, Location: libraries.IDEBuiltIn, @@ -338,14 +358,14 @@ func Init(req *rpc.InitRequest, responseCallback func(r *rpc.InitResponse)) erro // Add libraries directory from config file lmb.AddLibrariesDir(librariesmanager.LibrariesDir{ - Path: configuration.LibrariesDir(configuration.Settings), + Path: s.settings.LibrariesDir(), Location: libraries.User, }) } else { // Load libraries required for profile for _, libraryRef := range profile.Libraries { uid := libraryRef.InternalUniqueIdentifier() - libRoot := configuration.ProfilesCacheDir(configuration.Settings).Join(uid) + libRoot := s.settings.ProfilesCacheDir().Join(uid) libDir := libRoot.Join(libraryRef.Library) if !libDir.IsDir() { @@ -355,13 +375,20 @@ func Init(req *rpc.InitRequest, responseCallback func(r *rpc.InitResponse)) erro if err != nil { taskCallback(&rpc.TaskProgress{Name: tr("Library %s not found", libraryRef)}) err := &cmderrors.LibraryNotFoundError{Library: libraryRef.Library} - responseError(err.ToRPCStatus()) + responseError(err.GRPCStatus()) continue } - if err := libRelease.Resource.Download(pme.DownloadDir, nil, libRelease.String(), downloadCallback, ""); err != nil { + config, err := s.settings.DownloaderConfig() + if err != nil { taskCallback(&rpc.TaskProgress{Name: tr("Error downloading library %s", libraryRef)}) e := &cmderrors.FailedLibraryInstallError{Cause: err} - responseError(e.ToRPCStatus()) + responseError(e.GRPCStatus()) + continue + } + if err := libRelease.Resource.Download(pme.DownloadDir, config, libRelease.String(), downloadCallback, ""); err != nil { + taskCallback(&rpc.TaskProgress{Name: tr("Error downloading library %s", libraryRef)}) + e := &cmderrors.FailedLibraryInstallError{Cause: err} + responseError(e.GRPCStatus()) continue } taskCallback(&rpc.TaskProgress{Completed: true}) @@ -371,7 +398,7 @@ func Init(req *rpc.InitRequest, responseCallback func(r *rpc.InitResponse)) erro if err := libRelease.Resource.Install(pme.DownloadDir, libRoot, libDir); err != nil { taskCallback(&rpc.TaskProgress{Name: tr("Error installing library %s", libraryRef)}) e := &cmderrors.FailedLibraryInstallError{Cause: err} - responseError(e.ToRPCStatus()) + responseError(e.GRPCStatus()) continue } taskCallback(&rpc.TaskProgress{Completed: true}) @@ -394,43 +421,69 @@ func Init(req *rpc.InitRequest, responseCallback func(r *rpc.InitResponse)) erro // Refreshes the locale used, this will change the // language of the CLI if the locale is different // after started. - i18n.Init(configuration.Settings.GetString("locale")) + i18n.Init(s.settings.GetString("locale")) return nil } -// Destroy FIXMEDOC -func Destroy(ctx context.Context, req *rpc.DestroyRequest) (*rpc.DestroyResponse, error) { +// Destroy deletes an instance. +func (s *arduinoCoreServerImpl) Destroy(ctx context.Context, req *rpc.DestroyRequest) (*rpc.DestroyResponse, error) { if ok := instances.Delete(req.GetInstance()); !ok { return nil, &cmderrors.InvalidInstanceError{} } return &rpc.DestroyResponse{}, nil } +// UpdateLibrariesIndexStreamResponseToCallbackFunction returns a gRPC stream to be used in UpdateLibrariesIndex that sends +// all responses to the callback function. +func UpdateLibrariesIndexStreamResponseToCallbackFunction(ctx context.Context, downloadCB rpc.DownloadProgressCB) (rpc.ArduinoCoreService_UpdateLibrariesIndexServer, func() *rpc.UpdateLibrariesIndexResponse_Result) { + var result *rpc.UpdateLibrariesIndexResponse_Result + return streamResponseToCallback(ctx, func(r *rpc.UpdateLibrariesIndexResponse) error { + if r.GetDownloadProgress() != nil { + downloadCB(r.GetDownloadProgress()) + } + if r.GetResult() != nil { + result = r.GetResult() + } + return nil + }), func() *rpc.UpdateLibrariesIndexResponse_Result { + return result + } +} + // UpdateLibrariesIndex updates the library_index.json -func UpdateLibrariesIndex(ctx context.Context, req *rpc.UpdateLibrariesIndexRequest, downloadCB rpc.DownloadProgressCB) (*rpc.UpdateLibrariesIndexResponse_Result, error) { - logrus.Info("Updating libraries index") +func (s *arduinoCoreServerImpl) UpdateLibrariesIndex(req *rpc.UpdateLibrariesIndexRequest, stream rpc.ArduinoCoreService_UpdateLibrariesIndexServer) error { + syncSend := NewSynchronizedSend(stream.Send) + downloadCB := func(p *rpc.DownloadProgress) { + syncSend.Send(&rpc.UpdateLibrariesIndexResponse{ + Message: &rpc.UpdateLibrariesIndexResponse_DownloadProgress{DownloadProgress: p}}) + } pme, release, err := instances.GetPackageManagerExplorer(req.GetInstance()) if err != nil { - return nil, err + return err } indexDir := pme.IndexDir release() - index := globals.LibrariesIndexResource - result := func(status rpc.IndexUpdateReport_Status) *rpc.UpdateLibrariesIndexResponse_Result { - return &rpc.UpdateLibrariesIndexResponse_Result{ - LibrariesIndex: &rpc.IndexUpdateReport{ - IndexUrl: globals.LibrariesIndexResource.URL.String(), - Status: status, + + resultCB := func(status rpc.IndexUpdateReport_Status) { + syncSend.Send(&rpc.UpdateLibrariesIndexResponse{ + Message: &rpc.UpdateLibrariesIndexResponse_Result_{ + Result: &rpc.UpdateLibrariesIndexResponse_Result{ + LibrariesIndex: &rpc.IndexUpdateReport{ + IndexUrl: index.URL.String(), + Status: status, + }, + }, }, - } + }) } // Create the index directory if it doesn't exist if err := indexDir.MkdirAll(); err != nil { - return result(rpc.IndexUpdateReport_STATUS_FAILED), &cmderrors.PermissionDeniedError{Message: tr("Could not create index directory"), Cause: err} + resultCB(rpc.IndexUpdateReport_STATUS_FAILED) + return &cmderrors.PermissionDeniedError{Message: tr("Could not create index directory"), Cause: err} } // Check if the index file is already up-to-date @@ -438,22 +491,46 @@ func UpdateLibrariesIndex(ctx context.Context, req *rpc.UpdateLibrariesIndexRequ if info, err := indexDir.Join(indexFileName).Stat(); err == nil { ageSecs := int64(time.Since(info.ModTime()).Seconds()) if ageSecs < req.GetUpdateIfOlderThanSecs() { - return result(rpc.IndexUpdateReport_STATUS_ALREADY_UP_TO_DATE), nil + resultCB(rpc.IndexUpdateReport_STATUS_ALREADY_UP_TO_DATE) + return nil } } // Perform index update - if err := globals.LibrariesIndexResource.Download(indexDir, downloadCB); err != nil { - return nil, err + config, err := s.settings.DownloaderConfig() + if err != nil { + return err + } + if err := globals.LibrariesIndexResource.Download(stream.Context(), indexDir, downloadCB, config); err != nil { + resultCB(rpc.IndexUpdateReport_STATUS_FAILED) + return err } - return result(rpc.IndexUpdateReport_STATUS_UPDATED), nil + resultCB(rpc.IndexUpdateReport_STATUS_UPDATED) + return nil +} + +// UpdateIndexStreamResponseToCallbackFunction returns a gRPC stream to be used in UpdateIndex that sends +// all responses to the callback function. +func UpdateIndexStreamResponseToCallbackFunction(ctx context.Context, downloadCB rpc.DownloadProgressCB) (rpc.ArduinoCoreService_UpdateIndexServer, func() *rpc.UpdateIndexResponse_Result) { + var result *rpc.UpdateIndexResponse_Result + return streamResponseToCallback(ctx, func(r *rpc.UpdateIndexResponse) error { + if r.GetDownloadProgress() != nil { + downloadCB(r.GetDownloadProgress()) + } + if r.GetResult() != nil { + result = r.GetResult() + } + return nil + }), func() *rpc.UpdateIndexResponse_Result { + return result + } } // UpdateIndex FIXMEDOC -func UpdateIndex(ctx context.Context, req *rpc.UpdateIndexRequest, downloadCB rpc.DownloadProgressCB) (*rpc.UpdateIndexResponse_Result, error) { +func (s *arduinoCoreServerImpl) UpdateIndex(req *rpc.UpdateIndexRequest, stream rpc.ArduinoCoreService_UpdateIndexServer) error { if !instances.IsValid(req.GetInstance()) { - return nil, &cmderrors.InvalidInstanceError{} + return &cmderrors.InvalidInstanceError{} } report := func(indexURL *url.URL, status rpc.IndexUpdateReport_Status) *rpc.IndexUpdateReport { @@ -463,11 +540,17 @@ func UpdateIndex(ctx context.Context, req *rpc.UpdateIndexRequest, downloadCB rp } } - indexpath := configuration.DataDir(configuration.Settings) + syncSend := NewSynchronizedSend(stream.Send) + var downloadCB rpc.DownloadProgressCB = func(p *rpc.DownloadProgress) { + syncSend.Send(&rpc.UpdateIndexResponse{ + Message: &rpc.UpdateIndexResponse_DownloadProgress{DownloadProgress: p}, + }) + } + indexpath := s.settings.DataDir() urls := []string{globals.DefaultIndexURL} if !req.GetIgnoreCustomPackageIndexes() { - urls = append(urls, configuration.Settings.GetStringSlice("board_manager.additional_urls")...) + urls = append(urls, s.settings.GetStringSlice("board_manager.additional_urls")...) } failed := false @@ -524,36 +607,45 @@ func UpdateIndex(ctx context.Context, req *rpc.UpdateIndexRequest, downloadCB rp } } + config, err := s.settings.DownloaderConfig() + if err != nil { + downloadCB.Start(u, tr("Downloading index: %s", filepath.Base(URL.Path))) + downloadCB.End(false, tr("Invalid network configuration: %s", err)) + failed = true + continue + } + if strings.HasSuffix(URL.Host, "arduino.cc") && strings.HasSuffix(URL.Path, ".json") { indexResource.SignatureURL, _ = url.Parse(u) // should not fail because we already parsed it indexResource.SignatureURL.Path += ".sig" } - if err := indexResource.Download(indexpath, downloadCB); err != nil { + if err := indexResource.Download(stream.Context(), indexpath, downloadCB, config); err != nil { failed = true result.UpdatedIndexes = append(result.GetUpdatedIndexes(), report(URL, rpc.IndexUpdateReport_STATUS_FAILED)) } else { result.UpdatedIndexes = append(result.GetUpdatedIndexes(), report(URL, rpc.IndexUpdateReport_STATUS_UPDATED)) } } - + syncSend.Send(&rpc.UpdateIndexResponse{ + Message: &rpc.UpdateIndexResponse_Result_{Result: result}, + }) if failed { - return result, &cmderrors.FailedDownloadError{Message: tr("Some indexes could not be updated.")} + return &cmderrors.FailedDownloadError{Message: tr("Some indexes could not be updated.")} } - return result, nil + return nil } // firstUpdate downloads libraries and packages indexes if they don't exist. // This ideally is only executed the first time the CLI is run. -func firstUpdate(ctx context.Context, instance *rpc.Instance, downloadCb func(msg *rpc.DownloadProgress), externalPackageIndexes []*url.URL) error { - // Gets the data directory to verify if library_index.json and package_index.json exist - dataDir := configuration.DataDir(configuration.Settings) - libraryIndex := dataDir.Join("library_index.json") +func firstUpdate(ctx context.Context, srv rpc.ArduinoCoreServiceServer, instance *rpc.Instance, indexDir *paths.Path, downloadCb func(msg *rpc.DownloadProgress), externalPackageIndexes []*url.URL) error { + libraryIndex := indexDir.Join("library_index.json") if libraryIndex.NotExist() { // The library_index.json file doesn't exists, that means the CLI is run for the first time // so we proceed with the first update that downloads the file req := &rpc.UpdateLibrariesIndexRequest{Instance: instance} - if _, err := UpdateLibrariesIndex(ctx, req, downloadCb); err != nil { + stream, _ := UpdateLibrariesIndexStreamResponseToCallbackFunction(ctx, downloadCb) + if err := srv.UpdateLibrariesIndex(req, stream); err != nil { return err } } @@ -568,14 +660,15 @@ func firstUpdate(ctx context.Context, instance *rpc.Instance, downloadCb func(ms Message: tr("Error downloading index '%s'", URL), Cause: &cmderrors.InvalidURLError{}} } - packageIndexFile := dataDir.Join(packageIndexFileName) + packageIndexFile := indexDir.Join(packageIndexFileName) if packageIndexFile.NotExist() { // The index file doesn't exists, that means the CLI is run for the first time, // or the 3rd party package index URL has just been added. Similarly to the // library update we download that file and all the other package indexes from // additional_urls req := &rpc.UpdateIndexRequest{Instance: instance} - if _, err := UpdateIndex(ctx, req, downloadCb); err != nil { + stream, _ := UpdateIndexStreamResponseToCallbackFunction(ctx, downloadCb) + if err := srv.UpdateIndex(req, stream); err != nil { return err } break diff --git a/commands/internal/instances/instances.go b/commands/internal/instances/instances.go index 019927a4967..272eaaaafdb 100644 --- a/commands/internal/instances/instances.go +++ b/commands/internal/instances/instances.go @@ -25,6 +25,7 @@ import ( rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/arduino-cli/version" "github.com/arduino/go-paths-helper" + "go.bug.st/downloader/v2" ) // coreInstance is an instance of the Arduino Core Services. The user can @@ -133,15 +134,15 @@ func SetLibraryManager(inst *rpc.Instance, lm *librariesmanager.LibrariesManager } // Create a new *rpc.Instance ready to be initialized -func Create(dataDir, packagesDir, downloadsDir *paths.Path, extraUserAgent ...string) (*rpc.Instance, error) { +func Create(dataDir, packagesDir, userPackagesDir, downloadsDir *paths.Path, extraUserAgent string, downloaderConfig downloader.Config) (*rpc.Instance, error) { // Create package manager userAgent := "arduino-cli/" + version.VersionInfo.VersionString - for _, ua := range extraUserAgent { - userAgent += " " + ua + if extraUserAgent != "" { + userAgent += " " + extraUserAgent } tempDir := dataDir.Join("tmp") - pm := packagemanager.NewBuilder(dataDir, packagesDir, downloadsDir, tempDir, userAgent).Build() + pm := packagemanager.NewBuilder(dataDir, packagesDir, userPackagesDir, downloadsDir, tempDir, userAgent, downloaderConfig).Build() lm, _ := librariesmanager.NewBuilder().Build() instance := &coreInstance{ diff --git a/commands/lib.go b/commands/lib.go deleted file mode 100644 index 8794f4ea94a..00000000000 --- a/commands/lib.go +++ /dev/null @@ -1,46 +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 ( - "fmt" - "context" -) - -func (s *Service) ListLibraries(ctx context.Context, in *ListLibrariesReq) (*ListLibrariesResp, error) { - if in.Instance == nil { - return nil, fmt.Errorf("invalid request") - } - instance, ok := instances[in.Instance.Id] - if !ok { - return nil, fmt.Errorf("instance not found") - } - libs := lib.ListLibraries(instance.lm, in.Updatable) - - result := []*pb.Library{} - for _, lib := range libs.Libraries { - result = append(result, &pb.Library{ - Name: lib.Library.Name, - Paragraph: lib.Library.Paragraph, - Precompiled: lib.Library.Precompiled, - }) - } - return &pb.ListLibrariesResp{ - Libraries: result, - }, nil -} -*/ diff --git a/commands/lib/upgrade.go b/commands/lib/upgrade.go deleted file mode 100644 index 0377744b972..00000000000 --- a/commands/lib/upgrade.go +++ /dev/null @@ -1,107 +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 lib - -import ( - "context" - - "github.com/arduino/arduino-cli/commands" - "github.com/arduino/arduino-cli/commands/cmderrors" - "github.com/arduino/arduino-cli/commands/internal/instances" - rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" -) - -// LibraryUpgradeAll upgrades all the available libraries -func LibraryUpgradeAll(req *rpc.LibraryUpgradeAllRequest, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) error { - li, err := instances.GetLibrariesIndex(req.GetInstance()) - if err != nil { - return err - } - - lme, release, err := instances.GetLibraryManagerExplorer(req.GetInstance()) - if err != nil { - return err - } - libsToUpgrade := listLibraries(lme, li, true, false) - release() - - if err := upgrade(req.GetInstance(), libsToUpgrade, downloadCB, taskCB); err != nil { - return err - } - - if err := commands.Init(&rpc.InitRequest{Instance: req.GetInstance()}, nil); err != nil { - return err - } - - return nil -} - -// LibraryUpgrade upgrades a library -func LibraryUpgrade(ctx context.Context, req *rpc.LibraryUpgradeRequest, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) error { - li, err := instances.GetLibrariesIndex(req.GetInstance()) - if err != nil { - return err - } - - lme, release, err := instances.GetLibraryManagerExplorer(req.GetInstance()) - if err != nil { - return err - } - libs := listLibraries(lme, li, false, false) - release() - - // Get the library to upgrade - name := req.GetName() - lib := filterByName(libs, name) - if lib == nil { - // library not installed... - return &cmderrors.LibraryNotFoundError{Library: name} - } - if lib.Available == nil { - taskCB(&rpc.TaskProgress{Message: tr("Library %s is already at the latest version", name), Completed: true}) - return nil - } - - // Install update - return upgrade(req.GetInstance(), []*installedLib{lib}, downloadCB, taskCB) -} - -func upgrade(instance *rpc.Instance, libs []*installedLib, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) error { - for _, lib := range libs { - libInstallReq := &rpc.LibraryInstallRequest{ - Instance: instance, - Name: lib.Library.Name, - Version: "", - NoDeps: false, - NoOverwrite: false, - } - err := LibraryInstall(context.Background(), libInstallReq, downloadCB, taskCB) - if err != nil { - return err - } - } - - return nil -} - -func filterByName(libs []*installedLib, name string) *installedLib { - for _, lib := range libs { - if lib.Library.Name == name { - return lib - } - } - return nil -} diff --git a/commands/monitor/monitor.go b/commands/monitor/monitor.go deleted file mode 100644 index 357a885be31..00000000000 --- a/commands/monitor/monitor.go +++ /dev/null @@ -1,176 +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 monitor - -import ( - "context" - "fmt" - "io" - - "github.com/arduino/arduino-cli/commands/cmderrors" - "github.com/arduino/arduino-cli/commands/internal/instances" - "github.com/arduino/arduino-cli/internal/arduino/cores" - "github.com/arduino/arduino-cli/internal/arduino/cores/packagemanager" - pluggableMonitor "github.com/arduino/arduino-cli/internal/arduino/monitor" - "github.com/arduino/arduino-cli/internal/i18n" - rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" - "github.com/arduino/go-properties-orderedmap" - "github.com/sirupsen/logrus" -) - -var tr = i18n.Tr - -// PortProxy is an io.ReadWriteCloser that maps into the monitor port of the board -type PortProxy struct { - rw io.ReadWriter - changeSettingsCB func(setting, value string) error - closeCB func() error -} - -func (p *PortProxy) Read(buff []byte) (int, error) { - return p.rw.Read(buff) -} - -func (p *PortProxy) Write(buff []byte) (int, error) { - return p.rw.Write(buff) -} - -// Config sets the port configuration setting to the specified value -func (p *PortProxy) Config(setting, value string) error { - return p.changeSettingsCB(setting, value) -} - -// Close the port -func (p *PortProxy) Close() error { - return p.closeCB() -} - -// Monitor opens a communication port. It returns a PortProxy to communicate with the port and a PortDescriptor -// that describes the available configuration settings. -func Monitor(ctx context.Context, req *rpc.MonitorPortOpenRequest) (*PortProxy, *pluggableMonitor.PortDescriptor, error) { - pme, release, err := instances.GetPackageManagerExplorer(req.GetInstance()) - if err != nil { - return nil, nil, err - } - defer release() - - m, boardSettings, err := findMonitorAndSettingsForProtocolAndBoard(pme, req.GetPort().GetProtocol(), req.GetFqbn()) - if err != nil { - return nil, nil, err - } - - if err := m.Run(); err != nil { - return nil, nil, &cmderrors.FailedMonitorError{Cause: err} - } - - descriptor, err := m.Describe() - if err != nil { - m.Quit() - return nil, nil, &cmderrors.FailedMonitorError{Cause: err} - } - - // Apply user-requested settings - if portConfig := req.GetPortConfiguration(); portConfig != nil { - for _, setting := range portConfig.GetSettings() { - boardSettings.Remove(setting.GetSettingId()) // Remove board settings overridden by the user - if err := m.Configure(setting.GetSettingId(), setting.GetValue()); err != nil { - logrus.Errorf("Could not set configuration %s=%s: %s", setting.GetSettingId(), setting.GetValue(), err) - } - } - } - // Apply specific board settings - for setting, value := range boardSettings.AsMap() { - m.Configure(setting, value) - } - - monIO, err := m.Open(req.GetPort().GetAddress(), req.GetPort().GetProtocol()) - if err != nil { - m.Quit() - return nil, nil, &cmderrors.FailedMonitorError{Cause: err} - } - - logrus.Infof("Port %s successfully opened", req.GetPort().GetAddress()) - return &PortProxy{ - rw: monIO, - changeSettingsCB: m.Configure, - closeCB: func() error { - m.Close() - return m.Quit() - }, - }, descriptor, nil -} - -func findMonitorAndSettingsForProtocolAndBoard(pme *packagemanager.Explorer, protocol, fqbn string) (*pluggableMonitor.PluggableMonitor, *properties.Map, error) { - if protocol == "" { - return nil, nil, &cmderrors.MissingPortProtocolError{} - } - - var monitorDepOrRecipe *cores.MonitorDependency - boardSettings := properties.NewMap() - - // If a board is specified search the monitor in the board package first - if fqbn != "" { - fqbn, err := cores.ParseFQBN(fqbn) - if err != nil { - return nil, nil, &cmderrors.InvalidFQBNError{Cause: err} - } - - _, boardPlatform, _, boardProperties, _, err := pme.ResolveFQBN(fqbn) - if err != nil { - return nil, nil, &cmderrors.UnknownFQBNError{Cause: err} - } - - boardSettings = cores.GetMonitorSettings(protocol, boardProperties) - - if mon, ok := boardPlatform.Monitors[protocol]; ok { - monitorDepOrRecipe = mon - } else if recipe, ok := boardPlatform.MonitorsDevRecipes[protocol]; ok { - // If we have a recipe we must resolve it - cmdLine := boardProperties.ExpandPropsInString(recipe) - cmdArgs, err := properties.SplitQuotedString(cmdLine, `"'`, false) - if err != nil { - return nil, nil, &cmderrors.InvalidArgumentError{Message: tr("Invalid recipe in platform.txt"), Cause: err} - } - id := fmt.Sprintf("%s-%s", boardPlatform, protocol) - return pluggableMonitor.New(id, cmdArgs...), boardSettings, nil - } - } - - if monitorDepOrRecipe == nil { - // Otherwise look in all package for a suitable monitor - for _, platformRel := range pme.InstalledPlatformReleases() { - if mon, ok := platformRel.Monitors[protocol]; ok { - monitorDepOrRecipe = mon - break - } - } - } - - if monitorDepOrRecipe == nil { - return nil, nil, &cmderrors.NoMonitorAvailableForProtocolError{Protocol: protocol} - } - - // If it is a monitor dependency, resolve tool and create a monitor client - tool := pme.FindMonitorDependency(monitorDepOrRecipe) - if tool == nil { - return nil, nil, &cmderrors.MonitorNotFoundError{Monitor: monitorDepOrRecipe.String()} - } - - return pluggableMonitor.New( - monitorDepOrRecipe.Name, - tool.InstallDir.Join(monitorDepOrRecipe.Name).String(), - ), boardSettings, nil -} diff --git a/commands/service.go b/commands/service.go new file mode 100644 index 00000000000..46c7bf3e6d5 --- /dev/null +++ b/commands/service.go @@ -0,0 +1,48 @@ +// 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 ( + "context" + + "github.com/arduino/arduino-cli/internal/cli/configuration" + "github.com/arduino/arduino-cli/internal/i18n" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" + "github.com/arduino/arduino-cli/version" +) + +// NewArduinoCoreServer returns an implementation of the ArduinoCoreService gRPC service +// that uses the provided version string. +func NewArduinoCoreServer() rpc.ArduinoCoreServiceServer { + settings := configuration.NewSettings() + + // Setup i18n + i18n.Init(settings.Locale()) + + return &arduinoCoreServerImpl{settings: settings} +} + +type arduinoCoreServerImpl struct { + rpc.UnsafeArduinoCoreServiceServer // Force compile error for unimplemented methods + + // Settings holds configurations of the CLI and the gRPC consumers + settings *configuration.Settings +} + +// Version returns the version of the Arduino CLI +func (s *arduinoCoreServerImpl) Version(ctx context.Context, req *rpc.VersionRequest) (*rpc.VersionResponse, error) { + return &rpc.VersionResponse{Version: version.VersionInfo.VersionString}, nil +} diff --git a/commands/board/details.go b/commands/service_board_details.go similarity index 95% rename from commands/board/details.go rename to commands/service_board_details.go index 22cfb36a354..5f042452582 100644 --- a/commands/board/details.go +++ b/commands/service_board_details.go @@ -13,7 +13,7 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package board +package commands import ( "context" @@ -25,9 +25,9 @@ import ( rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" ) -// Details returns all details for a board including tools and HW identifiers. +// BoardDetails returns all details for a board including tools and HW identifiers. // This command basically gather al the information and translates it into the required grpc struct properties -func Details(ctx context.Context, req *rpc.BoardDetailsRequest) (*rpc.BoardDetailsResponse, error) { +func (s *arduinoCoreServerImpl) BoardDetails(ctx context.Context, req *rpc.BoardDetailsRequest) (*rpc.BoardDetailsResponse, error) { pme, release, err := instances.GetPackageManagerExplorer(req.GetInstance()) if err != nil { return nil, err diff --git a/commands/board/list.go b/commands/service_board_list.go similarity index 77% rename from commands/board/list.go rename to commands/service_board_list.go index 25c6ef53401..aff1085c9c2 100644 --- a/commands/board/list.go +++ b/commands/service_board_list.go @@ -13,7 +13,7 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package board +package commands import ( "context" @@ -29,9 +29,10 @@ import ( "github.com/arduino/arduino-cli/commands/cmderrors" "github.com/arduino/arduino-cli/commands/internal/instances" + f "github.com/arduino/arduino-cli/internal/algorithms" "github.com/arduino/arduino-cli/internal/arduino/cores" "github.com/arduino/arduino-cli/internal/arduino/cores/packagemanager" - "github.com/arduino/arduino-cli/internal/arduino/httpclient" + "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/arduino/arduino-cli/internal/inventory" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/go-properties-orderedmap" @@ -44,7 +45,7 @@ var ( validVidPid = regexp.MustCompile(`0[xX][a-fA-F\d]{4}`) ) -func cachedAPIByVidPid(vid, pid string) ([]*rpc.BoardListItem, error) { +func cachedAPIByVidPid(vid, pid string, settings *configuration.Settings) ([]*rpc.BoardListItem, error) { var resp []*rpc.BoardListItem cacheKey := fmt.Sprintf("cache.builder-api.v3/boards/byvid/pid/%s/%s", vid, pid) @@ -58,7 +59,7 @@ func cachedAPIByVidPid(vid, pid string) ([]*rpc.BoardListItem, error) { } } - resp, err := apiByVidPid(vid, pid) // Perform API requrest + resp, err := apiByVidPid(vid, pid, settings) // Perform API requrest if err == nil { if cachedResp, err := json.Marshal(resp); err == nil { @@ -70,7 +71,7 @@ func cachedAPIByVidPid(vid, pid string) ([]*rpc.BoardListItem, error) { return resp, err } -func apiByVidPid(vid, pid string) ([]*rpc.BoardListItem, error) { +func apiByVidPid(vid, pid string, settings *configuration.Settings) ([]*rpc.BoardListItem, error) { // ensure vid and pid are valid before hitting the API if !validVidPid.MatchString(vid) { return nil, errors.New(tr("Invalid vid value: '%s'", vid)) @@ -83,10 +84,7 @@ func apiByVidPid(vid, pid string) ([]*rpc.BoardListItem, error) { req, _ := http.NewRequest("GET", url, nil) req.Header.Set("Content-Type", "application/json") - // TODO: use proxy if set - - httpClient, err := httpclient.New() - + httpClient, err := settings.NewHttpClient() if err != nil { return nil, fmt.Errorf("%s: %w", tr("failed to initialize http client"), err) } @@ -129,18 +127,18 @@ func apiByVidPid(vid, pid string) ([]*rpc.BoardListItem, error) { }, nil } -func identifyViaCloudAPI(props *properties.Map) ([]*rpc.BoardListItem, error) { +func identifyViaCloudAPI(props *properties.Map, settings *configuration.Settings) ([]*rpc.BoardListItem, error) { // If the port is not USB do not try identification via cloud if !props.ContainsKey("vid") || !props.ContainsKey("pid") { return nil, nil } logrus.Debug("Querying builder API for board identification...") - return cachedAPIByVidPid(props.Get("vid"), props.Get("pid")) + return cachedAPIByVidPid(props.Get("vid"), props.Get("pid"), settings) } // identify returns a list of boards checking first the installed platforms or the Cloud API -func identify(pme *packagemanager.Explorer, port *discovery.Port) ([]*rpc.BoardListItem, error) { +func identify(pme *packagemanager.Explorer, port *discovery.Port, settings *configuration.Settings) ([]*rpc.BoardListItem, error) { boards := []*rpc.BoardListItem{} if port.Properties == nil { return boards, nil @@ -172,7 +170,7 @@ func identify(pme *packagemanager.Explorer, port *discovery.Port) ([]*rpc.BoardL // if installed cores didn't recognize the board, try querying // the builder API if the board is a USB device port if len(boards) == 0 { - items, err := identifyViaCloudAPI(port.Properties) + items, err := identifyViaCloudAPI(port.Properties, settings) if err != nil { // this is bad, but keep going logrus.WithError(err).Debug("Error querying builder API") @@ -201,13 +199,13 @@ func identify(pme *packagemanager.Explorer, port *discovery.Port) ([]*rpc.BoardL return boards, nil } -// List returns a list of boards found by the loaded discoveries. +// BoardList returns a list of boards found by the loaded discoveries. // In case of errors partial results from discoveries that didn't fail // are returned. -func List(req *rpc.BoardListRequest) (r []*rpc.DetectedPort, discoveryStartErrors []error, e error) { +func (s *arduinoCoreServerImpl) BoardList(ctx context.Context, req *rpc.BoardListRequest) (*rpc.BoardListResponse, error) { pme, release, err := instances.GetPackageManagerExplorer(req.GetInstance()) if err != nil { - return nil, nil, err + return nil, err } defer release() @@ -216,19 +214,19 @@ func List(req *rpc.BoardListRequest) (r []*rpc.DetectedPort, discoveryStartError var err error fqbnFilter, err = cores.ParseFQBN(f) if err != nil { - return nil, nil, &cmderrors.InvalidFQBNError{Cause: err} + return nil, &cmderrors.InvalidFQBNError{Cause: err} } } dm := pme.DiscoveryManager() - discoveryStartErrors = dm.Start() + warnings := f.Map(dm.Start(), (error).Error) time.Sleep(time.Duration(req.GetTimeout()) * time.Millisecond) - retVal := []*rpc.DetectedPort{} + ports := []*rpc.DetectedPort{} for _, port := range dm.List() { - boards, err := identify(pme, port) + boards, err := identify(pme, port, s.settings) if err != nil { - return nil, discoveryStartErrors, err + warnings = append(warnings, err.Error()) } // boards slice can be empty at this point if neither the cores nor the @@ -239,10 +237,13 @@ func List(req *rpc.BoardListRequest) (r []*rpc.DetectedPort, discoveryStartError } if fqbnFilter == nil || hasMatchingBoard(b, fqbnFilter) { - retVal = append(retVal, b) + ports = append(ports, b) } } - return retVal, discoveryStartErrors, nil + return &rpc.BoardListResponse{ + Ports: ports, + Warnings: warnings, + }, nil } func hasMatchingBoard(b *rpc.DetectedPort, fqbnFilter *cores.FQBN) bool { @@ -258,29 +259,43 @@ func hasMatchingBoard(b *rpc.DetectedPort, fqbnFilter *cores.FQBN) bool { return false } -// Watch returns a channel that receives boards connection and disconnection events. -func Watch(ctx context.Context, req *rpc.BoardListWatchRequest) (<-chan *rpc.BoardListWatchResponse, error) { +// BoardListWatchProxyToChan return a stream, to be used in BoardListWatch method, +// that proxies all the responses to a channel. +func BoardListWatchProxyToChan(ctx context.Context) (rpc.ArduinoCoreService_BoardListWatchServer, <-chan *rpc.BoardListWatchResponse) { + return streamResponseToChan[rpc.BoardListWatchResponse](ctx) +} + +// BoardListWatch FIXMEDOC +func (s *arduinoCoreServerImpl) BoardListWatch(req *rpc.BoardListWatchRequest, stream rpc.ArduinoCoreService_BoardListWatchServer) error { + syncSend := NewSynchronizedSend(stream.Send) + if req.GetInstance() == nil { + err := fmt.Errorf(tr("no instance specified")) + syncSend.Send(&rpc.BoardListWatchResponse{ + EventType: "error", + Error: err.Error(), + }) + return err + } + pme, release, err := instances.GetPackageManagerExplorer(req.GetInstance()) if err != nil { - return nil, err + return err } defer release() dm := pme.DiscoveryManager() watcher, err := dm.Watch() if err != nil { - return nil, err + return err } go func() { - <-ctx.Done() + <-stream.Context().Done() logrus.Trace("closed watch") watcher.Close() }() - outChan := make(chan *rpc.BoardListWatchResponse) go func() { - defer close(outChan) for event := range watcher.Feed() { port := &rpc.DetectedPort{ Port: rpc.DiscoveryPortToRPC(event.Port), @@ -288,19 +303,19 @@ func Watch(ctx context.Context, req *rpc.BoardListWatchRequest) (<-chan *rpc.Boa boardsError := "" if event.Type == "add" { - boards, err := identify(pme, event.Port) + boards, err := identify(pme, event.Port, s.settings) if err != nil { boardsError = err.Error() } port.MatchingBoards = boards } - outChan <- &rpc.BoardListWatchResponse{ + stream.Send(&rpc.BoardListWatchResponse{ EventType: event.Type, Port: port, Error: boardsError, - } + }) } }() - return outChan, nil + return nil } diff --git a/commands/board/list_test.go b/commands/service_board_list_test.go similarity index 85% rename from commands/board/list_test.go rename to commands/service_board_list_test.go index f6e46f19a46..6fb1366d111 100644 --- a/commands/board/list_test.go +++ b/commands/service_board_list_test.go @@ -13,7 +13,7 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package board +package commands import ( "fmt" @@ -27,13 +27,10 @@ import ( "github.com/arduino/go-properties-orderedmap" discovery "github.com/arduino/pluggable-discovery-protocol-handler/v2" "github.com/stretchr/testify/require" + "go.bug.st/downloader/v2" semver "go.bug.st/relaxed-semver" ) -func init() { - configuration.Settings = configuration.Init("") -} - func TestGetByVidPid(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, ` @@ -51,30 +48,36 @@ func TestGetByVidPid(t *testing.T) { defer ts.Close() vidPidURL = ts.URL - res, err := apiByVidPid("0xf420", "0XF069") + settings := configuration.NewSettings() + res, err := apiByVidPid("0xf420", "0XF069", settings) require.Nil(t, err) require.Len(t, res, 1) require.Equal(t, "Arduino/Genuino MKR1000", res[0].GetName()) require.Equal(t, "arduino:samd:mkr1000", res[0].GetFqbn()) // wrong vid (too long), wrong pid (not an hex value) - _, err = apiByVidPid("0xfffff", "0xDEFG") + + _, err = apiByVidPid("0xfffff", "0xDEFG", settings) require.NotNil(t, err) } func TestGetByVidPidNotFound(t *testing.T) { + settings := configuration.NewSettings() + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotFound) })) defer ts.Close() vidPidURL = ts.URL - res, err := apiByVidPid("0x0420", "0x0069") + res, err := apiByVidPid("0x0420", "0x0069", settings) require.NoError(t, err) require.Empty(t, res) } func TestGetByVidPid5xx(t *testing.T) { + settings := configuration.NewSettings() + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("500 - Ooooops!")) @@ -82,27 +85,30 @@ func TestGetByVidPid5xx(t *testing.T) { defer ts.Close() vidPidURL = ts.URL - res, err := apiByVidPid("0x0420", "0x0069") + res, err := apiByVidPid("0x0420", "0x0069", settings) require.NotNil(t, err) require.Equal(t, "the server responded with status 500 Internal Server Error", err.Error()) require.Len(t, res, 0) } func TestGetByVidPidMalformedResponse(t *testing.T) { + settings := configuration.NewSettings() + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "{}") })) defer ts.Close() vidPidURL = ts.URL - res, err := apiByVidPid("0x0420", "0x0069") + res, err := apiByVidPid("0x0420", "0x0069", settings) require.NotNil(t, err) require.Equal(t, "wrong format in server response", err.Error()) require.Len(t, res, 0) } func TestBoardDetectionViaAPIWithNonUSBPort(t *testing.T) { - items, err := identifyViaCloudAPI(properties.NewMap()) + settings := configuration.NewSettings() + items, err := identifyViaCloudAPI(properties.NewMap(), settings) require.NoError(t, err) require.Empty(t, items) } @@ -114,7 +120,7 @@ func TestBoardIdentifySorting(t *testing.T) { defer paths.TempDir().Join("test").RemoveAll() // We don't really care about the paths in this case - pmb := packagemanager.NewBuilder(dataDir, dataDir, dataDir, dataDir, "test") + pmb := packagemanager.NewBuilder(dataDir, dataDir, nil, dataDir, dataDir, "test", downloader.GetDefaultConfig()) // Create some boards with identical VID:PID combination pack := pmb.GetOrCreatePackage("packager") @@ -150,7 +156,8 @@ func TestBoardIdentifySorting(t *testing.T) { pme, release := pm.NewExplorer() defer release() - res, err := identify(pme, &discovery.Port{Properties: idPrefs}) + settings := configuration.NewSettings() + res, err := identify(pme, &discovery.Port{Properties: idPrefs}, settings) require.NoError(t, err) require.NotNil(t, res) require.Len(t, res, 4) diff --git a/commands/board/listall.go b/commands/service_board_listall.go similarity index 91% rename from commands/board/listall.go rename to commands/service_board_listall.go index 2b4e4d153e2..93df1f4e338 100644 --- a/commands/board/listall.go +++ b/commands/service_board_listall.go @@ -13,22 +13,21 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package board +package commands import ( "context" "sort" "strings" - "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/commands/internal/instances" "github.com/arduino/arduino-cli/internal/arduino/cores" "github.com/arduino/arduino-cli/internal/arduino/utils" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" ) -// ListAll FIXMEDOC -func ListAll(ctx context.Context, req *rpc.BoardListAllRequest) (*rpc.BoardListAllResponse, error) { +// BoardListAll list all the boards provided by installed platforms. +func (s *arduinoCoreServerImpl) BoardListAll(ctx context.Context, req *rpc.BoardListAllRequest) (*rpc.BoardListAllResponse, error) { pme, release, err := instances.GetPackageManagerExplorer(req.GetInstance()) if err != nil { return nil, err @@ -47,8 +46,8 @@ func ListAll(ctx context.Context, req *rpc.BoardListAllRequest) (*rpc.BoardListA } rpcPlatform := &rpc.Platform{ - Metadata: commands.PlatformToRPCPlatformMetadata(platform), - Release: commands.PlatformReleaseToRPC(installedPlatformRelease), + Metadata: platformToRPCPlatformMetadata(platform), + Release: platformReleaseToRPC(installedPlatformRelease), } toTest := []string{ diff --git a/commands/board/search.go b/commands/service_board_search.go similarity index 87% rename from commands/board/search.go rename to commands/service_board_search.go index 00ea3ac1e20..bbe4ca22529 100644 --- a/commands/board/search.go +++ b/commands/service_board_search.go @@ -13,24 +13,23 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package board +package commands import ( "context" "sort" "strings" - "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/commands/internal/instances" "github.com/arduino/arduino-cli/internal/arduino/utils" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" ) -// Search returns all boards that match the search arg. +// BoardSearch returns all boards that match the search arg. // Boards are searched in all platforms, including those in the index that are not yet // installed. Note that platforms that are not installed don't include boards' FQBNs. // If no search argument is used all boards are returned. -func Search(ctx context.Context, req *rpc.BoardSearchRequest) (*rpc.BoardSearchResponse, error) { +func (s *arduinoCoreServerImpl) BoardSearch(ctx context.Context, req *rpc.BoardSearchRequest) (*rpc.BoardSearchResponse, error) { pme, release, err := instances.GetPackageManagerExplorer(req.GetInstance()) if err != nil { return nil, err @@ -68,8 +67,8 @@ func Search(ctx context.Context, req *rpc.BoardSearchRequest) (*rpc.BoardSearchR Fqbn: board.FQBN(), IsHidden: board.IsHidden(), Platform: &rpc.Platform{ - Metadata: commands.PlatformToRPCPlatformMetadata(platform), - Release: commands.PlatformReleaseToRPC(installedPlatformRelease), + Metadata: platformToRPCPlatformMetadata(platform), + Release: platformReleaseToRPC(installedPlatformRelease), }, }) } @@ -83,8 +82,8 @@ func Search(ctx context.Context, req *rpc.BoardSearchRequest) (*rpc.BoardSearchR foundBoards = append(foundBoards, &rpc.BoardListItem{ Name: strings.Trim(board.Name, " \n"), Platform: &rpc.Platform{ - Metadata: commands.PlatformToRPCPlatformMetadata(platform), - Release: commands.PlatformReleaseToRPC(latestPlatformRelease), + Metadata: platformToRPCPlatformMetadata(platform), + Release: platformReleaseToRPC(latestPlatformRelease), }, }) } diff --git a/commands/cache/clean.go b/commands/service_cache_clean.go similarity index 73% rename from commands/cache/clean.go rename to commands/service_cache_clean.go index f823b56fffa..5897ebd2e99 100644 --- a/commands/cache/clean.go +++ b/commands/service_cache_clean.go @@ -1,6 +1,6 @@ // This file is part of arduino-cli. // -// Copyright 2024 ARDUINO SA (http://www.arduino.cc/) +// 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. @@ -13,18 +13,17 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package cache +package commands import ( "context" - "github.com/arduino/arduino-cli/internal/cli/configuration" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" ) // CleanDownloadCacheDirectory clean the download cache directory (where archives are downloaded). -func CleanDownloadCacheDirectory(ctx context.Context, req *rpc.CleanDownloadCacheDirectoryRequest) (*rpc.CleanDownloadCacheDirectoryResponse, error) { - cachePath := configuration.DownloadsDir(configuration.Settings) +func (s *arduinoCoreServerImpl) CleanDownloadCacheDirectory(ctx context.Context, req *rpc.CleanDownloadCacheDirectoryRequest) (*rpc.CleanDownloadCacheDirectoryResponse, error) { + cachePath := s.settings.DownloadsDir() err := cachePath.RemoveAll() if err != nil { return nil, err diff --git a/commands/updatecheck/check_for_updates.go b/commands/service_check_for_updates.go similarity index 84% rename from commands/updatecheck/check_for_updates.go rename to commands/service_check_for_updates.go index e7cda71638d..15221905d56 100644 --- a/commands/updatecheck/check_for_updates.go +++ b/commands/service_check_for_updates.go @@ -13,15 +13,13 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package updatecheck +package commands import ( "context" "strings" "time" - "github.com/arduino/arduino-cli/internal/arduino/httpclient" - "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/inventory" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" @@ -29,13 +27,13 @@ import ( semver "go.bug.st/relaxed-semver" ) -func CheckForArduinoCLIUpdates(ctx context.Context, req *rpc.CheckForArduinoCLIUpdatesRequest) (*rpc.CheckForArduinoCLIUpdatesResponse, error) { +func (s *arduinoCoreServerImpl) CheckForArduinoCLIUpdates(ctx context.Context, req *rpc.CheckForArduinoCLIUpdatesRequest) (*rpc.CheckForArduinoCLIUpdatesResponse, error) { currentVersion, err := semver.Parse(version.VersionInfo.VersionString) if err != nil { return nil, err } - if !shouldCheckForUpdate(currentVersion) && !req.GetForceCheck() { + if !s.shouldCheckForUpdate(currentVersion) && !req.GetForceCheck() { return &rpc.CheckForArduinoCLIUpdatesResponse{}, nil } @@ -45,7 +43,7 @@ func CheckForArduinoCLIUpdates(ctx context.Context, req *rpc.CheckForArduinoCLIU inventory.WriteStore() }() - latestVersion, err := semver.Parse(getLatestRelease()) + latestVersion, err := semver.Parse(s.getLatestRelease()) if err != nil { return nil, err } @@ -62,13 +60,13 @@ func CheckForArduinoCLIUpdates(ctx context.Context, req *rpc.CheckForArduinoCLIU // shouldCheckForUpdate return true if it actually makes sense to check for new updates, // false in all other cases. -func shouldCheckForUpdate(currentVersion *semver.Version) bool { +func (s *arduinoCoreServerImpl) shouldCheckForUpdate(currentVersion *semver.Version) bool { if strings.Contains(currentVersion.String(), "git-snapshot") || strings.Contains(currentVersion.String(), "nightly") { // This is a dev build, no need to check for updates return false } - if !configuration.Settings.GetBool("updater.enable_notification") { + if !s.settings.GetBool("updater.enable_notification") { // Don't check if the user disabled the notification return false } @@ -84,8 +82,8 @@ func shouldCheckForUpdate(currentVersion *semver.Version) bool { // getLatestRelease queries the official Arduino download server for the latest release, // if there are no errors or issues a version string is returned, in all other case an empty string. -func getLatestRelease() string { - client, err := httpclient.New() +func (s *arduinoCoreServerImpl) getLatestRelease() string { + client, err := s.settings.NewHttpClient() if err != nil { return "" } diff --git a/commands/compile/compile.go b/commands/service_compile.go similarity index 73% rename from commands/compile/compile.go rename to commands/service_compile.go index 7a124aceb00..9722c72671a 100644 --- a/commands/compile/compile.go +++ b/commands/service_compile.go @@ -13,7 +13,7 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package compile +package commands import ( "context" @@ -22,6 +22,7 @@ import ( "io" "sort" "strings" + "time" "github.com/arduino/arduino-cli/commands/cmderrors" "github.com/arduino/arduino-cli/commands/internal/instances" @@ -31,46 +32,68 @@ import ( "github.com/arduino/arduino-cli/internal/arduino/sketch" "github.com/arduino/arduino-cli/internal/arduino/utils" "github.com/arduino/arduino-cli/internal/buildcache" - "github.com/arduino/arduino-cli/internal/cli/configuration" - "github.com/arduino/arduino-cli/internal/i18n" "github.com/arduino/arduino-cli/internal/inventory" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" paths "github.com/arduino/go-paths-helper" "github.com/sirupsen/logrus" ) -var tr = i18n.Tr +// CompilerServerToStreams creates a gRPC CompileServer that sends the responses to the provided streams. +// The returned callback function can be used to retrieve the builder result after the compilation is done. +func CompilerServerToStreams(ctx context.Context, stdOut, stderr io.Writer) (server rpc.ArduinoCoreService_CompileServer, resultCB func() *rpc.BuilderResult) { + var builderResult *rpc.BuilderResult + stream := streamResponseToCallback(ctx, func(resp *rpc.CompileResponse) error { + if out := resp.GetOutStream(); len(out) > 0 { + if _, err := stdOut.Write(out); err != nil { + return err + } + } + if err := resp.GetErrStream(); len(err) > 0 { + if _, err := stderr.Write(err); err != nil { + return err + } + } + if result := resp.GetResult(); result != nil { + builderResult = result + } + return nil + }) + return stream, func() *rpc.BuilderResult { return builderResult } +} -// Compile FIXMEDOC -func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream io.Writer, progressCB rpc.TaskProgressCB) (r *rpc.BuilderResult, e error) { - exportBinaries := configuration.Settings.GetBool("sketch.always_export_binaries") +// Compile performs a compilation of a sketch. +func (s *arduinoCoreServerImpl) Compile(req *rpc.CompileRequest, stream rpc.ArduinoCoreService_CompileServer) error { + ctx := stream.Context() + syncSend := NewSynchronizedSend(stream.Send) + + exportBinaries := s.settings.SketchAlwaysExportBinaries() if e := req.ExportBinaries; e != nil { exportBinaries = *e } pme, release, err := instances.GetPackageManagerExplorer(req.GetInstance()) if err != nil { - return nil, err + return err } defer release() if pme.Dirty() { - return nil, &cmderrors.InstanceNeedsReinitialization{} + return &cmderrors.InstanceNeedsReinitialization{} } lm, err := instances.GetLibraryManager(req.GetInstance()) if err != nil { - return nil, err + return err } logrus.Tracef("Compile %s for %s started", req.GetSketchPath(), req.GetFqbn()) if req.GetSketchPath() == "" { - return nil, &cmderrors.MissingSketchPathError{} + return &cmderrors.MissingSketchPathError{} } sketchPath := paths.New(req.GetSketchPath()) sk, err := sketch.New(sketchPath) if err != nil { - return nil, &cmderrors.CantOpenSketchError{Cause: err} + return &cmderrors.CantOpenSketchError{Cause: err} } fqbnIn := req.GetFqbn() @@ -82,25 +105,30 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream } } if fqbnIn == "" { - return nil, &cmderrors.MissingFQBNError{} + return &cmderrors.MissingFQBNError{} } fqbn, err := cores.ParseFQBN(fqbnIn) if err != nil { - return nil, &cmderrors.InvalidFQBNError{Cause: err} + return &cmderrors.InvalidFQBNError{Cause: err} } _, targetPlatform, targetBoard, boardBuildProperties, buildPlatform, err := pme.ResolveFQBN(fqbn) if err != nil { if targetPlatform == nil { - return nil, &cmderrors.PlatformNotFoundError{ + return &cmderrors.PlatformNotFoundError{ Platform: fmt.Sprintf("%s:%s", fqbn.Package, fqbn.PlatformArch), Cause: fmt.Errorf(tr("platform not installed")), } } - return nil, &cmderrors.InvalidFQBNError{Cause: err} + return &cmderrors.InvalidFQBNError{Cause: err} } - r = &rpc.BuilderResult{} + r := &rpc.BuilderResult{} + defer func() { + syncSend.Send(&rpc.CompileResponse{ + Message: &rpc.CompileResponse_Result{Result: r}, + }) + }() r.BoardPlatform = targetPlatform.ToRPCPlatformReference() r.BuildPlatform = buildPlatform.ToRPCPlatformReference() @@ -123,7 +151,7 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream encryptProp := boardBuildProperties.ContainsKey("build.keys.encrypt_key") // we verify that all the properties for the secure boot keys are defined or none of them is defined. if !(keychainProp == signProp && signProp == encryptProp) { - return nil, fmt.Errorf(tr("Firmware encryption/signing requires all the following properties to be defined: %s", "build.keys.keychain, build.keys.sign_key, build.keys.encrypt_key")) + return fmt.Errorf(tr("Firmware encryption/signing requires all the following properties to be defined: %s", "build.keys.keychain, build.keys.sign_key, build.keys.encrypt_key")) } // Generate or retrieve build path @@ -132,7 +160,7 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream buildPath = paths.New(req.GetBuildPath()).Canonical() if in, _ := buildPath.IsInsideDir(sk.FullPath); in && buildPath.IsDir() { if sk.AdditionalFiles, err = removeBuildFromSketchFiles(sk.AdditionalFiles, buildPath); err != nil { - return nil, err + return err } } } @@ -140,11 +168,14 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream buildPath = sk.DefaultBuildPath() } if err = buildPath.MkdirAll(); err != nil { - return nil, &cmderrors.PermissionDeniedError{Message: tr("Cannot create build directory"), Cause: err} + return &cmderrors.PermissionDeniedError{Message: tr("Cannot create build directory"), Cause: err} } buildcache.New(buildPath.Parent()).GetOrCreate(buildPath.Base()) // cache is purged after compilation to not remove entries that might be required - defer maybePurgeBuildCache() + + defer maybePurgeBuildCache( + s.settings.GetCompilationsBeforeBuildCachePurge(), + s.settings.GetBuildCacheTTL().Abs()) var coreBuildCachePath *paths.Path if req.GetBuildCachePath() == "" { @@ -152,28 +183,46 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream } else { buildCachePath, err := paths.New(req.GetBuildCachePath()).Abs() if err != nil { - return nil, &cmderrors.PermissionDeniedError{Message: tr("Cannot create build cache directory"), Cause: err} + return &cmderrors.PermissionDeniedError{Message: tr("Cannot create build cache directory"), Cause: err} } if err := buildCachePath.MkdirAll(); err != nil { - return nil, &cmderrors.PermissionDeniedError{Message: tr("Cannot create build cache directory"), Cause: err} + return &cmderrors.PermissionDeniedError{Message: tr("Cannot create build cache directory"), Cause: err} } coreBuildCachePath = buildCachePath.Join("core") } if _, err := pme.FindToolsRequiredForBuild(targetPlatform, buildPlatform); err != nil { - return nil, err + return err } actualPlatform := buildPlatform otherLibrariesDirs := paths.NewPathList(req.GetLibraries()...) - otherLibrariesDirs.Add(configuration.LibrariesDir(configuration.Settings)) + otherLibrariesDirs.Add(s.settings.LibrariesDir()) var libsManager *librariesmanager.LibrariesManager if pme.GetProfile() != nil { libsManager = lm } + outStream := feedStreamTo(func(data []byte) { + syncSend.Send(&rpc.CompileResponse{ + Message: &rpc.CompileResponse_OutStream{OutStream: data}, + }) + }) + defer outStream.Close() + errStream := feedStreamTo(func(data []byte) { + syncSend.Send(&rpc.CompileResponse{ + Message: &rpc.CompileResponse_ErrStream{ErrStream: data}, + }) + }) + defer errStream.Close() + progressCB := func(p *rpc.TaskProgress) { + syncSend.Send(&rpc.CompileResponse{ + Message: &rpc.CompileResponse_Progress{Progress: p}, + }) + } sketchBuilder, err := builder.NewBuilder( + ctx, sk, boardBuildProperties, buildPath, @@ -181,9 +230,9 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream coreBuildCachePath, int(req.GetJobs()), req.GetBuildProperties(), - configuration.HardwareDirectories(configuration.Settings), + s.settings.HardwareDirectories(), otherLibrariesDirs, - configuration.IDEBuiltinLibrariesDir(configuration.Settings), + s.settings.IDEBuiltinLibrariesDir(), fqbn, req.GetClean(), req.GetSourceOverride(), @@ -198,14 +247,14 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream ) if err != nil { if strings.Contains(err.Error(), "invalid build properties") { - return nil, &cmderrors.InvalidArgumentError{Message: tr("Invalid build properties"), Cause: err} + return &cmderrors.InvalidArgumentError{Message: tr("Invalid build properties"), Cause: err} } if errors.Is(err, builder.ErrSketchCannotBeLocatedInBuildPath) { - return r, &cmderrors.CompileFailedError{ + return &cmderrors.CompileFailedError{ Message: tr("Sketch cannot be located in build path. Please specify a different build path"), } } - return r, &cmderrors.CompileFailedError{Message: err.Error()} + return &cmderrors.CompileFailedError{Message: err.Error()} } defer func() { @@ -235,7 +284,7 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream // Just get build properties and exit if req.GetShowProperties() { - return r, nil + return nil } if req.GetPreprocess() { @@ -243,10 +292,10 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream preprocessedSketch, err := sketchBuilder.Preprocess() if err != nil { err = &cmderrors.CompileFailedError{Message: err.Error()} - return r, err + return err } _, err = outStream.Write(preprocessedSketch) - return r, err + return err } defer func() { @@ -288,7 +337,7 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream } if err := sketchBuilder.Build(); err != nil { - return r, &cmderrors.CompileFailedError{Message: err.Error()} + return &cmderrors.CompileFailedError{Message: err.Error()} } // If the export directory is set we assume you want to export the binaries @@ -300,9 +349,8 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream exportBinaries = false } if exportBinaries { - err := sketchBuilder.RunRecipe("recipe.hooks.savehex.presavehex", ".pattern", false) - if err != nil { - return r, err + if err := sketchBuilder.RunRecipe("recipe.hooks.savehex.presavehex", ".pattern", false); err != nil { + return err } exportPath := paths.New(req.GetExportDir()) @@ -316,44 +364,40 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream if !buildPath.EqualsTo(exportPath) { logrus.WithField("path", exportPath).Trace("Saving sketch to export path.") if err := exportPath.MkdirAll(); err != nil { - return r, &cmderrors.PermissionDeniedError{Message: tr("Error creating output dir"), Cause: err} + return &cmderrors.PermissionDeniedError{Message: tr("Error creating output dir"), Cause: err} } baseName, ok := sketchBuilder.GetBuildProperties().GetOk("build.project_name") // == "sketch.ino" if !ok { - return r, &cmderrors.MissingPlatformPropertyError{Property: "build.project_name"} + return &cmderrors.MissingPlatformPropertyError{Property: "build.project_name"} } buildFiles, err := sketchBuilder.GetBuildPath().ReadDir() if err != nil { - return r, &cmderrors.PermissionDeniedError{Message: tr("Error reading build directory"), Cause: err} + return &cmderrors.PermissionDeniedError{Message: tr("Error reading build directory"), Cause: err} } buildFiles.FilterPrefix(baseName) for _, buildFile := range buildFiles { exportedFile := exportPath.Join(buildFile.Base()) logrus.WithField("src", buildFile).WithField("dest", exportedFile).Trace("Copying artifact.") if err = buildFile.CopyTo(exportedFile); err != nil { - return r, &cmderrors.PermissionDeniedError{Message: tr("Error copying output file %s", buildFile), Cause: err} + return &cmderrors.PermissionDeniedError{Message: tr("Error copying output file %s", buildFile), Cause: err} } } } - err = sketchBuilder.RunRecipe("recipe.hooks.savehex.postsavehex", ".pattern", false) - if err != nil { - return r, err + if err = sketchBuilder.RunRecipe("recipe.hooks.savehex.postsavehex", ".pattern", false); err != nil { + return err } } r.ExecutableSectionsSize = sketchBuilder.ExecutableSectionsSize().ToRPCExecutableSectionSizeArray() logrus.Tracef("Compile %s for %s successful", sk.Name, fqbnIn) - - return r, nil + return nil } // maybePurgeBuildCache runs the build files cache purge if the policy conditions are met. -func maybePurgeBuildCache() { - - compilationsBeforePurge := configuration.Settings.GetUint("build_cache.compilations_before_purge") +func maybePurgeBuildCache(compilationsBeforePurge uint, cacheTTL time.Duration) { // 0 means never purge if compilationsBeforePurge == 0 { return @@ -366,7 +410,6 @@ func maybePurgeBuildCache() { return } inventory.Store.Set("build_cache.compilation_count_since_last_purge", 0) - cacheTTL := configuration.Settings.GetDuration("build_cache.ttl").Abs() buildcache.New(paths.TempDir().Join("arduino", "cores")).Purge(cacheTTL) buildcache.New(paths.TempDir().Join("arduino", "sketches")).Purge(cacheTTL) } diff --git a/commands/daemon/debug.go b/commands/service_debug.go similarity index 81% rename from commands/daemon/debug.go rename to commands/service_debug.go index d3de963bae4..0f618ce7598 100644 --- a/commands/daemon/debug.go +++ b/commands/service_debug.go @@ -13,21 +13,20 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package daemon +package commands import ( "context" "errors" "os" - cmd "github.com/arduino/arduino-cli/commands/debug" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" ) // Debug returns a stream response that can be used to fetch data from the // target. The first message passed through the `Debug` request must // contain DebugRequest configuration params, not data. -func (s *ArduinoCoreServerImpl) Debug(stream rpc.ArduinoCoreService_DebugServer) error { +func (s *arduinoCoreServerImpl) Debug(stream rpc.ArduinoCoreService_DebugServer) error { // Grab the first message msg, err := stream.Recv() if err != nil { @@ -44,7 +43,7 @@ func (s *ArduinoCoreServerImpl) Debug(stream rpc.ArduinoCoreService_DebugServer) signalChan := make(chan os.Signal) defer close(signalChan) outStream := feedStreamTo(func(data []byte) { stream.Send(&rpc.DebugResponse{Data: data}) }) - resp, debugErr := cmd.Debug(stream.Context(), req, + resp, debugErr := Debug(stream.Context(), req, consumeStreamFrom(func() ([]byte, error) { command, err := stream.Recv() if command.GetSendInterrupt() { @@ -62,13 +61,11 @@ func (s *ArduinoCoreServerImpl) Debug(stream rpc.ArduinoCoreService_DebugServer) } // GetDebugConfig return metadata about a debug session -func (s *ArduinoCoreServerImpl) GetDebugConfig(ctx context.Context, req *rpc.GetDebugConfigRequest) (*rpc.GetDebugConfigResponse, error) { - res, err := cmd.GetDebugConfig(ctx, req) - return res, convertErrorToRPCStatus(err) +func (s *arduinoCoreServerImpl) GetDebugConfig(ctx context.Context, req *rpc.GetDebugConfigRequest) (*rpc.GetDebugConfigResponse, error) { + return GetDebugConfig(ctx, req) } // IsDebugSupported checks if debugging is supported for a given configuration -func (s *ArduinoCoreServerImpl) IsDebugSupported(ctx context.Context, req *rpc.IsDebugSupportedRequest) (*rpc.IsDebugSupportedResponse, error) { - res, err := cmd.IsDebugSupported(ctx, req) - return res, convertErrorToRPCStatus(err) +func (s *arduinoCoreServerImpl) IsDebugSupported(ctx context.Context, req *rpc.IsDebugSupportedRequest) (*rpc.IsDebugSupportedResponse, error) { + return IsDebugSupported(ctx, req) } diff --git a/commands/debug/debug_info.go b/commands/service_debug_config.go similarity index 99% rename from commands/debug/debug_info.go rename to commands/service_debug_config.go index 5528dd53b8f..0f4040011f8 100644 --- a/commands/debug/debug_info.go +++ b/commands/service_debug_config.go @@ -13,7 +13,7 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package debug +package commands import ( "context" diff --git a/commands/debug/debug.go b/commands/service_debug_run.go similarity index 98% rename from commands/debug/debug.go rename to commands/service_debug_run.go index 431c8fcb830..47ecbf4fe04 100644 --- a/commands/debug/debug.go +++ b/commands/service_debug_run.go @@ -13,7 +13,7 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package debug +package commands import ( "context" @@ -27,14 +27,11 @@ import ( "github.com/arduino/arduino-cli/commands/cmderrors" "github.com/arduino/arduino-cli/commands/internal/instances" "github.com/arduino/arduino-cli/internal/arduino/cores/packagemanager" - "github.com/arduino/arduino-cli/internal/i18n" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/go-paths-helper" "github.com/sirupsen/logrus" ) -var tr = i18n.Tr - // Debug command launches a debug tool for a sketch. // It also implements streams routing: // gRPC In -> tool stdIn diff --git a/commands/debug/debug_test.go b/commands/service_debug_test.go similarity index 95% rename from commands/debug/debug_test.go rename to commands/service_debug_test.go index 9ca9d7cf056..1aa4feb9796 100644 --- a/commands/debug/debug_test.go +++ b/commands/service_debug_test.go @@ -12,7 +12,7 @@ // 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 debug +package commands import ( "fmt" @@ -27,16 +27,17 @@ import ( "github.com/arduino/go-properties-orderedmap" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.bug.st/downloader/v2" ) func TestGetCommandLine(t *testing.T) { - customHardware := paths.New("testdata", "custom_hardware") - dataDir := paths.New("testdata", "data_dir", "packages") + customHardware := paths.New("testdata", "debug", "custom_hardware") + dataDir := paths.New("testdata", "debug", "data_dir", "packages") sketch := "hello" - sketchPath := paths.New("testdata", sketch) + sketchPath := paths.New("testdata", "debug", sketch) require.NoError(t, sketchPath.ToAbs()) - pmb := packagemanager.NewBuilder(nil, nil, nil, nil, "test") + pmb := packagemanager.NewBuilder(nil, nil, nil, nil, nil, "test", downloader.GetDefaultConfig()) pmb.LoadHardwareFromDirectory(customHardware) pmb.LoadHardwareFromDirectory(dataDir) diff --git a/commands/lib/download.go b/commands/service_library_download.go similarity index 57% rename from commands/lib/download.go rename to commands/service_library_download.go index 9d174ae1927..8543e8cdea6 100644 --- a/commands/lib/download.go +++ b/commands/service_library_download.go @@ -13,32 +13,39 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package lib +package commands import ( "context" - "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/commands/cmderrors" "github.com/arduino/arduino-cli/commands/internal/instances" - "github.com/arduino/arduino-cli/internal/arduino/httpclient" "github.com/arduino/arduino-cli/internal/arduino/libraries/librariesindex" - "github.com/arduino/arduino-cli/internal/i18n" + "github.com/arduino/arduino-cli/internal/cli/configuration" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/go-paths-helper" - "github.com/sirupsen/logrus" ) -var tr = i18n.Tr +// LibraryDownloadStreamResponseToCallbackFunction returns a gRPC stream to be used in LibraryDownload that sends +// all responses to the callback function. +func LibraryDownloadStreamResponseToCallbackFunction(ctx context.Context, downloadCB rpc.DownloadProgressCB) rpc.ArduinoCoreService_LibraryDownloadServer { + return streamResponseToCallback(ctx, func(r *rpc.LibraryDownloadResponse) error { + if r.GetProgress() != nil { + downloadCB(r.GetProgress()) + } + return nil + }) +} -// LibraryDownload executes the download of the library. -// A DownloadProgressCB callback function must be passed to monitor download progress. -func LibraryDownload(ctx context.Context, req *rpc.LibraryDownloadRequest, downloadCB rpc.DownloadProgressCB) (*rpc.LibraryDownloadResponse, error) { - logrus.Info("Executing `arduino-cli lib download`") +// LibraryDownload downloads a library +func (s *arduinoCoreServerImpl) LibraryDownload(req *rpc.LibraryDownloadRequest, stream rpc.ArduinoCoreService_LibraryDownloadServer) error { + syncSend := NewSynchronizedSend(stream.Send) + ctx := stream.Context() + downloadCB := func(p *rpc.DownloadProgress) { syncSend.Send(&rpc.LibraryDownloadResponse{Progress: p}) } var downloadsDir *paths.Path if pme, release, err := instances.GetPackageManagerExplorer(req.GetInstance()); err != nil { - return nil, err + return err } else { downloadsDir = pme.DownloadDir release() @@ -46,36 +53,35 @@ func LibraryDownload(ctx context.Context, req *rpc.LibraryDownloadRequest, downl li, err := instances.GetLibrariesIndex(req.GetInstance()) if err != nil { - return nil, err + return err } - logrus.Info("Preparing download") - - version, err := commands.ParseVersion(req.GetVersion()) + version, err := parseVersion(req.GetVersion()) if err != nil { - return nil, err + return err } lib, err := li.FindRelease(req.GetName(), version) if err != nil { - return nil, err + return err } - if err := downloadLibrary(downloadsDir, lib, downloadCB, func(*rpc.TaskProgress) {}, "download"); err != nil { - return nil, err + if err := downloadLibrary(ctx, downloadsDir, lib, downloadCB, func(*rpc.TaskProgress) {}, "download", s.settings); err != nil { + return err } - return &rpc.LibraryDownloadResponse{}, nil + return syncSend.Send(&rpc.LibraryDownloadResponse{}) } -func downloadLibrary(downloadsDir *paths.Path, libRelease *librariesindex.Release, - downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB, queryParameter string) error { +func downloadLibrary(_ context.Context, downloadsDir *paths.Path, libRelease *librariesindex.Release, + downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB, queryParameter string, settings *configuration.Settings) error { taskCB(&rpc.TaskProgress{Name: tr("Downloading %s", libRelease)}) - config, err := httpclient.GetDownloaderConfig() + config, err := settings.DownloaderConfig() if err != nil { return &cmderrors.FailedDownloadError{Message: tr("Can't download library"), Cause: err} } + // TODO: Pass context if err := libRelease.Resource.Download(downloadsDir, config, libRelease.String(), downloadCB, queryParameter); err != nil { return &cmderrors.FailedDownloadError{Message: tr("Can't download library"), Cause: err} } diff --git a/commands/lib/install.go b/commands/service_library_install.go similarity index 66% rename from commands/lib/install.go rename to commands/service_library_install.go index 873e442e4a0..20b98695de2 100644 --- a/commands/lib/install.go +++ b/commands/service_library_install.go @@ -13,14 +13,13 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package lib +package commands import ( "context" "errors" "fmt" - "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/commands/cmderrors" "github.com/arduino/arduino-cli/commands/internal/instances" "github.com/arduino/arduino-cli/internal/arduino/libraries" @@ -31,8 +30,27 @@ import ( "github.com/sirupsen/logrus" ) +// LibraryInstallStreamResponseToCallbackFunction returns a gRPC stream to be used in LibraryInstall that sends +// all responses to the callback function. +func LibraryInstallStreamResponseToCallbackFunction(ctx context.Context, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) rpc.ArduinoCoreService_LibraryInstallServer { + return streamResponseToCallback(ctx, func(r *rpc.LibraryInstallResponse) error { + if r.GetProgress() != nil { + downloadCB(r.GetProgress()) + } + if r.GetTaskProgress() != nil { + taskCB(r.GetTaskProgress()) + } + return nil + }) +} + // LibraryInstall resolves the library dependencies, then downloads and installs the libraries into the install location. -func LibraryInstall(ctx context.Context, req *rpc.LibraryInstallRequest, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) error { +func (s *arduinoCoreServerImpl) LibraryInstall(req *rpc.LibraryInstallRequest, stream rpc.ArduinoCoreService_LibraryInstallServer) error { + ctx := stream.Context() + syncSend := NewSynchronizedSend(stream.Send) + downloadCB := func(p *rpc.DownloadProgress) { syncSend.Send(&rpc.LibraryInstallResponse{Progress: p}) } + taskCB := func(p *rpc.TaskProgress) { syncSend.Send(&rpc.LibraryInstallResponse{TaskProgress: p}) } + // Obtain the library index from the manager li, err := instances.GetLibrariesIndex(req.GetInstance()) if err != nil { @@ -52,7 +70,7 @@ func LibraryInstall(ctx context.Context, req *rpc.LibraryInstallRequest, downloa return err } - res, err := libraryResolveDependencies(ctx, lme, li, req.GetName(), req.GetVersion(), req.GetNoOverwrite()) + res, err := libraryResolveDependencies(lme, li, req.GetName(), req.GetVersion(), req.GetNoOverwrite()) releaseLme() if err != nil { return err @@ -91,7 +109,7 @@ func LibraryInstall(ctx context.Context, req *rpc.LibraryInstallRequest, downloa libReleasesToInstall := map[*librariesindex.Release]*librariesmanager.LibraryInstallPlan{} installLocation := libraries.FromRPCLibraryInstallLocation(req.GetInstallLocation()) for _, lib := range toInstall { - version, err := commands.ParseVersion(lib.GetVersionRequired()) + version, err := parseVersion(lib.GetVersionRequired()) if err != nil { return err } @@ -129,7 +147,7 @@ func LibraryInstall(ctx context.Context, req *rpc.LibraryInstallRequest, downloa downloadReason += "-builtin" } } - if err := downloadLibrary(downloadsDir, libRelease, downloadCB, taskCB, downloadReason); err != nil { + if err := downloadLibrary(ctx, downloadsDir, libRelease, downloadCB, taskCB, downloadReason, s.settings); err != nil { return err } if err := installLibrary(lmi, downloadsDir, libRelease, installTask, taskCB); err != nil { @@ -137,7 +155,10 @@ func LibraryInstall(ctx context.Context, req *rpc.LibraryInstallRequest, downloa } } - if err := commands.Init(&rpc.InitRequest{Instance: req.GetInstance()}, nil); err != nil { + err = s.Init( + &rpc.InitRequest{Instance: req.GetInstance()}, + InitStreamResponseToCallbackFunction(ctx, nil)) + if err != nil { return err } @@ -166,8 +187,23 @@ func installLibrary(lmi *librariesmanager.Installer, downloadsDir *paths.Path, l return nil } +// ZipLibraryInstallStreamResponseToCallbackFunction returns a gRPC stream to be used in ZipLibraryInstall that sends +// all responses to the callback function. +func ZipLibraryInstallStreamResponseToCallbackFunction(ctx context.Context, taskCB rpc.TaskProgressCB) rpc.ArduinoCoreService_ZipLibraryInstallServer { + return streamResponseToCallback(ctx, func(r *rpc.ZipLibraryInstallResponse) error { + if r.GetTaskProgress() != nil { + taskCB(r.GetTaskProgress()) + } + return nil + }) +} + // ZipLibraryInstall FIXMEDOC -func ZipLibraryInstall(ctx context.Context, req *rpc.ZipLibraryInstallRequest, taskCB rpc.TaskProgressCB) error { +func (s *arduinoCoreServerImpl) ZipLibraryInstall(req *rpc.ZipLibraryInstallRequest, stream rpc.ArduinoCoreService_ZipLibraryInstallServer) error { + ctx := stream.Context() + syncSend := NewSynchronizedSend(stream.Send) + taskCB := func(p *rpc.TaskProgress) { syncSend.Send(&rpc.ZipLibraryInstallResponse{TaskProgress: p}) } + lm, err := instances.GetLibraryManager(req.GetInstance()) if err != nil { return err @@ -181,14 +217,30 @@ func ZipLibraryInstall(ctx context.Context, req *rpc.ZipLibraryInstallRequest, t return nil } +// GitLibraryInstallStreamResponseToCallbackFunction returns a gRPC stream to be used in GitLibraryInstall that sends +// all responses to the callback function. +func GitLibraryInstallStreamResponseToCallbackFunction(ctx context.Context, taskCB rpc.TaskProgressCB) rpc.ArduinoCoreService_GitLibraryInstallServer { + return streamResponseToCallback(ctx, func(r *rpc.GitLibraryInstallResponse) error { + if r.GetTaskProgress() != nil { + taskCB(r.GetTaskProgress()) + } + return nil + }) +} + // GitLibraryInstall FIXMEDOC -func GitLibraryInstall(ctx context.Context, req *rpc.GitLibraryInstallRequest, taskCB rpc.TaskProgressCB) error { +func (s *arduinoCoreServerImpl) GitLibraryInstall(req *rpc.GitLibraryInstallRequest, stream rpc.ArduinoCoreService_GitLibraryInstallServer) error { + syncSend := NewSynchronizedSend(stream.Send) + taskCB := func(p *rpc.TaskProgress) { syncSend.Send(&rpc.GitLibraryInstallResponse{TaskProgress: p}) } lm, err := instances.GetLibraryManager(req.GetInstance()) if err != nil { return err } lmi, release := lm.NewInstaller() defer release() + + // TODO: pass context + // ctx := stream.Context() if err := lmi.InstallGitLib(req.GetUrl(), req.GetOverwrite()); err != nil { return &cmderrors.FailedLibraryInstallError{Cause: err} } diff --git a/commands/lib/list.go b/commands/service_library_list.go similarity index 96% rename from commands/lib/list.go rename to commands/service_library_list.go index f5b28bdc426..be293c6161d 100644 --- a/commands/lib/list.go +++ b/commands/service_library_list.go @@ -13,7 +13,7 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package lib +package commands import ( "context" @@ -35,7 +35,7 @@ type installedLib struct { } // LibraryList FIXMEDOC -func LibraryList(ctx context.Context, req *rpc.LibraryListRequest) (*rpc.LibraryListResponse, error) { +func (s *arduinoCoreServerImpl) LibraryList(ctx context.Context, req *rpc.LibraryListRequest) (*rpc.LibraryListResponse, error) { pme, release, err := instances.GetPackageManagerExplorer(req.GetInstance()) if err != nil { return nil, err diff --git a/commands/lib/resolve_deps.go b/commands/service_library_resolve_deps.go similarity index 88% rename from commands/lib/resolve_deps.go rename to commands/service_library_resolve_deps.go index b734e3d4ec7..c50cc2d9f6a 100644 --- a/commands/lib/resolve_deps.go +++ b/commands/service_library_resolve_deps.go @@ -13,14 +13,13 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package lib +package commands import ( "context" "errors" "sort" - "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/commands/cmderrors" "github.com/arduino/arduino-cli/commands/internal/instances" "github.com/arduino/arduino-cli/internal/arduino/libraries" @@ -31,7 +30,7 @@ import ( ) // LibraryResolveDependencies FIXMEDOC -func LibraryResolveDependencies(ctx context.Context, req *rpc.LibraryResolveDependenciesRequest) (*rpc.LibraryResolveDependenciesResponse, error) { +func (s *arduinoCoreServerImpl) LibraryResolveDependencies(ctx context.Context, req *rpc.LibraryResolveDependenciesRequest) (*rpc.LibraryResolveDependenciesResponse, error) { lme, release, err := instances.GetLibraryManagerExplorer(req.GetInstance()) if err != nil { return nil, err @@ -43,12 +42,12 @@ func LibraryResolveDependencies(ctx context.Context, req *rpc.LibraryResolveDepe return nil, err } - return libraryResolveDependencies(ctx, lme, li, req.GetName(), req.GetVersion(), req.GetDoNotUpdateInstalledLibraries()) + return libraryResolveDependencies(lme, li, req.GetName(), req.GetVersion(), req.GetDoNotUpdateInstalledLibraries()) } -func libraryResolveDependencies(ctx context.Context, lme *librariesmanager.Explorer, li *librariesindex.Index, +func libraryResolveDependencies(lme *librariesmanager.Explorer, li *librariesindex.Index, reqName, reqVersion string, noOverwrite bool) (*rpc.LibraryResolveDependenciesResponse, error) { - version, err := commands.ParseVersion(reqVersion) + version, err := parseVersion(reqVersion) if err != nil { return nil, err } diff --git a/commands/lib/search.go b/commands/service_library_search.go similarity index 96% rename from commands/lib/search.go rename to commands/service_library_search.go index 7c2d21cdd70..39ff4c51c6e 100644 --- a/commands/lib/search.go +++ b/commands/service_library_search.go @@ -13,7 +13,7 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package lib +package commands import ( "context" @@ -27,7 +27,7 @@ import ( ) // LibrarySearch FIXMEDOC -func LibrarySearch(ctx context.Context, req *rpc.LibrarySearchRequest) (*rpc.LibrarySearchResponse, error) { +func (s *arduinoCoreServerImpl) LibrarySearch(ctx context.Context, req *rpc.LibrarySearchRequest) (*rpc.LibrarySearchResponse, error) { li, err := instances.GetLibrariesIndex(req.GetInstance()) if err != nil { return nil, err diff --git a/commands/lib/search_test.go b/commands/service_library_search_test.go similarity index 95% rename from commands/lib/search_test.go rename to commands/service_library_search_test.go index faae69ef1a9..84f625b511e 100644 --- a/commands/lib/search_test.go +++ b/commands/service_library_search_test.go @@ -13,7 +13,7 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package lib +package commands import ( "strings" @@ -28,9 +28,9 @@ import ( ) var indexFilename, _ = globals.LibrariesIndexResource.IndexFileName() -var customIndexPath = paths.New("testdata", "test1", indexFilename) -var fullIndexPath = paths.New("testdata", "full", indexFilename) -var qualifiedSearchIndexPath = paths.New("testdata", "qualified_search", indexFilename) +var customIndexPath = paths.New("testdata", "libraries", "test1", indexFilename) +var fullIndexPath = paths.New("testdata", "libraries", "full", indexFilename) +var qualifiedSearchIndexPath = paths.New("testdata", "libraries", "qualified_search", indexFilename) func TestSearchLibrary(t *testing.T) { li, err := librariesindex.LoadIndex(customIndexPath) diff --git a/commands/lib/uninstall.go b/commands/service_library_uninstall.go similarity index 66% rename from commands/lib/uninstall.go rename to commands/service_library_uninstall.go index aee4ea68143..8164427f139 100644 --- a/commands/lib/uninstall.go +++ b/commands/service_library_uninstall.go @@ -13,12 +13,11 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package lib +package commands import ( "context" - "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/commands/cmderrors" "github.com/arduino/arduino-cli/commands/internal/instances" "github.com/arduino/arduino-cli/internal/arduino/libraries" @@ -26,14 +25,29 @@ import ( "github.com/arduino/go-paths-helper" ) -// LibraryUninstall FIXMEDOC -func LibraryUninstall(ctx context.Context, req *rpc.LibraryUninstallRequest, taskCB rpc.TaskProgressCB) error { +// LibraryUninstallStreamResponseToCallbackFunction returns a gRPC stream to be used in LibraryUninstall that sends +// all responses to the callback function. +func LibraryUninstallStreamResponseToCallbackFunction(ctx context.Context, taskCB rpc.TaskProgressCB) rpc.ArduinoCoreService_LibraryUninstallServer { + return streamResponseToCallback(ctx, func(r *rpc.LibraryUninstallResponse) error { + if r.GetTaskProgress() != nil { + taskCB(r.GetTaskProgress()) + } + return nil + }) +} + +// LibraryUninstall uninstalls a library +func (s *arduinoCoreServerImpl) LibraryUninstall(req *rpc.LibraryUninstallRequest, stream rpc.ArduinoCoreService_LibraryUninstallServer) error { + // ctx := stream.Context() + syncSend := NewSynchronizedSend(stream.Send) + taskCB := func(p *rpc.TaskProgress) { syncSend.Send(&rpc.LibraryUninstallResponse{TaskProgress: p}) } + lm, err := instances.GetLibraryManager(req.GetInstance()) if err != nil { return err } - version, err := commands.ParseVersion(req.GetVersion()) + version, err := parseVersion(req.GetVersion()) if err != nil { return err } @@ -48,6 +62,7 @@ func LibraryUninstall(ctx context.Context, req *rpc.LibraryUninstallRequest, tas if len(libs) == 1 { taskCB(&rpc.TaskProgress{Name: tr("Uninstalling %s", libs)}) + // TODO: pass context lmi.Uninstall(libs[0]) taskCB(&rpc.TaskProgress{Completed: true}) return nil diff --git a/commands/service_library_upgrade.go b/commands/service_library_upgrade.go new file mode 100644 index 00000000000..ff8af393705 --- /dev/null +++ b/commands/service_library_upgrade.go @@ -0,0 +1,147 @@ +// 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 ( + "context" + + "github.com/arduino/arduino-cli/commands/cmderrors" + "github.com/arduino/arduino-cli/commands/internal/instances" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" +) + +// LibraryUpgradeAllStreamResponseToCallbackFunction returns a gRPC stream to be used in LibraryUpgradeAll that sends +// all responses to the callback function. +func LibraryUpgradeAllStreamResponseToCallbackFunction(ctx context.Context, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) rpc.ArduinoCoreService_LibraryUpgradeAllServer { + return streamResponseToCallback(ctx, func(r *rpc.LibraryUpgradeAllResponse) error { + if r.GetProgress() != nil { + downloadCB(r.GetProgress()) + } + if r.GetTaskProgress() != nil { + taskCB(r.GetTaskProgress()) + } + return nil + }) +} + +// LibraryUpgradeAll upgrades all the available libraries +func (s *arduinoCoreServerImpl) LibraryUpgradeAll(req *rpc.LibraryUpgradeAllRequest, stream rpc.ArduinoCoreService_LibraryUpgradeAllServer) error { + ctx := stream.Context() + syncSend := NewSynchronizedSend(stream.Send) + downloadCB := func(p *rpc.DownloadProgress) { syncSend.Send(&rpc.LibraryUpgradeAllResponse{Progress: p}) } + taskCB := func(p *rpc.TaskProgress) { syncSend.Send(&rpc.LibraryUpgradeAllResponse{TaskProgress: p}) } + + li, err := instances.GetLibrariesIndex(req.GetInstance()) + if err != nil { + return err + } + + lme, release, err := instances.GetLibraryManagerExplorer(req.GetInstance()) + if err != nil { + return err + } + libsToUpgrade := listLibraries(lme, li, true, false) + release() + + if err := s.libraryUpgrade(ctx, req.GetInstance(), libsToUpgrade, downloadCB, taskCB); err != nil { + return err + } + + err = s.Init( + &rpc.InitRequest{Instance: req.GetInstance()}, + InitStreamResponseToCallbackFunction(ctx, nil)) + if err != nil { + return err + } + + return nil +} + +// LibraryUpgradeStreamResponseToCallbackFunction returns a gRPC stream to be used in LibraryUpgrade that sends +// all responses to the callback function. +func LibraryUpgradeStreamResponseToCallbackFunction(ctx context.Context, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) rpc.ArduinoCoreService_LibraryUpgradeServer { + return streamResponseToCallback(ctx, func(r *rpc.LibraryUpgradeResponse) error { + if r.GetProgress() != nil { + downloadCB(r.GetProgress()) + } + if r.GetTaskProgress() != nil { + taskCB(r.GetTaskProgress()) + } + return nil + }) +} + +// LibraryUpgrade upgrades a library +func (s *arduinoCoreServerImpl) LibraryUpgrade(req *rpc.LibraryUpgradeRequest, stream rpc.ArduinoCoreService_LibraryUpgradeServer) error { + ctx := stream.Context() + syncSend := NewSynchronizedSend(stream.Send) + downloadCB := func(p *rpc.DownloadProgress) { syncSend.Send(&rpc.LibraryUpgradeResponse{Progress: p}) } + taskCB := func(p *rpc.TaskProgress) { syncSend.Send(&rpc.LibraryUpgradeResponse{TaskProgress: p}) } + + li, err := instances.GetLibrariesIndex(req.GetInstance()) + if err != nil { + return err + } + + lme, release, err := instances.GetLibraryManagerExplorer(req.GetInstance()) + if err != nil { + return err + } + libs := listLibraries(lme, li, false, false) + release() + + // Get the library to upgrade + name := req.GetName() + lib := filterByName(libs, name) + if lib == nil { + // library not installed... + return &cmderrors.LibraryNotFoundError{Library: name} + } + if lib.Available == nil { + taskCB(&rpc.TaskProgress{Message: tr("Library %s is already at the latest version", name), Completed: true}) + return nil + } + + // Install update + return s.libraryUpgrade(ctx, req.GetInstance(), []*installedLib{lib}, downloadCB, taskCB) +} + +func (s *arduinoCoreServerImpl) libraryUpgrade(ctx context.Context, instance *rpc.Instance, libs []*installedLib, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) error { + for _, lib := range libs { + libInstallReq := &rpc.LibraryInstallRequest{ + Instance: instance, + Name: lib.Library.Name, + Version: "", + NoDeps: false, + NoOverwrite: false, + } + stream := LibraryInstallStreamResponseToCallbackFunction(ctx, downloadCB, taskCB) + if err := s.LibraryInstall(libInstallReq, stream); err != nil { + return err + } + } + + return nil +} + +func filterByName(libs []*installedLib, name string) *installedLib { + for _, lib := range libs { + if lib.Library.Name == name { + return lib + } + } + return nil +} diff --git a/commands/service_monitor.go b/commands/service_monitor.go new file mode 100644 index 00000000000..1e9b0e7d1f5 --- /dev/null +++ b/commands/service_monitor.go @@ -0,0 +1,299 @@ +// 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 ( + "context" + "errors" + "fmt" + "io" + "sync/atomic" + + "github.com/arduino/arduino-cli/commands/cmderrors" + "github.com/arduino/arduino-cli/commands/internal/instances" + "github.com/arduino/arduino-cli/internal/arduino/cores" + "github.com/arduino/arduino-cli/internal/arduino/cores/packagemanager" + pluggableMonitor "github.com/arduino/arduino-cli/internal/arduino/monitor" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" + "github.com/arduino/go-properties-orderedmap" + "github.com/djherbis/buffer" + "github.com/djherbis/nio/v3" + "github.com/sirupsen/logrus" + "google.golang.org/grpc/metadata" +) + +type monitorPipeServer struct { + ctx context.Context + req atomic.Pointer[rpc.MonitorPortOpenRequest] + in *nio.PipeReader + out *nio.PipeWriter +} + +func (s *monitorPipeServer) Send(resp *rpc.MonitorResponse) error { + if len(resp.GetRxData()) > 0 { + if _, err := s.out.Write(resp.GetRxData()); err != nil { + return err + } + } + return nil +} + +func (s *monitorPipeServer) Recv() (r *rpc.MonitorRequest, e error) { + if conf := s.req.Swap(nil); conf != nil { + return &rpc.MonitorRequest{Message: &rpc.MonitorRequest_OpenRequest{OpenRequest: conf}}, nil + } + buff := make([]byte, 4096) + n, err := s.in.Read(buff) + if err != nil { + return nil, err + } + return &rpc.MonitorRequest{Message: &rpc.MonitorRequest_TxData{TxData: buff[:n]}}, nil +} + +func (s *monitorPipeServer) Context() context.Context { + return s.ctx +} + +func (s *monitorPipeServer) RecvMsg(m any) error { return nil } +func (s *monitorPipeServer) SendHeader(metadata.MD) error { return nil } +func (s *monitorPipeServer) SendMsg(m any) error { return nil } +func (s *monitorPipeServer) SetHeader(metadata.MD) error { return nil } +func (s *monitorPipeServer) SetTrailer(metadata.MD) {} + +type monitorPipeClient struct { + in *nio.PipeReader + out *nio.PipeWriter + close func() +} + +func (s *monitorPipeClient) Read(buff []byte) (n int, err error) { + return s.in.Read(buff) +} + +func (s *monitorPipeClient) Write(buff []byte) (n int, err error) { + return s.out.Write(buff) +} + +func (s *monitorPipeClient) Close() error { + s.in.Close() + s.out.Close() + s.close() + return nil +} + +// MonitorServerToReadWriteCloser creates a monitor server that proxies the data to a ReadWriteCloser. +// The server is returned along with the ReadWriteCloser that can be used to send and receive data +// to the server. The MonitorPortOpenRequest is used to configure the monitor. +func MonitorServerToReadWriteCloser(ctx context.Context, req *rpc.MonitorPortOpenRequest) (rpc.ArduinoCoreService_MonitorServer, io.ReadWriteCloser) { + server := &monitorPipeServer{} + client := &monitorPipeClient{} + server.req.Store(req) + server.ctx, client.close = context.WithCancel(ctx) + client.in, server.out = nio.Pipe(buffer.New(32 * 1024)) + server.in, client.out = nio.Pipe(buffer.New(32 * 1024)) + return server, client +} + +// Monitor opens a port monitor and streams data back and forth until the request is kept alive. +func (s *arduinoCoreServerImpl) Monitor(stream rpc.ArduinoCoreService_MonitorServer) error { + // The configuration must be sent on the first message + req, err := stream.Recv() + if err != nil { + return err + } + + openReq := req.GetOpenRequest() + if openReq == nil { + return &cmderrors.InvalidInstanceError{} + } + + pme, release, err := instances.GetPackageManagerExplorer(openReq.GetInstance()) + if err != nil { + return err + } + defer release() + monitor, boardSettings, err := findMonitorAndSettingsForProtocolAndBoard(pme, openReq.GetPort().GetProtocol(), openReq.GetFqbn()) + if err != nil { + return err + } + if err := monitor.Run(); err != nil { + return &cmderrors.FailedMonitorError{Cause: err} + } + if _, err := monitor.Describe(); err != nil { + monitor.Quit() + return &cmderrors.FailedMonitorError{Cause: err} + } + if portConfig := openReq.GetPortConfiguration(); portConfig != nil { + for _, setting := range portConfig.GetSettings() { + boardSettings.Remove(setting.GetSettingId()) + if err := monitor.Configure(setting.GetSettingId(), setting.GetValue()); err != nil { + logrus.Errorf("Could not set configuration %s=%s: %s", setting.GetSettingId(), setting.GetValue(), err) + } + } + } + for setting, value := range boardSettings.AsMap() { + monitor.Configure(setting, value) + } + monitorIO, err := monitor.Open(openReq.GetPort().GetAddress(), openReq.GetPort().GetProtocol()) + if err != nil { + monitor.Quit() + return &cmderrors.FailedMonitorError{Cause: err} + } + logrus.Infof("Port %s successfully opened", openReq.GetPort().GetAddress()) + monitorClose := func() error { + monitor.Close() + return monitor.Quit() + } + + // Send a message with Success set to true to notify the caller of the port being now active + syncSend := NewSynchronizedSend(stream.Send) + _ = syncSend.Send(&rpc.MonitorResponse{Success: true}) + + ctx, cancel := context.WithCancel(stream.Context()) + gracefulCloseInitiated := &atomic.Bool{} + gracefuleCloseCtx, gracefulCloseCancel := context.WithCancel(context.Background()) + + // gRPC stream receiver (gRPC data -> monitor, config, close) + go func() { + defer cancel() + for { + msg, err := stream.Recv() + if errors.Is(err, io.EOF) { + return + } + if err != nil { + syncSend.Send(&rpc.MonitorResponse{Error: err.Error()}) + return + } + if conf := msg.GetUpdatedConfiguration(); conf != nil { + for _, c := range conf.GetSettings() { + if err := monitor.Configure(c.GetSettingId(), c.GetValue()); err != nil { + syncSend.Send(&rpc.MonitorResponse{Error: err.Error()}) + } + } + } + if closeMsg := msg.GetClose(); closeMsg { + gracefulCloseInitiated.Store(true) + if err := monitorClose(); err != nil { + logrus.WithError(err).Debug("Error closing monitor port") + } + gracefulCloseCancel() + } + tx := msg.GetTxData() + for len(tx) > 0 { + n, err := monitorIO.Write(tx) + if errors.Is(err, io.EOF) { + return + } + if err != nil { + syncSend.Send(&rpc.MonitorResponse{Error: err.Error()}) + return + } + tx = tx[n:] + } + } + }() + + // gRPC stream sender (monitor -> gRPC) + go func() { + defer cancel() // unlock the receiver + buff := make([]byte, 4096) + for { + n, err := monitorIO.Read(buff) + if errors.Is(err, io.EOF) { + break + } + if err != nil { + syncSend.Send(&rpc.MonitorResponse{Error: err.Error()}) + break + } + if err := syncSend.Send(&rpc.MonitorResponse{RxData: buff[:n]}); err != nil { + break + } + } + }() + + <-ctx.Done() + if gracefulCloseInitiated.Load() { + // Port closing has been initiated in the receiver + <-gracefuleCloseCtx.Done() + } else { + monitorClose() + } + return nil +} + +func findMonitorAndSettingsForProtocolAndBoard(pme *packagemanager.Explorer, protocol, fqbn string) (*pluggableMonitor.PluggableMonitor, *properties.Map, error) { + if protocol == "" { + return nil, nil, &cmderrors.MissingPortProtocolError{} + } + + var monitorDepOrRecipe *cores.MonitorDependency + boardSettings := properties.NewMap() + + // If a board is specified search the monitor in the board package first + if fqbn != "" { + fqbn, err := cores.ParseFQBN(fqbn) + if err != nil { + return nil, nil, &cmderrors.InvalidFQBNError{Cause: err} + } + + _, boardPlatform, _, boardProperties, _, err := pme.ResolveFQBN(fqbn) + if err != nil { + return nil, nil, &cmderrors.UnknownFQBNError{Cause: err} + } + + boardSettings = cores.GetMonitorSettings(protocol, boardProperties) + + if mon, ok := boardPlatform.Monitors[protocol]; ok { + monitorDepOrRecipe = mon + } else if recipe, ok := boardPlatform.MonitorsDevRecipes[protocol]; ok { + // If we have a recipe we must resolve it + cmdLine := boardProperties.ExpandPropsInString(recipe) + cmdArgs, err := properties.SplitQuotedString(cmdLine, `"'`, false) + if err != nil { + return nil, nil, &cmderrors.InvalidArgumentError{Message: tr("Invalid recipe in platform.txt"), Cause: err} + } + id := fmt.Sprintf("%s-%s", boardPlatform, protocol) + return pluggableMonitor.New(id, cmdArgs...), boardSettings, nil + } + } + + if monitorDepOrRecipe == nil { + // Otherwise look in all package for a suitable monitor + for _, platformRel := range pme.InstalledPlatformReleases() { + if mon, ok := platformRel.Monitors[protocol]; ok { + monitorDepOrRecipe = mon + break + } + } + } + + if monitorDepOrRecipe == nil { + return nil, nil, &cmderrors.NoMonitorAvailableForProtocolError{Protocol: protocol} + } + + // If it is a monitor dependency, resolve tool and create a monitor client + tool := pme.FindMonitorDependency(monitorDepOrRecipe) + if tool == nil { + return nil, nil, &cmderrors.MonitorNotFoundError{Monitor: monitorDepOrRecipe.String()} + } + + return pluggableMonitor.New( + monitorDepOrRecipe.Name, + tool.InstallDir.Join(monitorDepOrRecipe.Name).String(), + ), boardSettings, nil +} diff --git a/commands/monitor/settings.go b/commands/service_monitor_settings.go similarity index 92% rename from commands/monitor/settings.go rename to commands/service_monitor_settings.go index f6d41c15219..4eb71c7add9 100644 --- a/commands/monitor/settings.go +++ b/commands/service_monitor_settings.go @@ -13,7 +13,7 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package monitor +package commands import ( "context" @@ -25,7 +25,7 @@ import ( ) // EnumerateMonitorPortSettings returns a description of the configuration settings of a monitor port -func EnumerateMonitorPortSettings(ctx context.Context, req *rpc.EnumerateMonitorPortSettingsRequest) (*rpc.EnumerateMonitorPortSettingsResponse, error) { +func (s *arduinoCoreServerImpl) EnumerateMonitorPortSettings(ctx context.Context, req *rpc.EnumerateMonitorPortSettingsRequest) (*rpc.EnumerateMonitorPortSettingsResponse, error) { pme, release, err := instances.GetPackageManagerExplorer(req.GetInstance()) if err != nil { return nil, err diff --git a/commands/core/download.go b/commands/service_platform_download.go similarity index 52% rename from commands/core/download.go rename to commands/service_platform_download.go index 7c686f4aee9..09f49a2d76b 100644 --- a/commands/core/download.go +++ b/commands/service_platform_download.go @@ -13,12 +13,11 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package core +package commands import ( "context" - "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/commands/cmderrors" "github.com/arduino/arduino-cli/commands/internal/instances" "github.com/arduino/arduino-cli/internal/arduino/cores/packagemanager" @@ -28,17 +27,30 @@ import ( var tr = i18n.Tr -// PlatformDownload FIXMEDOC -func PlatformDownload(ctx context.Context, req *rpc.PlatformDownloadRequest, downloadCB rpc.DownloadProgressCB) (*rpc.PlatformDownloadResponse, error) { +// PlatformDownloadStreamResponseToCallbackFunction returns a gRPC stream to be used in PlatformDownload that sends +// all responses to the callback function. +func PlatformDownloadStreamResponseToCallbackFunction(ctx context.Context, downloadCB rpc.DownloadProgressCB) rpc.ArduinoCoreService_PlatformDownloadServer { + return streamResponseToCallback(ctx, func(r *rpc.PlatformDownloadResponse) error { + if r.GetProgress() != nil { + downloadCB(r.GetProgress()) + } + return nil + }) +} + +// PlatformDownload downloads a platform package +func (s *arduinoCoreServerImpl) PlatformDownload(req *rpc.PlatformDownloadRequest, stream rpc.ArduinoCoreService_PlatformDownloadServer) error { + syncSend := NewSynchronizedSend(stream.Send) + pme, release, err := instances.GetPackageManagerExplorer(req.GetInstance()) if err != nil { - return nil, err + return err } defer release() - version, err := commands.ParseVersion(req.GetVersion()) + version, err := parseVersion(req.GetVersion()) if err != nil { - return nil, &cmderrors.InvalidVersionError{Cause: err} + return &cmderrors.InvalidVersionError{Cause: err} } ref := &packagemanager.PlatformReference{ @@ -48,18 +60,23 @@ func PlatformDownload(ctx context.Context, req *rpc.PlatformDownloadRequest, dow } platform, tools, err := pme.FindPlatformReleaseDependencies(ref) if err != nil { - return nil, &cmderrors.PlatformNotFoundError{Platform: ref.String(), Cause: err} + return &cmderrors.PlatformNotFoundError{Platform: ref.String(), Cause: err} } - if err := pme.DownloadPlatformRelease(platform, nil, downloadCB); err != nil { - return nil, err + downloadCB := func(p *rpc.DownloadProgress) { syncSend.Send(&rpc.PlatformDownloadResponse{Progress: p}) } + + // TODO: pass context + // ctx := stream.Context() + if err := pme.DownloadPlatformRelease(platform, downloadCB); err != nil { + return err } for _, tool := range tools { - if err := pme.DownloadToolRelease(tool, nil, downloadCB); err != nil { - return nil, err + // TODO: pass context + if err := pme.DownloadToolRelease(tool, downloadCB); err != nil { + return err } } - return &rpc.PlatformDownloadResponse{}, nil + return syncSend.Send(&rpc.PlatformDownloadResponse{}) } diff --git a/commands/core/install.go b/commands/service_platform_install.go similarity index 62% rename from commands/core/install.go rename to commands/service_platform_install.go index a568a9dcfba..caa351a6ec6 100644 --- a/commands/core/install.go +++ b/commands/service_platform_install.go @@ -13,21 +13,39 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package core +package commands import ( "context" "fmt" - "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/commands/cmderrors" "github.com/arduino/arduino-cli/commands/internal/instances" "github.com/arduino/arduino-cli/internal/arduino/cores/packagemanager" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" ) -// PlatformInstall FIXMEDOC -func PlatformInstall(ctx context.Context, req *rpc.PlatformInstallRequest, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) (*rpc.PlatformInstallResponse, error) { +// UpdateIndexStreamResponseToCallbackFunction returns a gRPC stream to be used in PlatformInstall that sends +// all responses to the callback function. +func PlatformInstallStreamResponseToCallbackFunction(ctx context.Context, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) rpc.ArduinoCoreService_PlatformInstallServer { + return streamResponseToCallback(ctx, func(r *rpc.PlatformInstallResponse) error { + if r.GetProgress() != nil { + downloadCB(r.GetProgress()) + } + if r.GetTaskProgress() != nil { + taskCB(r.GetTaskProgress()) + } + return nil + }) +} + +// PlatformInstall installs a platform package +func (s *arduinoCoreServerImpl) PlatformInstall(req *rpc.PlatformInstallRequest, stream rpc.ArduinoCoreService_PlatformInstallServer) error { + ctx := stream.Context() + syncSend := NewSynchronizedSend(stream.Send) + taskCB := func(p *rpc.TaskProgress) { syncSend.Send(&rpc.PlatformInstallResponse{TaskProgress: p}) } + downloadCB := func(p *rpc.DownloadProgress) { syncSend.Send(&rpc.PlatformInstallResponse{Progress: p}) } + install := func() error { pme, release, err := instances.GetPackageManagerExplorer(req.GetInstance()) if err != nil { @@ -35,7 +53,7 @@ func PlatformInstall(ctx context.Context, req *rpc.PlatformInstallRequest, downl } defer release() - version, err := commands.ParseVersion(req.GetVersion()) + version, err := parseVersion(req.GetVersion()) if err != nil { return &cmderrors.InvalidVersionError{Cause: err} } @@ -64,6 +82,7 @@ func PlatformInstall(ctx context.Context, req *rpc.PlatformInstallRequest, downl } } + // TODO: Pass context if err := pme.DownloadAndInstallPlatformAndTools(platformRelease, tools, downloadCB, taskCB, req.GetSkipPostInstall(), req.GetSkipPreUninstall()); err != nil { return err } @@ -72,10 +91,16 @@ func PlatformInstall(ctx context.Context, req *rpc.PlatformInstallRequest, downl } if err := install(); err != nil { - return nil, err + return err } - if err := commands.Init(&rpc.InitRequest{Instance: req.GetInstance()}, nil); err != nil { - return nil, err + + err := s.Init( + &rpc.InitRequest{Instance: req.GetInstance()}, + InitStreamResponseToCallbackFunction(ctx, nil), + ) + if err != nil { + return err } - return &rpc.PlatformInstallResponse{}, nil + + return syncSend.Send(&rpc.PlatformInstallResponse{}) } diff --git a/commands/core/search.go b/commands/service_platform_search.go similarity index 92% rename from commands/core/search.go rename to commands/service_platform_search.go index f68712c17e3..4117bf7be18 100644 --- a/commands/core/search.go +++ b/commands/service_platform_search.go @@ -13,14 +13,14 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package core +package commands import ( + "context" "regexp" "sort" "strings" - "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/commands/internal/instances" "github.com/arduino/arduino-cli/internal/arduino/cores" "github.com/arduino/arduino-cli/internal/arduino/utils" @@ -28,7 +28,7 @@ import ( ) // PlatformSearch FIXMEDOC -func PlatformSearch(req *rpc.PlatformSearchRequest) (*rpc.PlatformSearchResponse, error) { +func (s *arduinoCoreServerImpl) PlatformSearch(_ context.Context, req *rpc.PlatformSearchRequest) (*rpc.PlatformSearchResponse, error) { pme, release, err := instances.GetPackageManagerExplorer(req.GetInstance()) if err != nil { return nil, err @@ -82,7 +82,7 @@ func PlatformSearch(req *rpc.PlatformSearchRequest) (*rpc.PlatformSearchResponse out := []*rpc.PlatformSummary{} for _, platform := range res { rpcPlatformSummary := &rpc.PlatformSummary{ - Metadata: commands.PlatformToRPCPlatformMetadata(platform), + Metadata: platformToRPCPlatformMetadata(platform), Releases: map[string]*rpc.PlatformRelease{}, } if installed := pme.GetInstalledPlatformRelease(platform); installed != nil { @@ -92,7 +92,7 @@ func PlatformSearch(req *rpc.PlatformSearchRequest) (*rpc.PlatformSearchResponse rpcPlatformSummary.LatestVersion = latestCompatible.Version.String() } for _, platformRelease := range platform.GetAllReleases() { - rpcPlatformRelease := commands.PlatformReleaseToRPC(platformRelease) + rpcPlatformRelease := platformReleaseToRPC(platformRelease) rpcPlatformSummary.Releases[rpcPlatformRelease.GetVersion()] = rpcPlatformRelease } out = append(out, rpcPlatformSummary) diff --git a/commands/core/search_test.go b/commands/service_platform_search_test.go similarity index 87% rename from commands/core/search_test.go rename to commands/service_platform_search_test.go index e67c07c7e95..0e98b28fe59 100644 --- a/commands/core/search_test.go +++ b/commands/service_platform_search_test.go @@ -13,13 +13,12 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package core +package commands import ( + "context" "testing" - "github.com/arduino/arduino-cli/internal/cli/configuration" - "github.com/arduino/arduino-cli/internal/cli/instance" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/go-paths-helper" "github.com/stretchr/testify/require" @@ -33,16 +32,26 @@ func TestPlatformSearch(t *testing.T) { dataDir.MkdirAll() downloadDir.MkdirAll() defer paths.TempDir().Join("test").RemoveAll() - err := paths.New("testdata").Join("package_index.json").CopyTo(dataDir.Join("package_index.json")) + err := paths.New("testdata", "platform", "package_index.json").CopyTo(dataDir.Join("package_index.json")) require.Nil(t, err) - configuration.Settings = configuration.Init(paths.TempDir().Join("test", "arduino-cli.yaml").String()) + ctx := context.Background() + srv := NewArduinoCoreServer() - inst := instance.CreateAndInit() + _, err = srv.ConfigurationOpen(ctx, &rpc.ConfigurationOpenRequest{SettingsFormat: "yaml"}) + require.NoError(t, err) + + createResp, err := srv.Create(ctx, &rpc.CreateRequest{}) + require.NoError(t, err) + + inst := createResp.GetInstance() require.NotNil(t, inst) + err = srv.Init(&rpc.InitRequest{Instance: inst}, InitStreamResponseToCallbackFunction(ctx, nil)) + require.NoError(t, err) + t.Run("SearchAllVersions", func(t *testing.T) { - res, stat := PlatformSearch(&rpc.PlatformSearchRequest{ + res, stat := srv.PlatformSearch(ctx, &rpc.PlatformSearchRequest{ Instance: inst, SearchArgs: "retrokit", }) @@ -83,7 +92,7 @@ func TestPlatformSearch(t *testing.T) { }) t.Run("SearchThePackageMaintainer", func(t *testing.T) { - res, stat := PlatformSearch(&rpc.PlatformSearchRequest{ + res, stat := srv.PlatformSearch(ctx, &rpc.PlatformSearchRequest{ Instance: inst, SearchArgs: "Retrokits (www.retrokits.com)", }) @@ -123,7 +132,7 @@ func TestPlatformSearch(t *testing.T) { }) t.Run("SearchPackageName", func(t *testing.T) { - res, stat := PlatformSearch(&rpc.PlatformSearchRequest{ + res, stat := srv.PlatformSearch(ctx, &rpc.PlatformSearchRequest{ Instance: inst, SearchArgs: "Retrokits-RK002", }) @@ -163,7 +172,7 @@ func TestPlatformSearch(t *testing.T) { }) t.Run("SearchPlatformName", func(t *testing.T) { - res, stat := PlatformSearch(&rpc.PlatformSearchRequest{ + res, stat := srv.PlatformSearch(ctx, &rpc.PlatformSearchRequest{ Instance: inst, SearchArgs: "rk002", }) @@ -203,7 +212,7 @@ func TestPlatformSearch(t *testing.T) { }) t.Run("SearchBoardName", func(t *testing.T) { - res, stat := PlatformSearch(&rpc.PlatformSearchRequest{ + res, stat := srv.PlatformSearch(ctx, &rpc.PlatformSearchRequest{ Instance: inst, SearchArgs: "Yún", }) @@ -261,7 +270,7 @@ func TestPlatformSearch(t *testing.T) { }) t.Run("SearchBoardName2", func(t *testing.T) { - res, stat := PlatformSearch(&rpc.PlatformSearchRequest{ + res, stat := srv.PlatformSearch(ctx, &rpc.PlatformSearchRequest{ Instance: inst, SearchArgs: "yun", }) @@ -327,15 +336,23 @@ func TestPlatformSearchSorting(t *testing.T) { dataDir.MkdirAll() downloadDir.MkdirAll() defer paths.TempDir().Join("test").RemoveAll() - err := paths.New("testdata").Join("package_index.json").CopyTo(dataDir.Join("package_index.json")) + err := paths.New("testdata", "platform", "package_index.json").CopyTo(dataDir.Join("package_index.json")) require.Nil(t, err) - configuration.Settings = configuration.Init(paths.TempDir().Join("test", "arduino-cli.yaml").String()) + ctx := context.Background() + srv := NewArduinoCoreServer() + + _, err = srv.ConfigurationOpen(ctx, &rpc.ConfigurationOpenRequest{SettingsFormat: "yaml"}) + require.NoError(t, err) - inst := instance.CreateAndInit() + createResp, err := srv.Create(ctx, &rpc.CreateRequest{}) + require.NoError(t, err) + inst := createResp.GetInstance() require.NotNil(t, inst) + err = srv.Init(&rpc.InitRequest{Instance: inst}, InitStreamResponseToCallbackFunction(ctx, nil)) + require.NoError(t, err) - res, stat := PlatformSearch(&rpc.PlatformSearchRequest{ + res, stat := srv.PlatformSearch(ctx, &rpc.PlatformSearchRequest{ Instance: inst, SearchArgs: "", }) diff --git a/commands/core/uninstall.go b/commands/service_platform_uninstall.go similarity index 66% rename from commands/core/uninstall.go rename to commands/service_platform_uninstall.go index d1c826ad210..07a32988a6a 100644 --- a/commands/core/uninstall.go +++ b/commands/service_platform_uninstall.go @@ -13,31 +13,44 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package core +package commands import ( "context" - "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/commands/cmderrors" "github.com/arduino/arduino-cli/commands/internal/instances" "github.com/arduino/arduino-cli/internal/arduino/cores/packagemanager" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" ) -// PlatformUninstall FIXMEDOC -func PlatformUninstall(ctx context.Context, req *rpc.PlatformUninstallRequest, taskCB rpc.TaskProgressCB) (*rpc.PlatformUninstallResponse, error) { +// PlatformUninstallStreamResponseToCallbackFunction returns a gRPC stream to be used in PlatformUninstall that sends +// all responses to the callback function. +func PlatformUninstallStreamResponseToCallbackFunction(ctx context.Context, taskCB rpc.TaskProgressCB) rpc.ArduinoCoreService_PlatformUninstallServer { + return streamResponseToCallback(ctx, func(r *rpc.PlatformUninstallResponse) error { + if r.GetTaskProgress() != nil { + taskCB(r.GetTaskProgress()) + } + return nil + }) +} + +// PlatformUninstall uninstalls a platform package +func (s *arduinoCoreServerImpl) PlatformUninstall(req *rpc.PlatformUninstallRequest, stream rpc.ArduinoCoreService_PlatformUninstallServer) error { + syncSend := NewSynchronizedSend(stream.Send) + ctx := stream.Context() + taskCB := func(p *rpc.TaskProgress) { syncSend.Send(&rpc.PlatformUninstallResponse{TaskProgress: p}) } if err := platformUninstall(ctx, req, taskCB); err != nil { - return nil, err + return err } - if err := commands.Init(&rpc.InitRequest{Instance: req.GetInstance()}, nil); err != nil { - return nil, err + if err := s.Init(&rpc.InitRequest{Instance: req.GetInstance()}, InitStreamResponseToCallbackFunction(ctx, nil)); err != nil { + return err } - return &rpc.PlatformUninstallResponse{}, nil + return syncSend.Send(&rpc.PlatformUninstallResponse{}) } // platformUninstall is the implementation of platform unistaller -func platformUninstall(ctx context.Context, req *rpc.PlatformUninstallRequest, taskCB rpc.TaskProgressCB) error { +func platformUninstall(_ context.Context, req *rpc.PlatformUninstallRequest, taskCB rpc.TaskProgressCB) error { pme, release, err := instances.GetPackageManagerExplorer(req.GetInstance()) if err != nil { return &cmderrors.InvalidInstanceError{} @@ -65,6 +78,7 @@ func platformUninstall(ctx context.Context, req *rpc.PlatformUninstallRequest, t return &cmderrors.NotFoundError{Message: tr("Can't find dependencies for platform %s", ref), Cause: err} } + // TODO: pass context if err := pme.UninstallPlatform(platform, taskCB, req.GetSkipPreUninstall()); err != nil { return err } diff --git a/commands/core/upgrade.go b/commands/service_platform_upgrade.go similarity index 51% rename from commands/core/upgrade.go rename to commands/service_platform_upgrade.go index 57ba8de045e..2737eac4f37 100644 --- a/commands/core/upgrade.go +++ b/commands/service_platform_upgrade.go @@ -13,20 +13,45 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package core +package commands import ( "context" - "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/commands/internal/instances" "github.com/arduino/arduino-cli/internal/arduino/cores" "github.com/arduino/arduino-cli/internal/arduino/cores/packagemanager" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" ) -// PlatformUpgrade FIXMEDOC -func PlatformUpgrade(ctx context.Context, req *rpc.PlatformUpgradeRequest, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) (*rpc.PlatformUpgradeResponse, error) { +// PlatformUpgradeStreamResponseToCallbackFunction returns a gRPC stream to be used in PlatformUpgrade that sends +// all responses to the callback function. +func PlatformUpgradeStreamResponseToCallbackFunction(ctx context.Context, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) (rpc.ArduinoCoreService_PlatformUpgradeServer, func() *rpc.Platform) { + var resp *rpc.Platform + return streamResponseToCallback(ctx, func(r *rpc.PlatformUpgradeResponse) error { + // TODO: use oneof in protoc files? + if r.GetProgress() != nil { + downloadCB(r.GetProgress()) + } + if r.GetTaskProgress() != nil { + taskCB(r.GetTaskProgress()) + } + if r.GetPlatform() != nil { + resp = r.GetPlatform() + } + return nil + }), func() *rpc.Platform { + return resp + } +} + +// PlatformUpgrade upgrades a platform package +func (s *arduinoCoreServerImpl) PlatformUpgrade(req *rpc.PlatformUpgradeRequest, stream rpc.ArduinoCoreService_PlatformUpgradeServer) error { + syncSend := NewSynchronizedSend(stream.Send) + ctx := stream.Context() + downloadCB := func(p *rpc.DownloadProgress) { syncSend.Send(&rpc.PlatformUpgradeResponse{Progress: p}) } + taskCB := func(p *rpc.TaskProgress) { syncSend.Send(&rpc.PlatformUpgradeResponse{TaskProgress: p}) } + upgrade := func() (*cores.PlatformRelease, error) { pme, release, err := instances.GetPackageManagerExplorer(req.GetInstance()) if err != nil { @@ -47,20 +72,22 @@ func PlatformUpgrade(ctx context.Context, req *rpc.PlatformUpgradeRequest, downl return platform, nil } - var rpcPlatform *rpc.Platform platformRelease, err := upgrade() if platformRelease != nil { - rpcPlatform = &rpc.Platform{ - Metadata: commands.PlatformToRPCPlatformMetadata(platformRelease.Platform), - Release: commands.PlatformReleaseToRPC(platformRelease), - } + syncSend.Send(&rpc.PlatformUpgradeResponse{ + Platform: &rpc.Platform{ + Metadata: platformToRPCPlatformMetadata(platformRelease.Platform), + Release: platformReleaseToRPC(platformRelease), + }, + }) } if err != nil { - return &rpc.PlatformUpgradeResponse{Platform: rpcPlatform}, err + return err } - if err := commands.Init(&rpc.InitRequest{Instance: req.GetInstance()}, nil); err != nil { - return nil, err + + if err := s.Init(&rpc.InitRequest{Instance: req.GetInstance()}, InitStreamResponseToCallbackFunction(ctx, nil)); err != nil { + return err } - return &rpc.PlatformUpgradeResponse{Platform: rpcPlatform}, nil + return nil } diff --git a/commands/sketch/set_defaults.go b/commands/service_set_sketch_defaults.go similarity index 93% rename from commands/sketch/set_defaults.go rename to commands/service_set_sketch_defaults.go index c53bc1b1ae5..6b3ba044899 100644 --- a/commands/sketch/set_defaults.go +++ b/commands/service_set_sketch_defaults.go @@ -13,7 +13,7 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package sketch +package commands import ( "context" @@ -26,7 +26,7 @@ import ( // SetSketchDefaults updates the sketch project file (sketch.yaml) with the given defaults // for the values `default_fqbn`, `default_port`, and `default_protocol`. -func SetSketchDefaults(ctx context.Context, req *rpc.SetSketchDefaultsRequest) (*rpc.SetSketchDefaultsResponse, error) { +func (s *arduinoCoreServerImpl) SetSketchDefaults(ctx context.Context, req *rpc.SetSketchDefaultsRequest) (*rpc.SetSketchDefaultsResponse, error) { sk, err := sketch.New(paths.New(req.GetSketchPath())) if err != nil { return nil, &cmderrors.CantOpenSketchError{Cause: err} diff --git a/commands/service_settings.go b/commands/service_settings.go new file mode 100644 index 00000000000..d38f4a95cfd --- /dev/null +++ b/commands/service_settings.go @@ -0,0 +1,229 @@ +// 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 ( + "context" + "encoding/json" + "fmt" + "reflect" + + "github.com/arduino/arduino-cli/commands/cmderrors" + f "github.com/arduino/arduino-cli/internal/algorithms" + "github.com/arduino/arduino-cli/internal/cli/configuration" + "github.com/arduino/arduino-cli/internal/go-configmap" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" + "google.golang.org/protobuf/proto" + "gopkg.in/yaml.v3" +) + +// SettingsGetValue returns a settings value given its key. If the key is not present +// an error will be returned, so that we distinguish empty settings from missing +// ones. +func (s *arduinoCoreServerImpl) ConfigurationGet(ctx context.Context, req *rpc.ConfigurationGetRequest) (*rpc.ConfigurationGetResponse, error) { + conf := &rpc.Configuration{ + Directories: &rpc.Configuration_Directories{ + Builtin: &rpc.Configuration_Directories_Builtin{}, + Data: s.settings.DataDir().String(), + Downloads: s.settings.DownloadsDir().String(), + User: s.settings.UserDir().String(), + }, + Network: &rpc.Configuration_Network{}, + Sketch: &rpc.Configuration_Sketch{ + AlwaysExportBinaries: s.settings.SketchAlwaysExportBinaries(), + }, + BuildCache: &rpc.Configuration_BuildCache{ + CompilationsBeforePurge: uint64(s.settings.GetCompilationsBeforeBuildCachePurge()), + TtlSecs: uint64(s.settings.GetBuildCacheTTL().Seconds()), + }, + BoardManager: &rpc.Configuration_BoardManager{ + AdditionalUrls: s.settings.BoardManagerAdditionalUrls(), + }, + Daemon: &rpc.Configuration_Daemon{ + Port: s.settings.DaemonPort(), + }, + Output: &rpc.Configuration_Output{ + NoColor: s.settings.NoColor(), + }, + Logging: &rpc.Configuration_Logging{ + Level: s.settings.LoggingLevel(), + Format: s.settings.LoggingFormat(), + }, + Library: &rpc.Configuration_Library{ + EnableUnsafeInstall: s.settings.LibraryEnableUnsafeInstall(), + }, + Updater: &rpc.Configuration_Updater{ + EnableNotification: s.settings.UpdaterEnableNotification(), + }, + } + + if builtinLibs := s.settings.IDEBuiltinLibrariesDir(); builtinLibs != nil { + conf.Directories.Builtin.Libraries = proto.String(builtinLibs.String()) + } + + if ua := s.settings.ExtraUserAgent(); ua != "" { + conf.Network.ExtraUserAgent = &ua + } + if proxy, err := s.settings.NetworkProxy(); err == nil && proxy != nil { + conf.Network.Proxy = proto.String(proxy.String()) + } + + if logFile := s.settings.LoggingFile(); logFile != nil { + file := logFile.String() + conf.Logging.File = &file + } + + if locale := s.settings.Locale(); locale != "" { + conf.Locale = &locale + } + + return &rpc.ConfigurationGetResponse{Configuration: conf}, nil +} + +func (s *arduinoCoreServerImpl) SettingsSetValue(ctx context.Context, req *rpc.SettingsSetValueRequest) (*rpc.SettingsSetValueResponse, error) { + // Determine the existence and the kind of the value + key := req.GetKey() + + // Extract the value from the request + encodedValue := []byte(req.GetEncodedValue()) + if len(encodedValue) == 0 { + // If the value is empty, unset the key + s.settings.Delete(key) + return &rpc.SettingsSetValueResponse{}, nil + } + + var newValue any + switch req.GetValueFormat() { + case "", "json": + if err := json.Unmarshal(encodedValue, &newValue); err != nil { + return nil, &cmderrors.InvalidArgumentError{Message: fmt.Sprintf("invalid value: %v", err)} + } + case "yaml": + if err := yaml.Unmarshal(encodedValue, &newValue); err != nil { + return nil, &cmderrors.InvalidArgumentError{Message: fmt.Sprintf("invalid value: %v", err)} + } + case "cli": + err := s.settings.SetFromCLIArgs(key, req.GetEncodedValue()) + if err != nil { + return nil, err + } + return &rpc.SettingsSetValueResponse{}, nil + default: + return nil, &cmderrors.InvalidArgumentError{Message: fmt.Sprintf("unsupported value format: %s", req.ValueFormat)} + } + + // If the value is "null", unset the key + if reflect.TypeOf(newValue) == reflect.TypeOf(nil) { + s.settings.Delete(key) + return &rpc.SettingsSetValueResponse{}, nil + } + + // Set the value + if err := s.settings.Set(key, newValue); err != nil { + return nil, err + } + + return &rpc.SettingsSetValueResponse{}, nil +} + +func (s *arduinoCoreServerImpl) SettingsGetValue(ctx context.Context, req *rpc.SettingsGetValueRequest) (*rpc.SettingsGetValueResponse, error) { + key := req.GetKey() + value, ok := s.settings.GetOk(key) + if !ok { + value, ok = s.settings.Defaults.GetOk(key) + } + if !ok { + return nil, &cmderrors.InvalidArgumentError{Message: fmt.Sprintf("key %s not found", key)} + } + + switch req.GetValueFormat() { + case "", "json": + valueJson, err := json.Marshal(value) + if err != nil { + return nil, fmt.Errorf("error marshalling value: %v", err) + } + return &rpc.SettingsGetValueResponse{EncodedValue: string(valueJson)}, nil + case "yaml": + valueYaml, err := yaml.Marshal(value) + if err != nil { + return nil, fmt.Errorf("error marshalling value: %v", err) + } + return &rpc.SettingsGetValueResponse{EncodedValue: string(valueYaml)}, nil + default: + return nil, &cmderrors.InvalidArgumentError{Message: fmt.Sprintf("unsupported value format: %s", req.ValueFormat)} + } +} + +// ConfigurationSave encodes the current configuration in the specified format +func (s *arduinoCoreServerImpl) ConfigurationSave(ctx context.Context, req *rpc.ConfigurationSaveRequest) (*rpc.ConfigurationSaveResponse, error) { + switch req.GetSettingsFormat() { + case "yaml": + data, err := yaml.Marshal(s.settings) + if err != nil { + return nil, fmt.Errorf("error marshalling settings: %v", err) + } + return &rpc.ConfigurationSaveResponse{EncodedSettings: string(data)}, nil + case "json": + data, err := json.MarshalIndent(s.settings, "", " ") + if err != nil { + return nil, fmt.Errorf("error marshalling settings: %v", err) + } + return &rpc.ConfigurationSaveResponse{EncodedSettings: string(data)}, nil + default: + return nil, &cmderrors.InvalidArgumentError{Message: fmt.Sprintf("unsupported format: %s", req.GetSettingsFormat())} + } +} + +// SettingsReadFromFile read settings from a YAML file and replace the settings currently stored in memory. +func (s *arduinoCoreServerImpl) ConfigurationOpen(ctx context.Context, req *rpc.ConfigurationOpenRequest) (*rpc.ConfigurationOpenResponse, error) { + warnings := []string{} + + switch req.GetSettingsFormat() { + case "yaml": + err := yaml.Unmarshal([]byte(req.GetEncodedSettings()), s.settings) + if errs, ok := err.(*configmap.UnmarshalErrors); ok { + warnings = f.Map(errs.WrappedErrors(), (error).Error) + } else if err != nil { + return nil, fmt.Errorf("error unmarshalling settings: %v", err) + } + case "json": + err := json.Unmarshal([]byte(req.GetEncodedSettings()), s.settings) + if errs, ok := err.(*configmap.UnmarshalErrors); ok { + warnings = f.Map(errs.WrappedErrors(), (error).Error) + } else if err != nil { + return nil, fmt.Errorf("error unmarshalling settings: %v", err) + } + default: + return nil, &cmderrors.InvalidArgumentError{Message: fmt.Sprintf("unsupported format: %s", req.GetSettingsFormat())} + } + + configuration.InjectEnvVars(s.settings) + return &rpc.ConfigurationOpenResponse{Warnings: warnings}, nil +} + +// SettingsEnumerate returns the list of all the settings keys. +func (s *arduinoCoreServerImpl) SettingsEnumerate(ctx context.Context, req *rpc.SettingsEnumerateRequest) (*rpc.SettingsEnumerateResponse, error) { + var entries []*rpc.SettingsEnumerateResponse_Entry + for k, t := range s.settings.Defaults.Schema() { + entries = append(entries, &rpc.SettingsEnumerateResponse_Entry{ + Key: k, + Type: t.String(), + }) + } + return &rpc.SettingsEnumerateResponse{ + Entries: entries, + }, nil +} diff --git a/commands/service_settings_test.go b/commands/service_settings_test.go new file mode 100644 index 00000000000..1c77856a336 --- /dev/null +++ b/commands/service_settings_test.go @@ -0,0 +1,236 @@ +// 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 ( + "context" + "encoding/json" + "testing" + + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" + "github.com/arduino/go-paths-helper" + "github.com/stretchr/testify/require" +) + +func loadConfig(t *testing.T, srv rpc.ArduinoCoreServiceServer, confPath *paths.Path) { + confPath.ToAbs() + conf, err := confPath.ReadFile() + require.NoError(t, err) + _, err = srv.ConfigurationOpen(context.Background(), &rpc.ConfigurationOpenRequest{ + EncodedSettings: string(conf), + SettingsFormat: "yaml", + }) + require.NoError(t, err) +} + +func TestGetAll(t *testing.T) { + srv := NewArduinoCoreServer() + loadConfig(t, srv, paths.New("testdata", "arduino-cli.yml")) + resp, err := srv.ConfigurationGet(context.Background(), &rpc.ConfigurationGetRequest{}) + require.Nil(t, err) + + defaultUserDir, err := srv.SettingsGetValue(context.Background(), &rpc.SettingsGetValueRequest{Key: "directories.user"}) + require.NoError(t, err) + + content, err := json.Marshal(resp.GetConfiguration()) + require.Nil(t, err) + require.JSONEq(t, `{ + "board_manager": { + "additional_urls": [ "http://foobar.com", "http://example.com" ] + }, + "build_cache": { + "compilations_before_purge": 10, + "ttl_secs": 2592000 + }, + "directories": { + "builtin": {}, + "data": "/home/massi/.arduino15", + "downloads": "/home/massi/.arduino15/staging", + "user": `+defaultUserDir.GetEncodedValue()+` + }, + "library": {}, + "locale": "en", + "logging": { + "format": "text", + "level": "info" + }, + "daemon":{ + "port":"50051" + }, + "network":{ + "proxy":"123" + }, + "output": {}, + "sketch": {}, + "updater": {} + }`, string(content)) +} + +func TestMerge(t *testing.T) { + srv := NewArduinoCoreServer() + loadConfig(t, srv, paths.New("testdata", "arduino-cli.yml")) + + ctx := context.Background() + + get := func(key string) string { + resp, err := srv.SettingsGetValue(ctx, &rpc.SettingsGetValueRequest{Key: key}) + if err != nil { + return "" + } + return resp.GetEncodedValue() + } + + // Verify defaults + require.Equal(t, `"50051"`, get("daemon.port")) + require.Equal(t, "", get("foo")) + require.Equal(t, "false", get("sketch.always_export_binaries")) + + bulkSettings := `{"foo": "bar", "daemon":{"port":"420"}, "sketch": {"always_export_binaries": "true"}}` + _, err := srv.ConfigurationOpen(ctx, &rpc.ConfigurationOpenRequest{EncodedSettings: bulkSettings, SettingsFormat: "json"}) + require.Error(t, err) + + bulkSettings = `{"daemon":{"port":"420"}, "sketch": {"always_export_binaries": "true"}}` + _, err = srv.ConfigurationOpen(ctx, &rpc.ConfigurationOpenRequest{EncodedSettings: bulkSettings, SettingsFormat: "json"}) + require.Error(t, err) + + bulkSettings = `{"daemon":{"port":"420"}, "sketch": {"always_export_binaries": true}}` + _, err = srv.ConfigurationOpen(ctx, &rpc.ConfigurationOpenRequest{EncodedSettings: bulkSettings, SettingsFormat: "json"}) + require.NoError(t, err) + + require.Equal(t, `"420"`, get("daemon.port")) + require.Equal(t, ``, get("foo")) + require.Equal(t, "true", get("sketch.always_export_binaries")) + + bulkSettings = `{"daemon": {}, "sketch": {"always_export_binaries": false}}` + _, err = srv.ConfigurationOpen(ctx, &rpc.ConfigurationOpenRequest{EncodedSettings: bulkSettings, SettingsFormat: "json"}) + require.NoError(t, err) + + require.Equal(t, `"50051"`, get("daemon.port")) + require.Equal(t, "", get("foo")) + require.Equal(t, "false", get("sketch.always_export_binaries")) + + _, err = srv.SettingsSetValue(ctx, &rpc.SettingsSetValueRequest{Key: "daemon.port", EncodedValue: ""}) + require.NoError(t, err) + + require.Equal(t, `"50051"`, get("daemon.port")) + // Verifies other values are not changed + require.Equal(t, "", get("foo")) + require.Equal(t, "false", get("sketch.always_export_binaries")) + +} + +func TestGetValue(t *testing.T) { + srv := NewArduinoCoreServer() + loadConfig(t, srv, paths.New("testdata", "arduino-cli.yml")) + + key := &rpc.SettingsGetValueRequest{Key: "daemon"} + resp, err := srv.SettingsGetValue(context.Background(), key) + require.NoError(t, err) + require.Equal(t, `{"port":"50051"}`, resp.GetEncodedValue()) + + key = &rpc.SettingsGetValueRequest{Key: "daemon.port"} + resp, err = srv.SettingsGetValue(context.Background(), key) + require.NoError(t, err) + require.Equal(t, `"50051"`, resp.GetEncodedValue()) +} + +func TestGetOfSettedValue(t *testing.T) { + srv := NewArduinoCoreServer() + loadConfig(t, srv, paths.New("testdata", "arduino-cli.yml")) + + // Verifies value is not set (try with a key without a default, like "directories.builtin.libraries") + ctx := context.Background() + res, err := srv.SettingsGetValue(ctx, &rpc.SettingsGetValueRequest{Key: "directories.builtin.libraries"}) + require.Nil(t, res) + require.Error(t, err, "Error getting settings value") + + // Set value + _, err = srv.SettingsSetValue(ctx, &rpc.SettingsSetValueRequest{ + Key: "directories.builtin.libraries", + EncodedValue: `"bar"`}) + require.NoError(t, err) + + // Verifies value is correctly returned + res, err = srv.SettingsGetValue(ctx, &rpc.SettingsGetValueRequest{Key: "directories.builtin.libraries"}) + require.NoError(t, err) + require.Equal(t, `"bar"`, res.GetEncodedValue()) +} + +func TestGetValueNotFound(t *testing.T) { + srv := NewArduinoCoreServer() + loadConfig(t, srv, paths.New("testdata", "arduino-cli.yml")) + + key := &rpc.SettingsGetValueRequest{Key: "DOESNTEXIST"} + _, err := srv.SettingsGetValue(context.Background(), key) + require.Error(t, err) +} + +func TestWrite(t *testing.T) { + srv := NewArduinoCoreServer() + loadConfig(t, srv, paths.New("testdata", "arduino-cli.yml")) + + // Writes some settings + val := &rpc.SettingsSetValueRequest{ + Key: "directories.builtin.libraries", + EncodedValue: `"bar"`, + } + _, err := srv.SettingsSetValue(context.Background(), val) + require.NoError(t, err) + + resp, err := srv.ConfigurationSave(context.Background(), &rpc.ConfigurationSaveRequest{ + SettingsFormat: "yaml", + }) + require.NoError(t, err) + + // Verify encoded content + require.YAMLEq(t, ` +board_manager: + additional_urls: + - http://foobar.com + - http://example.com + +daemon: + port: "50051" + +directories: + data: /home/massi/.arduino15 + downloads: /home/massi/.arduino15/staging + builtin: + libraries: bar + +logging: + file: "" + format: text + level: info + +network: + proxy: "123" +`, resp.GetEncodedSettings()) +} + +func TestDelete(t *testing.T) { + srv := NewArduinoCoreServer() + loadConfig(t, srv, paths.New("testdata", "arduino-cli.yml")) + + _, err := srv.SettingsGetValue(context.Background(), &rpc.SettingsGetValueRequest{Key: "network"}) + require.NoError(t, err) + + _, err = srv.SettingsSetValue(context.Background(), &rpc.SettingsSetValueRequest{Key: "network", EncodedValue: ""}) + require.NoError(t, err) + + _, err = srv.SettingsGetValue(context.Background(), &rpc.SettingsGetValueRequest{Key: "network"}) + require.Error(t, err) +} diff --git a/commands/sketch/archive.go b/commands/service_sketch_archive.go similarity index 93% rename from commands/sketch/archive.go rename to commands/service_sketch_archive.go index 8f843dbca6b..67515406382 100644 --- a/commands/sketch/archive.go +++ b/commands/service_sketch_archive.go @@ -13,7 +13,7 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package sketch +package commands import ( "archive/zip" @@ -24,15 +24,12 @@ import ( "github.com/arduino/arduino-cli/commands/cmderrors" "github.com/arduino/arduino-cli/internal/arduino/sketch" - "github.com/arduino/arduino-cli/internal/i18n" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" paths "github.com/arduino/go-paths-helper" ) -var tr = i18n.Tr - // ArchiveSketch FIXMEDOC -func ArchiveSketch(ctx context.Context, req *rpc.ArchiveSketchRequest) (*rpc.ArchiveSketchResponse, error) { +func (s *arduinoCoreServerImpl) ArchiveSketch(ctx context.Context, req *rpc.ArchiveSketchRequest) (*rpc.ArchiveSketchResponse, error) { // sketchName is the name of the sketch without extension, for example "MySketch" var sketchName string @@ -41,13 +38,13 @@ func ArchiveSketch(ctx context.Context, req *rpc.ArchiveSketchRequest) (*rpc.Arc sketchPath = paths.New(".") } - s, err := sketch.New(sketchPath) + sk, err := sketch.New(sketchPath) if err != nil { return nil, &cmderrors.CantOpenSketchError{Cause: err} } - sketchPath = s.FullPath - sketchName = s.Name + sketchPath = sk.FullPath + sketchName = sk.Name archivePath := paths.New(req.GetArchivePath()) if archivePath == nil { diff --git a/commands/sketch/load.go b/commands/service_sketch_load.go similarity index 85% rename from commands/sketch/load.go rename to commands/service_sketch_load.go index 5dadb0113c4..8360ce7adfd 100644 --- a/commands/sketch/load.go +++ b/commands/service_sketch_load.go @@ -13,7 +13,7 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package sketch +package commands import ( "context" @@ -25,10 +25,10 @@ import ( ) // LoadSketch collects and returns all information about a sketch -func LoadSketch(ctx context.Context, req *rpc.LoadSketchRequest) (*rpc.Sketch, error) { +func (s *arduinoCoreServerImpl) LoadSketch(ctx context.Context, req *rpc.LoadSketchRequest) (*rpc.LoadSketchResponse, error) { sk, err := sketch.New(paths.New(req.GetSketchPath())) if err != nil { return nil, &cmderrors.CantOpenSketchError{Cause: err} } - return sk.ToRpc(), nil + return &rpc.LoadSketchResponse{Sketch: sk.ToRpc()}, nil } diff --git a/commands/sketch/load_test.go b/commands/service_sketch_load_test.go similarity index 78% rename from commands/sketch/load_test.go rename to commands/service_sketch_load_test.go index bfae0e6a959..d7dfcdcf8fa 100644 --- a/commands/sketch/load_test.go +++ b/commands/service_sketch_load_test.go @@ -13,7 +13,7 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package sketch +package commands import ( "context" @@ -24,10 +24,11 @@ import ( ) func TestLoadSketchProfiles(t *testing.T) { - loadResp, err := LoadSketch(context.Background(), &commands.LoadSketchRequest{ + srv := NewArduinoCoreServer() + loadResp, err := srv.LoadSketch(context.Background(), &commands.LoadSketchRequest{ SketchPath: "./testdata/sketch_with_profile", }) require.NoError(t, err) - require.Len(t, loadResp.GetProfiles(), 2) - require.Equal(t, loadResp.GetDefaultProfile().GetName(), "nanorp") + require.Len(t, loadResp.GetSketch().GetProfiles(), 2) + require.Equal(t, loadResp.GetSketch().GetDefaultProfile().GetName(), "nanorp") } diff --git a/commands/sketch/new.go b/commands/service_sketch_new.go similarity index 93% rename from commands/sketch/new.go rename to commands/service_sketch_new.go index 5a7f6003e29..b0c9c079f73 100644 --- a/commands/sketch/new.go +++ b/commands/service_sketch_new.go @@ -13,7 +13,7 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package sketch +package commands import ( "context" @@ -22,7 +22,6 @@ import ( "github.com/arduino/arduino-cli/commands/cmderrors" "github.com/arduino/arduino-cli/internal/arduino/globals" - "github.com/arduino/arduino-cli/internal/cli/configuration" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" paths "github.com/arduino/go-paths-helper" ) @@ -43,12 +42,12 @@ var invalidNames = []string{"CON", "PRN", "AUX", "NUL", "COM0", "COM1", "COM2", "COM6", "COM7", "COM8", "COM9", "LPT0", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"} // NewSketch creates a new sketch via gRPC -func NewSketch(ctx context.Context, req *rpc.NewSketchRequest) (*rpc.NewSketchResponse, error) { +func (s *arduinoCoreServerImpl) NewSketch(ctx context.Context, req *rpc.NewSketchRequest) (*rpc.NewSketchResponse, error) { var sketchesDir string if len(req.GetSketchDir()) > 0 { sketchesDir = req.GetSketchDir() } else { - sketchesDir = configuration.Settings.GetString("directories.User") + sketchesDir = s.settings.GetString("directories.User") } if err := validateSketchName(req.GetSketchName()); err != nil { diff --git a/commands/sketch/new_test.go b/commands/service_sketch_new_test.go similarity index 82% rename from commands/sketch/new_test.go rename to commands/service_sketch_new_test.go index 00ae7856732..8dfb5a8168e 100644 --- a/commands/sketch/new_test.go +++ b/commands/service_sketch_new_test.go @@ -13,14 +13,14 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package sketch +package commands import ( "context" "fmt" "testing" - "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/stretchr/testify/require" ) @@ -34,8 +34,10 @@ func Test_SketchNameWrongPattern(t *testing.T) { "||||||||||||||", ",`hack[}attempt{];", } + + srv := NewArduinoCoreServer() for _, name := range invalidNames { - _, err := NewSketch(context.Background(), &commands.NewSketchRequest{ + _, err := srv.NewSketch(context.Background(), &rpc.NewSketchRequest{ SketchName: name, SketchDir: t.TempDir(), }) @@ -46,9 +48,9 @@ func Test_SketchNameWrongPattern(t *testing.T) { } func Test_SketchNameEmpty(t *testing.T) { - emptyName := "" - _, err := NewSketch(context.Background(), &commands.NewSketchRequest{ - SketchName: emptyName, + srv := NewArduinoCoreServer() + _, err := srv.NewSketch(context.Background(), &rpc.NewSketchRequest{ + SketchName: "", SketchDir: t.TempDir(), }) @@ -60,7 +62,8 @@ func Test_SketchNameTooLong(t *testing.T) { for i := range tooLongName { tooLongName[i] = 'a' } - _, err := NewSketch(context.Background(), &commands.NewSketchRequest{ + srv := NewArduinoCoreServer() + _, err := srv.NewSketch(context.Background(), &rpc.NewSketchRequest{ SketchName: string(tooLongName), SketchDir: t.TempDir(), }) @@ -83,8 +86,9 @@ func Test_SketchNameOk(t *testing.T) { "_hello_world", string(lengthLimitName), } + srv := NewArduinoCoreServer() for _, name := range validNames { - _, err := NewSketch(context.Background(), &commands.NewSketchRequest{ + _, err := srv.NewSketch(context.Background(), &rpc.NewSketchRequest{ SketchName: name, SketchDir: t.TempDir(), }) @@ -95,8 +99,9 @@ func Test_SketchNameOk(t *testing.T) { func Test_SketchNameReserved(t *testing.T) { invalidNames := []string{"CON", "PRN", "AUX", "NUL", "COM0", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT0", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"} + srv := NewArduinoCoreServer() for _, name := range invalidNames { - _, err := NewSketch(context.Background(), &commands.NewSketchRequest{ + _, err := srv.NewSketch(context.Background(), &rpc.NewSketchRequest{ SketchName: name, SketchDir: t.TempDir(), }) diff --git a/commands/daemon/stream.go b/commands/service_stream_utility.go similarity index 99% rename from commands/daemon/stream.go rename to commands/service_stream_utility.go index aa5e70fb770..4b285c443fc 100644 --- a/commands/daemon/stream.go +++ b/commands/service_stream_utility.go @@ -13,7 +13,7 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package daemon +package commands import ( "errors" diff --git a/commands/upload/upload.go b/commands/service_upload.go similarity index 90% rename from commands/upload/upload.go rename to commands/service_upload.go index fcc248ce3b9..6dcacf1d3e5 100644 --- a/commands/upload/upload.go +++ b/commands/service_upload.go @@ -13,7 +13,7 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package upload +package commands import ( "context" @@ -31,7 +31,6 @@ import ( "github.com/arduino/arduino-cli/internal/arduino/cores/packagemanager" "github.com/arduino/arduino-cli/internal/arduino/globals" "github.com/arduino/arduino-cli/internal/arduino/sketch" - "github.com/arduino/arduino-cli/internal/i18n" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" paths "github.com/arduino/go-paths-helper" properties "github.com/arduino/go-properties-orderedmap" @@ -40,11 +39,9 @@ import ( "github.com/sirupsen/logrus" ) -var tr = i18n.Tr - // SupportedUserFields returns a SupportedUserFieldsResponse containing all the UserFields supported // by the upload tools needed by the board using the protocol specified in SupportedUserFieldsRequest. -func SupportedUserFields(ctx context.Context, req *rpc.SupportedUserFieldsRequest) (*rpc.SupportedUserFieldsResponse, error) { +func (s *arduinoCoreServerImpl) SupportedUserFields(ctx context.Context, req *rpc.SupportedUserFieldsRequest) (*rpc.SupportedUserFieldsResponse, error) { if req.GetProtocol() == "" { return nil, &cmderrors.MissingPortProtocolError{} } @@ -123,8 +120,33 @@ func getUserFields(toolID string, platformRelease *cores.PlatformRelease) []*rpc return userFields } -// Upload FIXMEDOC -func Upload(ctx context.Context, req *rpc.UploadRequest, outStream io.Writer, errStream io.Writer) (*rpc.UploadResult, error) { +// UploadToServerStreams return a server stream that forwards the output and error streams to the provided writers. +// It also returns a function that can be used to retrieve the result of the upload. +func UploadToServerStreams(ctx context.Context, outStream io.Writer, errStream io.Writer) (rpc.ArduinoCoreService_UploadServer, func() *rpc.UploadResult) { + var result *rpc.UploadResult + stream := streamResponseToCallback(ctx, func(resp *rpc.UploadResponse) error { + if errData := resp.GetErrStream(); len(errData) > 0 { + _, err := errStream.Write(errData) + return err + } + if outData := resp.GetOutStream(); len(outData) > 0 { + _, err := outStream.Write(outData) + return err + } + if res := resp.GetResult(); res != nil { + result = res + } + return nil + }) + return stream, func() *rpc.UploadResult { + return result + } +} + +// Upload performs the upload of a sketch to a board. +func (s *arduinoCoreServerImpl) Upload(req *rpc.UploadRequest, stream rpc.ArduinoCoreService_UploadServer) error { + syncSend := NewSynchronizedSend(stream.Send) + logrus.Tracef("Upload %s on %s started", req.GetSketchPath(), req.GetFqbn()) // TODO: make a generic function to extract sketch from request @@ -132,12 +154,12 @@ func Upload(ctx context.Context, req *rpc.UploadRequest, outStream io.Writer, er sketchPath := paths.New(req.GetSketchPath()) sk, err := sketch.New(sketchPath) if err != nil && req.GetImportDir() == "" && req.GetImportFile() == "" { - return nil, &cmderrors.CantOpenSketchError{Cause: err} + return &cmderrors.CantOpenSketchError{Cause: err} } pme, pmeRelease, err := instances.GetPackageManagerExplorer(req.GetInstance()) if err != nil { - return nil, err + return err } defer pmeRelease() @@ -154,7 +176,20 @@ func Upload(ctx context.Context, req *rpc.UploadRequest, outStream io.Writer, er programmer = sk.GetDefaultProgrammer() } + outStream := feedStreamTo(func(data []byte) { + syncSend.Send(&rpc.UploadResponse{ + Message: &rpc.UploadResponse_OutStream{OutStream: data}, + }) + }) + defer outStream.Close() + errStream := feedStreamTo(func(data []byte) { + syncSend.Send(&rpc.UploadResponse{ + Message: &rpc.UploadResponse_ErrStream{ErrStream: data}, + }) + }) + defer errStream.Close() updatedPort, err := runProgramAction( + stream.Context(), pme, sk, req.GetImportFile(), @@ -171,22 +206,45 @@ func Upload(ctx context.Context, req *rpc.UploadRequest, outStream io.Writer, er req.GetUserFields(), ) if err != nil { - return nil, err + return err } - - return &rpc.UploadResult{ - UpdatedUploadPort: updatedPort, - }, nil + return syncSend.Send(&rpc.UploadResponse{ + Message: &rpc.UploadResponse_Result{ + Result: &rpc.UploadResult{ + UpdatedUploadPort: updatedPort, + }, + }, + }) } -// UsingProgrammer FIXMEDOC -func UsingProgrammer(ctx context.Context, req *rpc.UploadUsingProgrammerRequest, outStream io.Writer, errStream io.Writer) error { +// UploadUsingProgrammer FIXMEDOC +func (s *arduinoCoreServerImpl) UploadUsingProgrammer(req *rpc.UploadUsingProgrammerRequest, stream rpc.ArduinoCoreService_UploadUsingProgrammerServer) error { + syncSend := NewSynchronizedSend(stream.Send) + streamAdapter := streamResponseToCallback(stream.Context(), func(resp *rpc.UploadResponse) error { + if errData := resp.GetErrStream(); len(errData) > 0 { + syncSend.Send(&rpc.UploadUsingProgrammerResponse{ + Message: &rpc.UploadUsingProgrammerResponse_ErrStream{ + ErrStream: errData, + }, + }) + } + if outData := resp.GetOutStream(); len(outData) > 0 { + syncSend.Send(&rpc.UploadUsingProgrammerResponse{ + Message: &rpc.UploadUsingProgrammerResponse_OutStream{ + OutStream: outData, + }, + }) + } + // resp.GetResult() is ignored + return nil + }) + logrus.Tracef("Upload using programmer %s on %s started", req.GetSketchPath(), req.GetFqbn()) if req.GetProgrammer() == "" { return &cmderrors.MissingProgrammerError{} } - _, err := Upload(ctx, &rpc.UploadRequest{ + return s.Upload(&rpc.UploadRequest{ Instance: req.GetInstance(), SketchPath: req.GetSketchPath(), ImportFile: req.GetImportFile(), @@ -197,11 +255,10 @@ func UsingProgrammer(ctx context.Context, req *rpc.UploadUsingProgrammerRequest, Verbose: req.GetVerbose(), Verify: req.GetVerify(), UserFields: req.GetUserFields(), - }, outStream, errStream) - return err + }, streamAdapter) } -func runProgramAction(pme *packagemanager.Explorer, +func runProgramAction(ctx context.Context, pme *packagemanager.Explorer, sk *sketch.Sketch, importFile, importDir, fqbnIn string, userPort *rpc.Port, programmerID string, @@ -374,7 +431,7 @@ func runProgramAction(pme *packagemanager.Explorer, } if !burnBootloader { - importPath, sketchName, err := determineBuildPathAndSketchName(importFile, importDir, sk, fqbn) + importPath, sketchName, err := determineBuildPathAndSketchName(importFile, importDir, sk) if err != nil { return nil, &cmderrors.NotFoundError{Message: tr("Error finding build artifacts"), Cause: err} } @@ -389,7 +446,7 @@ func runProgramAction(pme *packagemanager.Explorer, } // This context is kept alive for the entire duration of the upload - uploadCtx, uploadCompleted := context.WithCancel(context.Background()) + uploadCtx, uploadCompleted := context.WithCancel(ctx) defer uploadCompleted() // Start the upload port change detector. @@ -677,7 +734,7 @@ func runTool(recipeID string, props *properties.Map, outStream, errStream io.Wri return nil } -func determineBuildPathAndSketchName(importFile, importDir string, sk *sketch.Sketch, fqbn *cores.FQBN) (*paths.Path, string, error) { +func determineBuildPathAndSketchName(importFile, importDir string, sk *sketch.Sketch) (*paths.Path, string, error) { // In general, compiling a sketch will produce a set of files that are // named as the sketch but have different extensions, for example Sketch.ino // may produce: Sketch.ino.bin; Sketch.ino.hex; Sketch.ino.zip; etc... diff --git a/commands/upload/burnbootloader.go b/commands/service_upload_burnbootloader.go similarity index 52% rename from commands/upload/burnbootloader.go rename to commands/service_upload_burnbootloader.go index b589e573698..448618b164c 100644 --- a/commands/upload/burnbootloader.go +++ b/commands/service_upload_burnbootloader.go @@ -13,7 +13,7 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package upload +package commands import ( "context" @@ -24,8 +24,42 @@ import ( "github.com/sirupsen/logrus" ) -// BurnBootloader FIXMEDOC -func BurnBootloader(ctx context.Context, req *rpc.BurnBootloaderRequest, outStream io.Writer, errStream io.Writer) (*rpc.BurnBootloaderResponse, error) { +// BurnBootloaderToServerStreams return a server stream that forwards the output and error streams to the provided io.Writers +func BurnBootloaderToServerStreams(ctx context.Context, outStrem, errStream io.Writer) rpc.ArduinoCoreService_BurnBootloaderServer { + stream := streamResponseToCallback(ctx, func(resp *rpc.BurnBootloaderResponse) error { + if outData := resp.GetOutStream(); len(outData) > 0 { + _, err := outStrem.Write(outData) + return err + } + if errData := resp.GetErrStream(); len(errData) > 0 { + _, err := errStream.Write(errData) + return err + } + return nil + }) + return stream +} + +// BurnBootloader performs the burn bootloader action +func (s *arduinoCoreServerImpl) BurnBootloader(req *rpc.BurnBootloaderRequest, stream rpc.ArduinoCoreService_BurnBootloaderServer) error { + syncSend := NewSynchronizedSend(stream.Send) + outStream := feedStreamTo(func(data []byte) { + syncSend.Send(&rpc.BurnBootloaderResponse{ + Message: &rpc.BurnBootloaderResponse_OutStream{ + OutStream: data, + }, + }) + }) + defer outStream.Close() + errStream := feedStreamTo(func(data []byte) { + syncSend.Send(&rpc.BurnBootloaderResponse{ + Message: &rpc.BurnBootloaderResponse_ErrStream{ + ErrStream: data, + }, + }) + }) + defer errStream.Close() + logrus. WithField("fqbn", req.GetFqbn()). WithField("port", req.GetPort()). @@ -34,11 +68,12 @@ func BurnBootloader(ctx context.Context, req *rpc.BurnBootloaderRequest, outStre pme, release, err := instances.GetPackageManagerExplorer(req.GetInstance()) if err != nil { - return nil, err + return err } defer release() if _, err := runProgramAction( + stream.Context(), pme, nil, // sketch "", // importFile @@ -54,7 +89,7 @@ func BurnBootloader(ctx context.Context, req *rpc.BurnBootloaderRequest, outStre req.GetDryRun(), map[string]string{}, // User fields ); err != nil { - return nil, err + return err } - return &rpc.BurnBootloaderResponse{}, nil + return syncSend.Send(&rpc.BurnBootloaderResponse{}) } diff --git a/commands/upload/programmers_list.go b/commands/service_upload_list_programmers.go similarity index 90% rename from commands/upload/programmers_list.go rename to commands/service_upload_list_programmers.go index 76188ac5e2e..f05142cf147 100644 --- a/commands/upload/programmers_list.go +++ b/commands/service_upload_list_programmers.go @@ -13,7 +13,7 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package upload +package commands import ( "context" @@ -25,7 +25,7 @@ import ( ) // ListProgrammersAvailableForUpload FIXMEDOC -func ListProgrammersAvailableForUpload(ctx context.Context, req *rpc.ListProgrammersAvailableForUploadRequest) (*rpc.ListProgrammersAvailableForUploadResponse, error) { +func (s *arduinoCoreServerImpl) ListProgrammersAvailableForUpload(ctx context.Context, req *rpc.ListProgrammersAvailableForUploadRequest) (*rpc.ListProgrammersAvailableForUploadResponse, error) { pme, release, err := instances.GetPackageManagerExplorer(req.GetInstance()) if err != nil { return nil, err diff --git a/commands/upload/upload_test.go b/commands/service_upload_test.go similarity index 82% rename from commands/upload/upload_test.go rename to commands/service_upload_test.go index 95d26de6634..d350b5a6470 100644 --- a/commands/upload/upload_test.go +++ b/commands/service_upload_test.go @@ -13,10 +13,11 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package upload +package commands import ( "bytes" + "context" "fmt" "strings" "testing" @@ -29,26 +30,27 @@ import ( properties "github.com/arduino/go-properties-orderedmap" "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" + "go.bug.st/downloader/v2" ) func TestDetectSketchNameFromBuildPath(t *testing.T) { - sk1, err1 := detectSketchNameFromBuildPath(paths.New("testdata/build_path_1")) + sk1, err1 := detectSketchNameFromBuildPath(paths.New("testdata/upload/build_path_1")) require.NoError(t, err1) require.Equal(t, "sketch.ino", sk1) - sk2, err2 := detectSketchNameFromBuildPath(paths.New("testdata/build_path_2")) + sk2, err2 := detectSketchNameFromBuildPath(paths.New("testdata/upload/build_path_2")) require.NoError(t, err2) require.Equal(t, "Blink.ino", sk2) - sk3, err3 := detectSketchNameFromBuildPath(paths.New("testdata/build_path_3")) + sk3, err3 := detectSketchNameFromBuildPath(paths.New("testdata/upload/build_path_3")) require.Error(t, err3) require.Equal(t, "", sk3) - sk4, err4 := detectSketchNameFromBuildPath(paths.New("testdata/build_path_4")) + sk4, err4 := detectSketchNameFromBuildPath(paths.New("testdata/upload/build_path_4")) require.Error(t, err4) require.Equal(t, "", sk4) - sk5, err5 := detectSketchNameFromBuildPath(paths.New("testdata/build_path_invalid")) + sk5, err5 := detectSketchNameFromBuildPath(paths.New("testdata/upload/build_path_invalid")) require.Error(t, err5) require.Equal(t, "", sk5) } @@ -63,7 +65,7 @@ func TestDetermineBuildPathAndSketchName(t *testing.T) { resSketchName string } - blonk, err := sketch.New(paths.New("testdata/Blonk")) + blonk, err := sketch.New(paths.New("testdata/upload/Blonk")) require.NoError(t, err) fqbn, err := cores.ParseFQBN("arduino:samd:mkr1000") @@ -73,43 +75,43 @@ func TestDetermineBuildPathAndSketchName(t *testing.T) { // 00: error: no data passed in {"", "", nil, nil, "", ""}, // 01: use importFile to detect build.path and project_name - {"testdata/build_path_2/Blink.ino.hex", "", nil, nil, "testdata/build_path_2", "Blink.ino"}, + {"testdata/upload/build_path_2/Blink.ino.hex", "", nil, nil, "testdata/upload/build_path_2", "Blink.ino"}, // 02: use importPath as build.path and project_name - {"", "testdata/build_path_2", nil, nil, "testdata/build_path_2", "Blink.ino"}, + {"", "testdata/upload/build_path_2", nil, nil, "testdata/upload/build_path_2", "Blink.ino"}, // 03: error: used both importPath and importFile - {"testdata/build_path_2/Blink.ino.hex", "testdata/build_path_2", nil, nil, "", ""}, + {"testdata/upload/build_path_2/Blink.ino.hex", "testdata/upload/build_path_2", nil, nil, "", ""}, // 04: only sketch without FQBN {"", "", blonk, nil, blonk.DefaultBuildPath().String(), "Blonk.ino"}, // 05: use importFile to detect build.path and project_name, sketch is ignored. - {"testdata/build_path_2/Blink.ino.hex", "", blonk, nil, "testdata/build_path_2", "Blink.ino"}, + {"testdata/upload/build_path_2/Blink.ino.hex", "", blonk, nil, "testdata/upload/build_path_2", "Blink.ino"}, // 06: use importPath as build.path and Blink as project name, ignore the sketch Blonk - {"", "testdata/build_path_2", blonk, nil, "testdata/build_path_2", "Blink.ino"}, + {"", "testdata/upload/build_path_2", blonk, nil, "testdata/upload/build_path_2", "Blink.ino"}, // 07: error: used both importPath and importFile - {"testdata/build_path_2/Blink.ino.hex", "testdata/build_path_2", blonk, nil, "", ""}, + {"testdata/upload/build_path_2/Blink.ino.hex", "testdata/upload/build_path_2", blonk, nil, "", ""}, // 08: error: no data passed in {"", "", nil, fqbn, "", ""}, // 09: use importFile to detect build.path and project_name, fqbn ignored - {"testdata/build_path_2/Blink.ino.hex", "", nil, fqbn, "testdata/build_path_2", "Blink.ino"}, + {"testdata/upload/build_path_2/Blink.ino.hex", "", nil, fqbn, "testdata/upload/build_path_2", "Blink.ino"}, // 10: use importPath as build.path and project_name, fqbn ignored - {"", "testdata/build_path_2", nil, fqbn, "testdata/build_path_2", "Blink.ino"}, + {"", "testdata/upload/build_path_2", nil, fqbn, "testdata/upload/build_path_2", "Blink.ino"}, // 11: error: used both importPath and importFile - {"testdata/build_path_2/Blink.ino.hex", "testdata/build_path_2", nil, fqbn, "", ""}, + {"testdata/upload/build_path_2/Blink.ino.hex", "testdata/upload/build_path_2", nil, fqbn, "", ""}, // 12: use sketch to determine project name and sketch+fqbn to determine build path {"", "", blonk, fqbn, blonk.DefaultBuildPath().String(), "Blonk.ino"}, // 13: use importFile to detect build.path and project_name, sketch+fqbn is ignored. - {"testdata/build_path_2/Blink.ino.hex", "", blonk, fqbn, "testdata/build_path_2", "Blink.ino"}, + {"testdata/upload/build_path_2/Blink.ino.hex", "", blonk, fqbn, "testdata/upload/build_path_2", "Blink.ino"}, // 14: use importPath as build.path and Blink as project name, ignore the sketch Blonk, ignore fqbn - {"", "testdata/build_path_2", blonk, fqbn, "testdata/build_path_2", "Blink.ino"}, + {"", "testdata/upload/build_path_2", blonk, fqbn, "testdata/upload/build_path_2", "Blink.ino"}, // 15: error: used both importPath and importFile - {"testdata/build_path_2/Blink.ino.hex", "testdata/build_path_2", blonk, fqbn, "", ""}, + {"testdata/upload/build_path_2/Blink.ino.hex", "testdata/upload/build_path_2", blonk, fqbn, "", ""}, // 16: importPath containing multiple firmwares, but one has the same name as the containing folder - {"", "testdata/firmware", nil, fqbn, "testdata/firmware", "firmware.ino"}, + {"", "testdata/upload/firmware", nil, fqbn, "testdata/upload/firmware", "firmware.ino"}, // 17: importFile among multiple firmwares - {"testdata/firmware/another_firmware.ino.bin", "", nil, fqbn, "testdata/firmware", "another_firmware.ino"}, + {"testdata/upload/firmware/another_firmware.ino.bin", "", nil, fqbn, "testdata/upload/firmware", "another_firmware.ino"}, } for i, test := range tests { t.Run(fmt.Sprintf("SubTest%02d", i), func(t *testing.T) { - buildPath, sketchName, err := determineBuildPathAndSketchName(test.importFile, test.importDir, test.sketch, test.fqbn) + buildPath, sketchName, err := determineBuildPathAndSketchName(test.importFile, test.importDir, test.sketch) if test.resBuildPath == "" { require.Error(t, err) require.Nil(t, buildPath) @@ -127,10 +129,10 @@ func TestDetermineBuildPathAndSketchName(t *testing.T) { } func TestUploadPropertiesComposition(t *testing.T) { - pmb := packagemanager.NewBuilder(nil, nil, nil, nil, "test") - errs := pmb.LoadHardwareFromDirectory(paths.New("testdata", "hardware")) + pmb := packagemanager.NewBuilder(nil, nil, nil, nil, nil, "test", downloader.GetDefaultConfig()) + errs := pmb.LoadHardwareFromDirectory(paths.New("testdata", "upload", "hardware")) require.Len(t, errs, 0) - buildPath1 := paths.New("testdata", "build_path_1") + buildPath1 := paths.New("testdata", "upload", "build_path_1") logrus.SetLevel(logrus.TraceLevel) type test struct { importDir *paths.Path @@ -149,32 +151,32 @@ func TestUploadPropertiesComposition(t *testing.T) { tests := []test{ // 0: classic upload, requires port - {buildPath1, "alice:avr:board1", "port", "serial", "", false, "conf-board1 conf-general conf-upload $$VERBOSE-VERIFY$$ protocol port -bspeed testdata/build_path_1/sketch.ino.hex\n", ""}, + {buildPath1, "alice:avr:board1", "port", "serial", "", false, "conf-board1 conf-general conf-upload $$VERBOSE-VERIFY$$ protocol port -bspeed testdata/upload/build_path_1/sketch.ino.hex\n", ""}, {buildPath1, "alice:avr:board1", "", "", "", false, "FAIL", ""}, // 2: classic upload, no port - {buildPath1, "alice:avr:board2", "port", "serial", "", false, "conf-board1 conf-general conf-upload $$VERBOSE-VERIFY$$ protocol -bspeed testdata/build_path_1/sketch.ino.hex\n", ""}, - {buildPath1, "alice:avr:board2", "", "", "", false, "conf-board1 conf-general conf-upload $$VERBOSE-VERIFY$$ protocol -bspeed testdata/build_path_1/sketch.ino.hex\n", ""}, + {buildPath1, "alice:avr:board2", "port", "serial", "", false, "conf-board1 conf-general conf-upload $$VERBOSE-VERIFY$$ protocol -bspeed testdata/upload/build_path_1/sketch.ino.hex\n", ""}, + {buildPath1, "alice:avr:board2", "", "", "", false, "conf-board1 conf-general conf-upload $$VERBOSE-VERIFY$$ protocol -bspeed testdata/upload/build_path_1/sketch.ino.hex\n", ""}, // 4: upload with programmer, requires port - {buildPath1, "alice:avr:board1", "port", "serial", "progr1", false, "conf-board1 conf-general conf-program $$VERBOSE-VERIFY$$ progprotocol port -bspeed testdata/build_path_1/sketch.ino.hex\n", ""}, + {buildPath1, "alice:avr:board1", "port", "serial", "progr1", false, "conf-board1 conf-general conf-program $$VERBOSE-VERIFY$$ progprotocol port -bspeed testdata/upload/build_path_1/sketch.ino.hex\n", ""}, {buildPath1, "alice:avr:board1", "", "", "progr1", false, "FAIL", ""}, // 6: upload with programmer, no port - {buildPath1, "alice:avr:board1", "port", "serial", "progr2", false, "conf-board1 conf-general conf-program $$VERBOSE-VERIFY$$ prog2protocol -bspeed testdata/build_path_1/sketch.ino.hex\n", ""}, - {buildPath1, "alice:avr:board1", "", "", "progr2", false, "conf-board1 conf-general conf-program $$VERBOSE-VERIFY$$ prog2protocol -bspeed testdata/build_path_1/sketch.ino.hex\n", ""}, + {buildPath1, "alice:avr:board1", "port", "serial", "progr2", false, "conf-board1 conf-general conf-program $$VERBOSE-VERIFY$$ prog2protocol -bspeed testdata/upload/build_path_1/sketch.ino.hex\n", ""}, + {buildPath1, "alice:avr:board1", "", "", "progr2", false, "conf-board1 conf-general conf-program $$VERBOSE-VERIFY$$ prog2protocol -bspeed testdata/upload/build_path_1/sketch.ino.hex\n", ""}, // 8: upload with programmer, require port through extra params - {buildPath1, "alice:avr:board1", "port", "serial", "progr3", false, "conf-board1 conf-general conf-program $$VERBOSE-VERIFY$$ prog3protocol port -bspeed testdata/build_path_1/sketch.ino.hex\n", ""}, + {buildPath1, "alice:avr:board1", "port", "serial", "progr3", false, "conf-board1 conf-general conf-program $$VERBOSE-VERIFY$$ prog3protocol port -bspeed testdata/upload/build_path_1/sketch.ino.hex\n", ""}, {buildPath1, "alice:avr:board1", "", "", "progr3", false, "FAIL", ""}, // 10: burn bootloader, require port {buildPath1, "alice:avr:board1", "port", "serial", "", true, "FAIL", ""}, // requires programmer {buildPath1, "alice:avr:board1", "port", "serial", "progr1", true, "ERASE conf-board1 conf-general conf-erase $$VERBOSE-VERIFY$$ genprog1protocol port -bspeed\n", - "BURN conf-board1 conf-general conf-bootloader $$VERBOSE-VERIFY$$ genprog1protocol port -bspeed -F0xFF " + cwd + "/testdata/hardware/alice/avr/bootloaders/niceboot/niceboot.hex\n"}, + "BURN conf-board1 conf-general conf-bootloader $$VERBOSE-VERIFY$$ genprog1protocol port -bspeed -F0xFF " + cwd + "/testdata/upload/hardware/alice/avr/bootloaders/niceboot/niceboot.hex\n"}, // 12: burn bootloader, preferences override from programmers.txt {buildPath1, "alice:avr:board1", "port", "serial", "progr4", true, "ERASE conf-board1 conf-two-general conf-two-erase $$VERBOSE-VERIFY$$ prog4protocol-bootloader port -bspeed\n", - "BURN conf-board1 conf-two-general conf-two-bootloader $$VERBOSE-VERIFY$$ prog4protocol-bootloader port -bspeed -F0xFF " + cwd + "/testdata/hardware/alice/avr/bootloaders/niceboot/niceboot.hex\n"}, + "BURN conf-board1 conf-two-general conf-two-bootloader $$VERBOSE-VERIFY$$ prog4protocol-bootloader port -bspeed -F0xFF " + cwd + "/testdata/upload/hardware/alice/avr/bootloaders/niceboot/niceboot.hex\n"}, } pm := pmb.Build() @@ -185,6 +187,7 @@ func TestUploadPropertiesComposition(t *testing.T) { outStream := &bytes.Buffer{} errStream := &bytes.Buffer{} _, err := runProgramAction( + context.Background(), pme, nil, // sketch "", // importFile diff --git a/commands/daemon/term_example/main.go b/commands/term_example/main.go similarity index 100% rename from commands/daemon/term_example/main.go rename to commands/term_example/main.go diff --git a/commands/daemon/testdata/arduino-cli.yml b/commands/testdata/arduino-cli.yml similarity index 100% rename from commands/daemon/testdata/arduino-cli.yml rename to commands/testdata/arduino-cli.yml diff --git a/commands/debug/testdata/custom_hardware/arduino-test/samd/boards.txt b/commands/testdata/debug/custom_hardware/arduino-test/samd/boards.txt similarity index 100% rename from commands/debug/testdata/custom_hardware/arduino-test/samd/boards.txt rename to commands/testdata/debug/custom_hardware/arduino-test/samd/boards.txt diff --git a/commands/debug/testdata/custom_hardware/arduino-test/samd/platform.txt b/commands/testdata/debug/custom_hardware/arduino-test/samd/platform.txt similarity index 100% rename from commands/debug/testdata/custom_hardware/arduino-test/samd/platform.txt rename to commands/testdata/debug/custom_hardware/arduino-test/samd/platform.txt diff --git a/commands/debug/testdata/custom_hardware/arduino-test/samd/programmers.txt b/commands/testdata/debug/custom_hardware/arduino-test/samd/programmers.txt similarity index 100% rename from commands/debug/testdata/custom_hardware/arduino-test/samd/programmers.txt rename to commands/testdata/debug/custom_hardware/arduino-test/samd/programmers.txt diff --git a/commands/debug/testdata/data_dir/packages/arduino-test/tools/arm-none-eabi-gcc/7-2017q4/bin/arm-none-eabi-gdb b/commands/testdata/debug/data_dir/packages/arduino-test/tools/arm-none-eabi-gcc/7-2017q4/bin/arm-none-eabi-gdb similarity index 100% rename from commands/debug/testdata/data_dir/packages/arduino-test/tools/arm-none-eabi-gcc/7-2017q4/bin/arm-none-eabi-gdb rename to commands/testdata/debug/data_dir/packages/arduino-test/tools/arm-none-eabi-gcc/7-2017q4/bin/arm-none-eabi-gdb diff --git a/commands/debug/testdata/data_dir/packages/arduino-test/tools/openocd/0.10.0-arduino7/bin/openocd b/commands/testdata/debug/data_dir/packages/arduino-test/tools/openocd/0.10.0-arduino7/bin/openocd similarity index 100% rename from commands/debug/testdata/data_dir/packages/arduino-test/tools/openocd/0.10.0-arduino7/bin/openocd rename to commands/testdata/debug/data_dir/packages/arduino-test/tools/openocd/0.10.0-arduino7/bin/openocd diff --git a/commands/debug/testdata/hello/build/arduino-test.samd.arduino_zero_edbg/hello.ino.bin b/commands/testdata/debug/hello/build/arduino-test.samd.arduino_zero_edbg/hello.ino.bin similarity index 100% rename from commands/debug/testdata/hello/build/arduino-test.samd.arduino_zero_edbg/hello.ino.bin rename to commands/testdata/debug/hello/build/arduino-test.samd.arduino_zero_edbg/hello.ino.bin diff --git a/commands/debug/testdata/hello/build/arduino-test.samd.mkr1000/hello.ino.bin b/commands/testdata/debug/hello/build/arduino-test.samd.mkr1000/hello.ino.bin similarity index 100% rename from commands/debug/testdata/hello/build/arduino-test.samd.mkr1000/hello.ino.bin rename to commands/testdata/debug/hello/build/arduino-test.samd.mkr1000/hello.ino.bin diff --git a/commands/debug/testdata/hello/hello.ino b/commands/testdata/debug/hello/hello.ino similarity index 100% rename from commands/debug/testdata/hello/hello.ino rename to commands/testdata/debug/hello/hello.ino diff --git a/commands/lib/testdata/full/library_index.json b/commands/testdata/libraries/full/library_index.json similarity index 100% rename from commands/lib/testdata/full/library_index.json rename to commands/testdata/libraries/full/library_index.json diff --git a/commands/lib/testdata/qualified_search/library_index.json b/commands/testdata/libraries/qualified_search/library_index.json similarity index 100% rename from commands/lib/testdata/qualified_search/library_index.json rename to commands/testdata/libraries/qualified_search/library_index.json diff --git a/commands/lib/testdata/test1/library_index.json b/commands/testdata/libraries/test1/library_index.json similarity index 100% rename from commands/lib/testdata/test1/library_index.json rename to commands/testdata/libraries/test1/library_index.json diff --git a/commands/core/testdata/package_index.json b/commands/testdata/platform/package_index.json similarity index 100% rename from commands/core/testdata/package_index.json rename to commands/testdata/platform/package_index.json diff --git a/commands/sketch/testdata/sketch_with_profile/sketch.yml b/commands/testdata/sketch_with_profile/sketch.yml similarity index 100% rename from commands/sketch/testdata/sketch_with_profile/sketch.yml rename to commands/testdata/sketch_with_profile/sketch.yml diff --git a/commands/sketch/testdata/sketch_with_profile/sketch_with_profile.ino b/commands/testdata/sketch_with_profile/sketch_with_profile.ino similarity index 100% rename from commands/sketch/testdata/sketch_with_profile/sketch_with_profile.ino rename to commands/testdata/sketch_with_profile/sketch_with_profile.ino diff --git a/commands/upload/testdata/Blonk/Blonk.ino b/commands/testdata/upload/Blonk/Blonk.ino similarity index 100% rename from commands/upload/testdata/Blonk/Blonk.ino rename to commands/testdata/upload/Blonk/Blonk.ino diff --git a/commands/upload/testdata/Blonk/build/arduino.samd.mkr1000/Blonk.ino.bin b/commands/testdata/upload/Blonk/build/arduino.samd.mkr1000/Blonk.ino.bin similarity index 100% rename from commands/upload/testdata/Blonk/build/arduino.samd.mkr1000/Blonk.ino.bin rename to commands/testdata/upload/Blonk/build/arduino.samd.mkr1000/Blonk.ino.bin diff --git a/commands/upload/testdata/Blonk/build/arduino.samd.mkr1000/Blonk.ino.elf b/commands/testdata/upload/Blonk/build/arduino.samd.mkr1000/Blonk.ino.elf similarity index 100% rename from commands/upload/testdata/Blonk/build/arduino.samd.mkr1000/Blonk.ino.elf rename to commands/testdata/upload/Blonk/build/arduino.samd.mkr1000/Blonk.ino.elf diff --git a/commands/upload/testdata/Blonk/build/arduino.samd.mkr1000/Blonk.ino.hex b/commands/testdata/upload/Blonk/build/arduino.samd.mkr1000/Blonk.ino.hex similarity index 100% rename from commands/upload/testdata/Blonk/build/arduino.samd.mkr1000/Blonk.ino.hex rename to commands/testdata/upload/Blonk/build/arduino.samd.mkr1000/Blonk.ino.hex diff --git a/commands/upload/testdata/Blonk/build/arduino.samd.mkr1000/Blonk.ino.map b/commands/testdata/upload/Blonk/build/arduino.samd.mkr1000/Blonk.ino.map similarity index 100% rename from commands/upload/testdata/Blonk/build/arduino.samd.mkr1000/Blonk.ino.map rename to commands/testdata/upload/Blonk/build/arduino.samd.mkr1000/Blonk.ino.map diff --git a/commands/upload/testdata/Blonk/build/arduino.samd.mkr1000/Blonk.ino.with_bootloader.bin b/commands/testdata/upload/Blonk/build/arduino.samd.mkr1000/Blonk.ino.with_bootloader.bin similarity index 100% rename from commands/upload/testdata/Blonk/build/arduino.samd.mkr1000/Blonk.ino.with_bootloader.bin rename to commands/testdata/upload/Blonk/build/arduino.samd.mkr1000/Blonk.ino.with_bootloader.bin diff --git a/commands/upload/testdata/Blonk/build/arduino.samd.mkr1000/Blonk.ino.with_bootloader.hex b/commands/testdata/upload/Blonk/build/arduino.samd.mkr1000/Blonk.ino.with_bootloader.hex similarity index 100% rename from commands/upload/testdata/Blonk/build/arduino.samd.mkr1000/Blonk.ino.with_bootloader.hex rename to commands/testdata/upload/Blonk/build/arduino.samd.mkr1000/Blonk.ino.with_bootloader.hex diff --git a/commands/upload/testdata/build_path_1/sketch.ino.bin b/commands/testdata/upload/build_path_1/sketch.ino.bin similarity index 100% rename from commands/upload/testdata/build_path_1/sketch.ino.bin rename to commands/testdata/upload/build_path_1/sketch.ino.bin diff --git a/commands/upload/testdata/build_path_2/Blink.ino.bin b/commands/testdata/upload/build_path_2/Blink.ino.bin similarity index 100% rename from commands/upload/testdata/build_path_2/Blink.ino.bin rename to commands/testdata/upload/build_path_2/Blink.ino.bin diff --git a/commands/upload/testdata/build_path_2/Blink.ino.elf b/commands/testdata/upload/build_path_2/Blink.ino.elf similarity index 100% rename from commands/upload/testdata/build_path_2/Blink.ino.elf rename to commands/testdata/upload/build_path_2/Blink.ino.elf diff --git a/commands/upload/testdata/build_path_2/Blink.ino.hex b/commands/testdata/upload/build_path_2/Blink.ino.hex similarity index 100% rename from commands/upload/testdata/build_path_2/Blink.ino.hex rename to commands/testdata/upload/build_path_2/Blink.ino.hex diff --git a/commands/upload/testdata/build_path_2/Blink.ino.map b/commands/testdata/upload/build_path_2/Blink.ino.map similarity index 100% rename from commands/upload/testdata/build_path_2/Blink.ino.map rename to commands/testdata/upload/build_path_2/Blink.ino.map diff --git a/commands/upload/testdata/build_path_2/Blink.ino.with_bootloader.bin b/commands/testdata/upload/build_path_2/Blink.ino.with_bootloader.bin similarity index 100% rename from commands/upload/testdata/build_path_2/Blink.ino.with_bootloader.bin rename to commands/testdata/upload/build_path_2/Blink.ino.with_bootloader.bin diff --git a/commands/upload/testdata/build_path_2/Blink.ino.with_bootloader.hex b/commands/testdata/upload/build_path_2/Blink.ino.with_bootloader.hex similarity index 100% rename from commands/upload/testdata/build_path_2/Blink.ino.with_bootloader.hex rename to commands/testdata/upload/build_path_2/Blink.ino.with_bootloader.hex diff --git a/commands/upload/testdata/build_path_3/AnotherSketch.ino.bin b/commands/testdata/upload/build_path_3/AnotherSketch.ino.bin similarity index 100% rename from commands/upload/testdata/build_path_3/AnotherSketch.ino.bin rename to commands/testdata/upload/build_path_3/AnotherSketch.ino.bin diff --git a/commands/upload/testdata/build_path_3/Blink.ino.bin b/commands/testdata/upload/build_path_3/Blink.ino.bin similarity index 100% rename from commands/upload/testdata/build_path_3/Blink.ino.bin rename to commands/testdata/upload/build_path_3/Blink.ino.bin diff --git a/commands/upload/testdata/build_path_3/Blink.ino.elf b/commands/testdata/upload/build_path_3/Blink.ino.elf similarity index 100% rename from commands/upload/testdata/build_path_3/Blink.ino.elf rename to commands/testdata/upload/build_path_3/Blink.ino.elf diff --git a/commands/upload/testdata/build_path_3/Blink.ino.hex b/commands/testdata/upload/build_path_3/Blink.ino.hex similarity index 100% rename from commands/upload/testdata/build_path_3/Blink.ino.hex rename to commands/testdata/upload/build_path_3/Blink.ino.hex diff --git a/commands/upload/testdata/build_path_3/Blink.ino.map b/commands/testdata/upload/build_path_3/Blink.ino.map similarity index 100% rename from commands/upload/testdata/build_path_3/Blink.ino.map rename to commands/testdata/upload/build_path_3/Blink.ino.map diff --git a/commands/upload/testdata/build_path_3/Blink.ino.with_bootloader.bin b/commands/testdata/upload/build_path_3/Blink.ino.with_bootloader.bin similarity index 100% rename from commands/upload/testdata/build_path_3/Blink.ino.with_bootloader.bin rename to commands/testdata/upload/build_path_3/Blink.ino.with_bootloader.bin diff --git a/commands/upload/testdata/build_path_3/Blink.ino.with_bootloader.hex b/commands/testdata/upload/build_path_3/Blink.ino.with_bootloader.hex similarity index 100% rename from commands/upload/testdata/build_path_3/Blink.ino.with_bootloader.hex rename to commands/testdata/upload/build_path_3/Blink.ino.with_bootloader.hex diff --git a/commands/upload/testdata/build_path_4/some_other_files.txt b/commands/testdata/upload/build_path_4/some_other_files.txt similarity index 100% rename from commands/upload/testdata/build_path_4/some_other_files.txt rename to commands/testdata/upload/build_path_4/some_other_files.txt diff --git a/commands/upload/testdata/firmware/another_firmware.ino.bin b/commands/testdata/upload/firmware/another_firmware.ino.bin similarity index 100% rename from commands/upload/testdata/firmware/another_firmware.ino.bin rename to commands/testdata/upload/firmware/another_firmware.ino.bin diff --git a/commands/upload/testdata/firmware/firmware.ino.bin b/commands/testdata/upload/firmware/firmware.ino.bin similarity index 100% rename from commands/upload/testdata/firmware/firmware.ino.bin rename to commands/testdata/upload/firmware/firmware.ino.bin diff --git a/commands/upload/testdata/hardware/alice/avr/boards.txt b/commands/testdata/upload/hardware/alice/avr/boards.txt similarity index 100% rename from commands/upload/testdata/hardware/alice/avr/boards.txt rename to commands/testdata/upload/hardware/alice/avr/boards.txt diff --git a/commands/upload/testdata/hardware/alice/avr/platform.txt b/commands/testdata/upload/hardware/alice/avr/platform.txt similarity index 100% rename from commands/upload/testdata/hardware/alice/avr/platform.txt rename to commands/testdata/upload/hardware/alice/avr/platform.txt diff --git a/commands/upload/testdata/hardware/alice/avr/programmers.txt b/commands/testdata/upload/hardware/alice/avr/programmers.txt similarity index 100% rename from commands/upload/testdata/hardware/alice/avr/programmers.txt rename to commands/testdata/upload/hardware/alice/avr/programmers.txt diff --git a/commands/core.go b/commands/utility_core.go similarity index 91% rename from commands/core.go rename to commands/utility_core.go index e61078da1b2..b5a57ad509d 100644 --- a/commands/core.go +++ b/commands/utility_core.go @@ -20,8 +20,8 @@ import ( rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" ) -// PlatformToRPCPlatformMetadata converts our internal structure to the RPC structure. -func PlatformToRPCPlatformMetadata(platform *cores.Platform) *rpc.PlatformMetadata { +// platformToRPCPlatformMetadata converts our internal structure to the RPC structure. +func platformToRPCPlatformMetadata(platform *cores.Platform) *rpc.PlatformMetadata { return &rpc.PlatformMetadata{ Id: platform.String(), Maintainer: platform.Package.Maintainer, @@ -33,10 +33,10 @@ func PlatformToRPCPlatformMetadata(platform *cores.Platform) *rpc.PlatformMetada } } -// PlatformReleaseToRPC converts our internal structure to the RPC structure. +// platformReleaseToRPC converts our internal structure to the RPC structure. // Note: this function does not touch the "Installed" field of rpc.Platform as it's not always clear that the // platformRelease we're currently converting is actually installed. -func PlatformReleaseToRPC(platformRelease *cores.PlatformRelease) *rpc.PlatformRelease { +func platformReleaseToRPC(platformRelease *cores.PlatformRelease) *rpc.PlatformRelease { // If the boards are not installed yet, the `platformRelease.Boards` will be a zero length slice. // In such case, we have to use the `platformRelease.BoardsManifest` instead. // So that we can retrieve the name of the boards at least. diff --git a/commands/utility_grpc_streaming.go b/commands/utility_grpc_streaming.go new file mode 100644 index 00000000000..927e5e40f2d --- /dev/null +++ b/commands/utility_grpc_streaming.go @@ -0,0 +1,120 @@ +// This file is part of arduino-cli. +// +// Copyright 2024 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 ( + "context" + "errors" + "sync" + + "google.golang.org/grpc/metadata" +) + +type streamingResponseProxyToChan[T any] struct { + ctx context.Context + respChan chan<- *T + respLock sync.Mutex +} + +func streamResponseToChan[T any](ctx context.Context) (*streamingResponseProxyToChan[T], <-chan *T) { + respChan := make(chan *T, 1) + w := &streamingResponseProxyToChan[T]{ + ctx: ctx, + respChan: respChan, + } + go func() { + <-ctx.Done() + w.respLock.Lock() + close(w.respChan) + w.respChan = nil + w.respLock.Unlock() + }() + return w, respChan +} + +func (w *streamingResponseProxyToChan[T]) Send(resp *T) error { + w.respLock.Lock() + if w.respChan != nil { + w.respChan <- resp + } + w.respLock.Unlock() + return nil +} + +func (w *streamingResponseProxyToChan[T]) Context() context.Context { + return w.ctx +} + +func (w *streamingResponseProxyToChan[T]) RecvMsg(m any) error { + return errors.New("RecvMsg not implemented") +} + +func (w *streamingResponseProxyToChan[T]) SendHeader(metadata.MD) error { + return errors.New("SendHeader not implemented") +} + +func (w *streamingResponseProxyToChan[T]) SendMsg(m any) error { + return errors.New("SendMsg not implemented") +} + +func (w *streamingResponseProxyToChan[T]) SetHeader(metadata.MD) error { + return errors.New("SetHeader not implemented") +} + +func (w *streamingResponseProxyToChan[T]) SetTrailer(tr metadata.MD) { +} + +// streamingResponseProxyToCallback is a streaming response proxy that +// forwards the responses to a callback function +type streamingResponseProxyToCallback[T any] struct { + ctx context.Context + cb func(*T) error +} + +// creates a streaming response proxy that forwards the responses to a callback function +func streamResponseToCallback[T any](ctx context.Context, cb func(*T) error) *streamingResponseProxyToCallback[T] { + if cb == nil { + cb = func(*T) error { return nil } + } + return &streamingResponseProxyToCallback[T]{ctx: ctx, cb: cb} +} + +func (w *streamingResponseProxyToCallback[T]) Send(resp *T) error { + return w.cb(resp) +} + +func (w *streamingResponseProxyToCallback[T]) Context() context.Context { + return w.ctx +} + +func (w *streamingResponseProxyToCallback[T]) RecvMsg(m any) error { + return errors.New("RecvMsg not implemented") +} + +func (w *streamingResponseProxyToCallback[T]) SendHeader(metadata.MD) error { + return errors.New("SendHeader not implemented") +} + +func (w *streamingResponseProxyToCallback[T]) SendMsg(m any) error { + return errors.New("SendMsg not implemented") +} + +func (w *streamingResponseProxyToCallback[T]) SetHeader(metadata.MD) error { + return errors.New("SetHeader not implemented") +} + +func (w *streamingResponseProxyToCallback[T]) SetTrailer(tr metadata.MD) { +} diff --git a/commands/lib/search_matcher.go b/commands/utility_libraries_index_search_matcher.go similarity index 99% rename from commands/lib/search_matcher.go rename to commands/utility_libraries_index_search_matcher.go index 3eec08b4ad9..f8ed7205c9d 100644 --- a/commands/lib/search_matcher.go +++ b/commands/utility_libraries_index_search_matcher.go @@ -13,7 +13,7 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package lib +package commands import ( "strings" diff --git a/commands/version.go b/commands/utility_version.go similarity index 89% rename from commands/version.go rename to commands/utility_version.go index 2fd87215a2f..a7232d6fdc4 100644 --- a/commands/version.go +++ b/commands/utility_version.go @@ -20,10 +20,10 @@ import ( semver "go.bug.st/relaxed-semver" ) -// ParseVersion returns the parsed version or nil if the version is +// parseVersion returns the parsed version or nil if the version is // the empty string. An error is returned if the version is not valid // semver. -func ParseVersion(version string) (*semver.Version, error) { +func parseVersion(version string) (*semver.Version, error) { if version == "" { return nil, nil } diff --git a/docs/UPGRADING.md b/docs/UPGRADING.md index ef245d67a75..1273fa0a455 100644 --- a/docs/UPGRADING.md +++ b/docs/UPGRADING.md @@ -4,6 +4,173 @@ Here you can find a list of migration guides to handle breaking changes between ## 0.36.0 +### Configuration file now supports only YAML format. + +The Arduino CLI configuration file now supports only the YAML format. + +### gRPC Setting API important changes + +The Settings API has been heavily refactored. Here a quick recap of the new methods: + +- `SettingsGetValue` returns the value of a setting given the key. The returned value is a string encoded in JSON, or + YAML + + ```proto + message SettingsGetValueRequest { + // The key to get + string key = 1; + // The format of the encoded_value (default is + // "json", allowed values are "json" and "yaml) + string value_format = 2; + } + + message SettingsGetValueResponse { + // The value of the key (encoded) + string encoded_value = 1; + } + ``` + +- `SettingsSetValue` change the value of a setting. The value may be specified in JSON, YAML, or as a command-line + argument. If `encoded_value` is an empty string the setting is deleted. + + ```proto + message SettingsSetValueRequest { + // The key to change + string key = 1; + // The new value (encoded), no objects, + // only scalar, or array of scalars are + // allowed. + string encoded_value = 2; + // The format of the encoded_value (default is + // "json", allowed values are "json", "yaml", + // and "cli") + string value_format = 3; + } + ``` + +- `SettingsEnumerate` returns all the available keys and their type (`string`, `int`, `[]string`...) +- `ConfigurationOpen` replaces the current configuration with the one passed as argument. Differently from + `SettingsSetValue`, this call replaces the whole configuration. +- `ConfigurationSave` outputs the current configuration in the specified format. The configuration is not saved in a + file, this call returns just the content, it's a duty of the caller to store the content in a file. +- `ConfigurationGet` return the current configuration in a structured gRPC message `Configuration`. + +The previous gRPC Setting rpc call may be replaced as follows: + +- The old `SettingsMerge` rpc call can now be done trough `SettingsSetValue`. +- The old `SettingsDelete` rpc call can now be done trough `SettingsSetValue` passing the `key` to delete with an empty + `value`. +- The old `SettingsGetAll` rpc call has been replaced by `ConfigurationGet` that returns a structured message + `Configuration` with all the settings populated. +- The old `SettingsWrite` rpc call has been removed. It is partially replaced by `ConfigurationSave` but the actual file + save must be performed by the caller. + +### golang: importing `arduino-cli` as a library now requires the creation of a gRPC service. + +Previously the methods implementing the Arduino business logic were available in the global namespace +`github.com/arduino/arduino-cli/commands/*` and could be called directly. + +The above is no more true. All the global `commands.*` functions have been converted to methods of the +`arduinoCoreServerImpl` struct that implements the gRPC `ArduinoCoreServer` interface. The configuration is now part of +the server internal state. Developers may create a "daemon-less" server by calling the `commands.NewArduinoCoreServer()` +function and can use the returned object to call all the needed gRPC functions. + +The methods of the `ArduinoCoreServer` are generated through the gRPC protobuf definitions, some of those methods are +gRPC "streaming" methods and requires a streaming object to be passed as argument, we provided helper methods to create +these objects. + +For example if previously we could call `commands.Init` like this: + +```go +// old Init signature +func Init(req *rpc.InitRequest, responseCallback func(r *rpc.InitResponse)) error { ... } + +// ... + +// Initialize instance +if err := commands.Init(&rpc.InitRequest{Instance: req.GetInstance()}, respCB); err != nil { + return err +} +``` + +now the `responseCallback` must be wrapped into an `rpc.ArduinoCoreService_InitServer`, and we provided a method exactly +for that: + +```go +// new Init method +func (s *arduinoCoreServerImpl) Init(req *rpc.InitRequest, stream rpc.ArduinoCoreService_InitServer) error { ... } + +/// ... + +// Initialize instance +initStream := InitStreamResponseToCallbackFunction(ctx, respCB) +if err := srv.Init(&rpc.InitRequest{Instance: req.GetInstance()}, initStream); err != nil { + return err +} +``` + +Each gRPC method has an helper method to obtain the corresponding `ArduinoCoreService_*Server` parameter. Here a simple, +but complete, example: + +```go +package main + +import ( + "context" + "fmt" + "io" + "log" + + "github.com/arduino/arduino-cli/commands" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" + "github.com/sirupsen/logrus" +) + +func main() { + // Create a new ArduinoCoreServer + srv := commands.NewArduinoCoreServer() + + // Disable logging + logrus.SetOutput(io.Discard) + + // Create a new instance in the server + ctx := context.Background() + resp, err := srv.Create(ctx, &rpc.CreateRequest{}) + if err != nil { + log.Fatal("Error creating instance:", err) + } + instance := resp.GetInstance() + + // Defer the destruction of the instance + defer func() { + if _, err := srv.Destroy(ctx, &rpc.DestroyRequest{Instance: instance}); err != nil { + log.Fatal("Error destroying instance:", err) + } + fmt.Println("Instance successfully destroyed") + }() + + // Initialize the instance + initStream := commands.InitStreamResponseToCallbackFunction(ctx, func(r *rpc.InitResponse) error { + fmt.Println("INIT> ", r) + return nil + }) + if err := srv.Init(&rpc.InitRequest{Instance: instance}, initStream); err != nil { + log.Fatal("Error during initialization:", err) + } + + // Search for platforms and output the result + searchResp, err := srv.PlatformSearch(ctx, &rpc.PlatformSearchRequest{Instance: instance}) + if err != nil { + log.Fatal("Error searching for platforms:", err) + } + for _, platformSummary := range searchResp.GetSearchOutput() { + installed := platformSummary.GetInstalledRelease() + meta := platformSummary.GetMetadata() + fmt.Printf("%30s %8s %s\n", meta.GetId(), installed.GetVersion(), installed.GetName()) + } +} +``` + ### YAML output format is no more supported The `yaml` option of the `--format` flag is no more supported. Use `--format json` if machine parsable output is needed. @@ -1698,7 +1865,7 @@ https://arduino.github.io/arduino-cli/dev/rpc/commands/#monitorresponse https://arduino.github.io/arduino-cli/dev/rpc/commands/#enumeratemonitorportsettingsrequest https://arduino.github.io/arduino-cli/dev/rpc/commands/#enumeratemonitorportsettingsresponse -https://github.com/arduino/arduino-cli/blob/master/commands/daemon/term_example/main.go +https://github.com/arduino/arduino-cli/blob/752709af9bf1bf8f6c1e6f689b1e8b86cc4e884e/commands/daemon/term_example/main.go ## 0.23.0 diff --git a/docs/configuration.md b/docs/configuration.md index 57bda320635..bfde1372dac 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -97,8 +97,7 @@ $ export ARDUINO_BOARD_MANAGER_ADDITIONAL_URLS="https://downloads.arduino.cc/pac ### Configuration file -[`arduino-cli config init`][arduino-cli config init] creates or updates a configuration file with the current -configuration settings. +[`arduino-cli config init`][arduino-cli config init] creates a new empty configuration file. This allows saving the options set by command line flags or environment variables. For example: @@ -106,29 +105,14 @@ This allows saving the options set by command line flags or environment variable arduino-cli config init --additional-urls https://downloads.arduino.cc/packages/package_staging_index.json ``` -#### File name - -The configuration file must be named `arduino-cli`, with the appropriate file extension for the file's format. - -#### Supported formats - -`arduino-cli config init` creates a YAML file, however a variety of common formats are supported: - -- [JSON] -- [TOML] -- [YAML] -- [Java properties file] -- [HCL] -- envfile -- [INI] - #### Locations -Configuration files in the following locations are recognized by Arduino CLI: +The default configuration file is named `arduino-cli.yaml`. The configuration file is searched in the following +locations, in order of priority: 1. Location specified by the [`--config-file`][arduino cli command reference] command line flag 1. Location specified by the `ARDUINO_CONFIG_FILE` environment variable -1. Arduino CLI data directory (as configured by `directories.data`) +1. Location specified by the `ARDUINO_DIRECTORIES_DATA` environment variable If multiple configuration files are present, the one highest on the above list is used. Configuration files are not combined. diff --git a/internal/algorithms/slices.go b/internal/algorithms/slices.go index ab904a97f94..90ea3d1984b 100644 --- a/internal/algorithms/slices.go +++ b/internal/algorithms/slices.go @@ -71,3 +71,17 @@ func NotEquals[T comparable](value T) Matcher[T] { return x != value } } + +// Uniq return a copy of the input array with all duplicates removed +func Uniq[T comparable](in []T) []T { + have := map[T]bool{} + var out []T + for _, v := range in { + if have[v] { + continue + } + out = append(out, v) + have[v] = true + } + return out +} diff --git a/internal/arduino/builder/builder.go b/internal/arduino/builder/builder.go index 303caaa2063..1727556b846 100644 --- a/internal/arduino/builder/builder.go +++ b/internal/arduino/builder/builder.go @@ -16,6 +16,7 @@ package builder import ( + "context" "errors" "fmt" "io" @@ -43,6 +44,8 @@ var ErrSketchCannotBeLocatedInBuildPath = errors.New("sketch cannot be located i // Builder is a Sketch builder. type Builder struct { + ctx context.Context + sketch *sketch.Sketch buildProperties *properties.Map @@ -111,6 +114,7 @@ type buildArtifacts struct { // NewBuilder creates a sketch Builder. func NewBuilder( + ctx context.Context, sk *sketch.Sketch, boardBuildProperties *properties.Map, buildPath *paths.Path, @@ -196,6 +200,7 @@ func NewBuilder( diagnosticStore := diagnostics.NewStore() b := &Builder{ + ctx: ctx, sketch: sk, buildProperties: buildProperties, buildPath: buildPath, @@ -303,6 +308,7 @@ func (b *Builder) preprocess() error { b.logIfVerbose(false, tr("Detecting libraries used...")) err := b.libsDetector.FindIncludes( + b.ctx, b.buildPath, b.buildProperties.GetPath("build.core.path"), b.buildProperties.GetPath("build.variant.path"), diff --git a/internal/arduino/builder/internal/detector/detector.go b/internal/arduino/builder/internal/detector/detector.go index 46fa991a132..8f9a305cea4 100644 --- a/internal/arduino/builder/internal/detector/detector.go +++ b/internal/arduino/builder/internal/detector/detector.go @@ -17,6 +17,7 @@ package detector import ( "bytes" + "context" "encoding/json" "errors" "fmt" @@ -196,6 +197,7 @@ func (l *SketchLibrariesDetector) appendIncludeFolder( // FindIncludes todo func (l *SketchLibrariesDetector) FindIncludes( + ctx context.Context, buildPath *paths.Path, buildCorePath *paths.Path, buildVariantPath *paths.Path, @@ -205,7 +207,7 @@ func (l *SketchLibrariesDetector) FindIncludes( buildProperties *properties.Map, platformArch string, ) error { - err := l.findIncludes(buildPath, buildCorePath, buildVariantPath, sketchBuildPath, sketch, librariesBuildPath, buildProperties, platformArch) + err := l.findIncludes(ctx, buildPath, buildCorePath, buildVariantPath, sketchBuildPath, sketch, librariesBuildPath, buildProperties, platformArch) if err != nil && l.onlyUpdateCompilationDatabase { l.logger.Info( fmt.Sprintf( @@ -220,6 +222,7 @@ func (l *SketchLibrariesDetector) FindIncludes( } func (l *SketchLibrariesDetector) findIncludes( + ctx context.Context, buildPath *paths.Path, buildCorePath *paths.Path, buildVariantPath *paths.Path, @@ -269,7 +272,7 @@ func (l *SketchLibrariesDetector) findIncludes( } for !sourceFileQueue.empty() { - err := l.findIncludesUntilDone(cache, sourceFileQueue, buildProperties, sketchBuildPath, librariesBuildPath, platformArch) + err := l.findIncludesUntilDone(ctx, cache, sourceFileQueue, buildProperties, librariesBuildPath, platformArch) if err != nil { cachePath.Remove() return err @@ -297,10 +300,10 @@ func (l *SketchLibrariesDetector) findIncludes( } func (l *SketchLibrariesDetector) findIncludesUntilDone( + ctx context.Context, cache *includeCache, sourceFileQueue *uniqueSourceFileQueue, buildProperties *properties.Map, - sketchBuildPath *paths.Path, librariesBuildPath *paths.Path, platformArch string, ) error { @@ -350,7 +353,7 @@ func (l *SketchLibrariesDetector) findIncludesUntilDone( l.logger.Info(tr("Using cached library dependencies for file: %[1]s", sourcePath)) } } else { - preprocFirstResult, preprocErr = preprocessor.GCC(sourcePath, targetFilePath, includeFolders, buildProperties) + preprocFirstResult, preprocErr = preprocessor.GCC(ctx, sourcePath, targetFilePath, includeFolders, buildProperties) if l.logger.Verbose() { l.logger.WriteStdout(preprocFirstResult.Stdout()) } @@ -381,7 +384,7 @@ func (l *SketchLibrariesDetector) findIncludesUntilDone( // Library could not be resolved, show error if preprocErr == nil || preprocFirstResult.Stderr() == nil { // Filename came from cache, so run preprocessor to obtain error to show - result, err := preprocessor.GCC(sourcePath, targetFilePath, includeFolders, buildProperties) + result, err := preprocessor.GCC(ctx, sourcePath, targetFilePath, includeFolders, buildProperties) if l.logger.Verbose() { l.logger.WriteStdout(result.Stdout()) } diff --git a/internal/arduino/builder/internal/preprocessor/arduino_preprocessor.go b/internal/arduino/builder/internal/preprocessor/arduino_preprocessor.go index da8cde2adfc..399ca34f742 100644 --- a/internal/arduino/builder/internal/preprocessor/arduino_preprocessor.go +++ b/internal/arduino/builder/internal/preprocessor/arduino_preprocessor.go @@ -31,6 +31,7 @@ import ( // PreprocessSketchWithArduinoPreprocessor performs preprocessing of the arduino sketch // using arduino-preprocessor (https://github.com/arduino/arduino-preprocessor). func PreprocessSketchWithArduinoPreprocessor( + ctx context.Context, sk *sketch.Sketch, buildPath *paths.Path, includeFolders paths.PathList, lineOffset int, buildProperties *properties.Map, onlyUpdateCompilationDatabase bool, ) (*Result, error) { @@ -42,7 +43,7 @@ func PreprocessSketchWithArduinoPreprocessor( sourceFile := buildPath.Join("sketch", sk.MainFile.Base()+".cpp") targetFile := buildPath.Join("preproc", "sketch_merged.cpp") - gccResult, err := GCC(sourceFile, targetFile, includeFolders, buildProperties) + gccResult, err := GCC(ctx, sourceFile, targetFile, includeFolders, buildProperties) verboseOut.Write(gccResult.Stdout()) verboseOut.Write(gccResult.Stderr()) if err != nil { @@ -78,7 +79,7 @@ func PreprocessSketchWithArduinoPreprocessor( } verboseOut.WriteString(commandLine) - commandStdOut, commandStdErr, err := command.RunAndCaptureOutput(context.Background()) + commandStdOut, commandStdErr, err := command.RunAndCaptureOutput(ctx) verboseOut.Write(commandStdErr) if err != nil { return &Result{args: gccResult.Args(), stdout: verboseOut.Bytes(), stderr: normalOut.Bytes()}, err diff --git a/internal/arduino/builder/internal/preprocessor/ctags.go b/internal/arduino/builder/internal/preprocessor/ctags.go index 4a0cf783b45..73b60f80c49 100644 --- a/internal/arduino/builder/internal/preprocessor/ctags.go +++ b/internal/arduino/builder/internal/preprocessor/ctags.go @@ -41,6 +41,7 @@ var DebugPreprocessor bool // PreprocessSketchWithCtags performs preprocessing of the arduino sketch using CTags. func PreprocessSketchWithCtags( + ctx context.Context, sketch *sketch.Sketch, buildPath *paths.Path, includes paths.PathList, lineOffset int, buildProperties *properties.Map, onlyUpdateCompilationDatabase, verbose bool, @@ -57,7 +58,7 @@ func PreprocessSketchWithCtags( // Run GCC preprocessor sourceFile := buildPath.Join("sketch", sketch.MainFile.Base()+".cpp") - result, err := GCC(sourceFile, ctagsTarget, includes, buildProperties) + result, err := GCC(ctx, sourceFile, ctagsTarget, includes, buildProperties) stdout.Write(result.Stdout()) stderr.Write(result.Stderr()) if err != nil { @@ -84,7 +85,7 @@ func PreprocessSketchWithCtags( } // Run CTags on gcc-preprocessed source - ctagsOutput, ctagsStdErr, err := RunCTags(ctagsTarget, buildProperties) + ctagsOutput, ctagsStdErr, err := RunCTags(ctx, ctagsTarget, buildProperties) if verbose { stderr.Write(ctagsStdErr) } @@ -179,7 +180,7 @@ func isFirstFunctionOutsideOfSource(firstFunctionLine int, sourceRows []string) } // RunCTags performs a run of ctags on the given source file. Returns the ctags output and the stderr contents. -func RunCTags(sourceFile *paths.Path, buildProperties *properties.Map) ([]byte, []byte, error) { +func RunCTags(ctx context.Context, sourceFile *paths.Path, buildProperties *properties.Map) ([]byte, []byte, error) { ctagsBuildProperties := properties.NewMap() ctagsBuildProperties.Set("tools.ctags.path", "{runtime.tools.ctags.path}") ctagsBuildProperties.Set("tools.ctags.cmd.path", "{path}/ctags") @@ -202,7 +203,7 @@ func RunCTags(sourceFile *paths.Path, buildProperties *properties.Map) ([]byte, if err != nil { return nil, nil, err } - stdout, stderr, err := proc.RunAndCaptureOutput(context.Background()) + stdout, stderr, err := proc.RunAndCaptureOutput(ctx) // Append ctags arguments to stderr args := fmt.Sprintln(strings.Join(parts, " ")) diff --git a/internal/arduino/builder/internal/preprocessor/gcc.go b/internal/arduino/builder/internal/preprocessor/gcc.go index d9cf1c446ea..f97426c2df8 100644 --- a/internal/arduino/builder/internal/preprocessor/gcc.go +++ b/internal/arduino/builder/internal/preprocessor/gcc.go @@ -30,6 +30,7 @@ import ( // GCC performs a run of the gcc preprocess (macro/includes expansion). The function outputs the result // to targetFilePath. Returns the stdout/stderr of gcc if any. func GCC( + ctx context.Context, sourceFilePath, targetFilePath *paths.Path, includes paths.PathList, buildProperties *properties.Map, ) (Result, error) { @@ -75,7 +76,7 @@ func GCC( if err != nil { return Result{}, err } - stdout, stderr, err := proc.RunAndCaptureOutput(context.Background()) + stdout, stderr, err := proc.RunAndCaptureOutput(ctx) // Append gcc arguments to stdout stdout = append([]byte(fmt.Sprintln(strings.Join(args, " "))), stdout...) diff --git a/internal/arduino/builder/preprocess_sketch.go b/internal/arduino/builder/preprocess_sketch.go index d7fd6e32e72..86d7bd7e7e9 100644 --- a/internal/arduino/builder/preprocess_sketch.go +++ b/internal/arduino/builder/preprocess_sketch.go @@ -24,6 +24,7 @@ import ( func (b *Builder) preprocessSketch(includes paths.PathList) error { // In the future we might change the preprocessor result, err := preprocessor.PreprocessSketchWithCtags( + b.ctx, b.sketch, b.buildPath, includes, b.lineOffset, b.buildProperties, b.onlyUpdateCompilationDatabase, b.logger.Verbose(), ) diff --git a/internal/arduino/cores/packagemanager/download.go b/internal/arduino/cores/packagemanager/download.go index c4a7cb62d59..4ac06cf19f8 100644 --- a/internal/arduino/cores/packagemanager/download.go +++ b/internal/arduino/cores/packagemanager/download.go @@ -22,7 +22,6 @@ import ( "github.com/arduino/arduino-cli/commands/cmderrors" "github.com/arduino/arduino-cli/internal/arduino/cores" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" - "go.bug.st/downloader/v2" semver "go.bug.st/relaxed-semver" ) @@ -120,21 +119,21 @@ func (pme *Explorer) FindPlatformReleaseDependencies(item *PlatformReference) (* // DownloadToolRelease downloads a ToolRelease. If the tool is already downloaded a nil Downloader // is returned. Uses the given downloader configuration for download, or the default config if nil. -func (pme *Explorer) DownloadToolRelease(tool *cores.ToolRelease, config *downloader.Config, progressCB rpc.DownloadProgressCB) error { +func (pme *Explorer) DownloadToolRelease(tool *cores.ToolRelease, progressCB rpc.DownloadProgressCB) error { resource := tool.GetCompatibleFlavour() if resource == nil { return &cmderrors.FailedDownloadError{ Message: tr("Error downloading tool %s", tool), Cause: errors.New(tr("no versions available for the current OS, try contacting %s", tool.Tool.Package.Email))} } - return resource.Download(pme.DownloadDir, config, tool.String(), progressCB, "") + return resource.Download(pme.DownloadDir, pme.downloaderConfig, tool.String(), progressCB, "") } // DownloadPlatformRelease downloads a PlatformRelease. If the platform is already downloaded a // nil Downloader is returned. -func (pme *Explorer) DownloadPlatformRelease(platform *cores.PlatformRelease, config *downloader.Config, progressCB rpc.DownloadProgressCB) error { +func (pme *Explorer) DownloadPlatformRelease(platform *cores.PlatformRelease, progressCB rpc.DownloadProgressCB) error { if platform.Resource == nil { return &cmderrors.PlatformNotFoundError{Platform: platform.String()} } - return platform.Resource.Download(pme.DownloadDir, config, platform.String(), progressCB, "") + return platform.Resource.Download(pme.DownloadDir, pme.downloaderConfig, platform.String(), progressCB, "") } diff --git a/internal/arduino/cores/packagemanager/install_uninstall.go b/internal/arduino/cores/packagemanager/install_uninstall.go index bc29f08fa43..4b8c1cba9b4 100644 --- a/internal/arduino/cores/packagemanager/install_uninstall.go +++ b/internal/arduino/cores/packagemanager/install_uninstall.go @@ -92,11 +92,11 @@ func (pme *Explorer) DownloadAndInstallPlatformAndTools( // Package download taskCB(&rpc.TaskProgress{Name: tr("Downloading packages")}) for _, tool := range toolsToInstall { - if err := pme.DownloadToolRelease(tool, nil, downloadCB); err != nil { + if err := pme.DownloadToolRelease(tool, downloadCB); err != nil { return err } } - if err := pme.DownloadPlatformRelease(platformRelease, nil, downloadCB); err != nil { + if err := pme.DownloadPlatformRelease(platformRelease, downloadCB); err != nil { return err } taskCB(&rpc.TaskProgress{Completed: true}) diff --git a/internal/arduino/cores/packagemanager/loader.go b/internal/arduino/cores/packagemanager/loader.go index 68c603044e5..8b42697a6e4 100644 --- a/internal/arduino/cores/packagemanager/loader.go +++ b/internal/arduino/cores/packagemanager/loader.go @@ -24,7 +24,6 @@ import ( "github.com/arduino/arduino-cli/commands/cmderrors" "github.com/arduino/arduino-cli/internal/arduino/cores" - "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/arduino/go-paths-helper" properties "github.com/arduino/go-properties-orderedmap" semver "go.bug.st/relaxed-semver" @@ -32,7 +31,13 @@ import ( // LoadHardware read all plaforms from the configured paths func (pm *Builder) LoadHardware() []error { - hardwareDirs := configuration.HardwareDirectories(configuration.Settings) + hardwareDirs := paths.NewPathList() + if pm.PackagesDir.IsDir() { + hardwareDirs.Add(pm.PackagesDir) + } + if pm.userPackagesDir != nil && pm.userPackagesDir.IsDir() { + hardwareDirs.Add(pm.userPackagesDir) + } return pm.LoadHardwareFromDirectories(hardwareDirs) } diff --git a/internal/arduino/cores/packagemanager/loader_test.go b/internal/arduino/cores/packagemanager/loader_test.go index d0d41992179..5d394280aa8 100644 --- a/internal/arduino/cores/packagemanager/loader_test.go +++ b/internal/arduino/cores/packagemanager/loader_test.go @@ -21,6 +21,7 @@ import ( "github.com/arduino/go-paths-helper" "github.com/arduino/go-properties-orderedmap" "github.com/stretchr/testify/require" + "go.bug.st/downloader/v2" semver "go.bug.st/relaxed-semver" ) @@ -174,7 +175,7 @@ func TestLoadDiscoveries(t *testing.T) { defer fakePath.RemoveAll() createTestPackageManager := func() *PackageManager { - pmb := NewBuilder(fakePath, fakePath, fakePath, fakePath, "test") + pmb := NewBuilder(fakePath, fakePath, nil, fakePath, fakePath, "test", downloader.GetDefaultConfig()) pack := pmb.packages.GetOrCreatePackage("arduino") // ble-discovery tool tool := pack.GetOrCreateTool("ble-discovery") diff --git a/internal/arduino/cores/packagemanager/package_manager.go b/internal/arduino/cores/packagemanager/package_manager.go index cea5e799d1b..ffe278a6205 100644 --- a/internal/arduino/cores/packagemanager/package_manager.go +++ b/internal/arduino/cores/packagemanager/package_manager.go @@ -33,12 +33,12 @@ import ( "github.com/arduino/arduino-cli/internal/arduino/cores/packageindex" "github.com/arduino/arduino-cli/internal/arduino/discovery/discoverymanager" "github.com/arduino/arduino-cli/internal/arduino/sketch" - "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/arduino/arduino-cli/internal/i18n" paths "github.com/arduino/go-paths-helper" properties "github.com/arduino/go-properties-orderedmap" "github.com/arduino/go-timeutils" "github.com/sirupsen/logrus" + "go.bug.st/downloader/v2" semver "go.bug.st/relaxed-semver" ) @@ -55,11 +55,13 @@ type PackageManager struct { log logrus.FieldLogger IndexDir *paths.Path PackagesDir *paths.Path + userPackagesDir *paths.Path DownloadDir *paths.Path tempDir *paths.Path profile *sketch.Profile discoveryManager *discoverymanager.DiscoveryManager userAgent string + downloaderConfig downloader.Config } // Builder is used to create a new PackageManager. The builder @@ -75,17 +77,19 @@ type Explorer PackageManager var tr = i18n.Tr // NewBuilder returns a new Builder -func NewBuilder(indexDir, packagesDir, downloadDir, tempDir *paths.Path, userAgent string) *Builder { +func NewBuilder(indexDir, packagesDir, userPackagesDir, downloadDir, tempDir *paths.Path, userAgent string, downloaderConfig downloader.Config) *Builder { return &Builder{ log: logrus.StandardLogger(), packages: cores.NewPackages(), IndexDir: indexDir, PackagesDir: packagesDir, + userPackagesDir: userPackagesDir, DownloadDir: downloadDir, tempDir: tempDir, packagesCustomGlobalProperties: properties.NewMap(), - discoveryManager: discoverymanager.New(configuration.UserAgent(configuration.Settings)), + discoveryManager: discoverymanager.New(userAgent), userAgent: userAgent, + downloaderConfig: downloaderConfig, } } @@ -98,6 +102,7 @@ func (pmb *Builder) BuildIntoExistingPackageManager(target *PackageManager) { target.packages = pmb.packages target.IndexDir = pmb.IndexDir target.PackagesDir = pmb.PackagesDir + target.userPackagesDir = pmb.userPackagesDir target.DownloadDir = pmb.DownloadDir target.tempDir = pmb.tempDir target.packagesCustomGlobalProperties = pmb.packagesCustomGlobalProperties @@ -114,6 +119,7 @@ func (pmb *Builder) Build() *PackageManager { packages: pmb.packages, IndexDir: pmb.IndexDir, PackagesDir: pmb.PackagesDir, + userPackagesDir: pmb.userPackagesDir, DownloadDir: pmb.DownloadDir, tempDir: pmb.tempDir, packagesCustomGlobalProperties: pmb.packagesCustomGlobalProperties, @@ -164,7 +170,7 @@ func (pmb *Builder) calculateCompatibleReleases() { // this function will make the builder write the new configuration into this // PackageManager. func (pm *PackageManager) NewBuilder() (builder *Builder, commit func()) { - pmb := NewBuilder(pm.IndexDir, pm.PackagesDir, pm.DownloadDir, pm.tempDir, pm.userAgent) + pmb := NewBuilder(pm.IndexDir, pm.PackagesDir, pm.userPackagesDir, pm.DownloadDir, pm.tempDir, pm.userAgent, pm.downloaderConfig) return pmb, func() { pmb.calculateCompatibleReleases() pmb.BuildIntoExistingPackageManager(pm) @@ -188,6 +194,7 @@ func (pm *PackageManager) NewExplorer() (explorer *Explorer, release func()) { profile: pm.profile, discoveryManager: pm.discoveryManager, userAgent: pm.userAgent, + downloaderConfig: pm.downloaderConfig, }, pm.packagesLock.RUnlock } diff --git a/internal/arduino/cores/packagemanager/package_manager_test.go b/internal/arduino/cores/packagemanager/package_manager_test.go index 055a6d332d0..595d08d0c21 100644 --- a/internal/arduino/cores/packagemanager/package_manager_test.go +++ b/internal/arduino/cores/packagemanager/package_manager_test.go @@ -24,10 +24,10 @@ import ( "testing" "github.com/arduino/arduino-cli/internal/arduino/cores" - "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/arduino/go-paths-helper" "github.com/arduino/go-properties-orderedmap" "github.com/stretchr/testify/require" + "go.bug.st/downloader/v2" semver "go.bug.st/relaxed-semver" ) @@ -38,7 +38,7 @@ var dataDir1 = paths.New("testdata", "data_dir_1") var extraHardware = paths.New("testdata", "extra_hardware") func TestFindBoardWithFQBN(t *testing.T) { - pmb := NewBuilder(customHardware, customHardware, customHardware, customHardware, "test") + pmb := NewBuilder(customHardware, customHardware, nil, customHardware, customHardware, "test", downloader.GetDefaultConfig()) pmb.LoadHardwareFromDirectory(customHardware) pm := pmb.Build() pme, release := pm.NewExplorer() @@ -56,7 +56,7 @@ func TestFindBoardWithFQBN(t *testing.T) { func TestResolveFQBN(t *testing.T) { // Pass nil, since these paths are only used for installing - pmb := NewBuilder(nil, nil, nil, nil, "test") + pmb := NewBuilder(nil, nil, nil, nil, nil, "test", downloader.GetDefaultConfig()) // Hardware from main packages directory pmb.LoadHardwareFromDirectory(dataDir1.Join("packages")) // This contains the arduino:avr core @@ -341,7 +341,7 @@ func TestResolveFQBN(t *testing.T) { } func TestBoardOptionsFunctions(t *testing.T) { - pmb := NewBuilder(customHardware, customHardware, customHardware, customHardware, "test") + pmb := NewBuilder(customHardware, customHardware, nil, customHardware, customHardware, "test", downloader.GetDefaultConfig()) pmb.LoadHardwareFromDirectory(customHardware) pm := pmb.Build() pme, release := pm.NewExplorer() @@ -381,7 +381,7 @@ func TestBoardOptionsFunctions(t *testing.T) { } func TestBoardOrdering(t *testing.T) { - pmb := NewBuilder(dataDir1, dataDir1.Join("packages"), nil, nil, "") + pmb := NewBuilder(dataDir1, dataDir1.Join("packages"), nil, nil, nil, "", downloader.GetDefaultConfig()) _ = pmb.LoadHardwareFromDirectories(paths.NewPathList(dataDir1.Join("packages").String())) pm := pmb.Build() pme, release := pm.NewExplorer() @@ -432,13 +432,14 @@ func TestBoardOrdering(t *testing.T) { func TestFindToolsRequiredForBoard(t *testing.T) { t.Setenv("ARDUINO_DATA_DIR", dataDir1.String()) - configuration.Settings = configuration.Init("") pmb := NewBuilder( dataDir1, - configuration.PackagesDir(configuration.Settings), - configuration.DownloadsDir(configuration.Settings), + dataDir1.Join("packages"), + nil, + dataDir1.Join("staging"), dataDir1, "test", + downloader.GetDefaultConfig(), ) loadIndex := func(addr string) { @@ -567,7 +568,7 @@ func TestFindToolsRequiredForBoard(t *testing.T) { } func TestIdentifyBoard(t *testing.T) { - pmb := NewBuilder(customHardware, customHardware, customHardware, customHardware, "test") + pmb := NewBuilder(customHardware, customHardware, nil, customHardware, customHardware, "test", downloader.GetDefaultConfig()) pmb.LoadHardwareFromDirectory(customHardware) pm := pmb.Build() pme, release := pm.NewExplorer() @@ -594,12 +595,12 @@ func TestIdentifyBoard(t *testing.T) { func TestPackageManagerClear(t *testing.T) { // Create a PackageManager and load the harware - pmb := NewBuilder(customHardware, customHardware, customHardware, customHardware, "test") + pmb := NewBuilder(customHardware, customHardware, nil, customHardware, customHardware, "test", downloader.GetDefaultConfig()) pmb.LoadHardwareFromDirectory(customHardware) pm := pmb.Build() // Creates another PackageManager but don't load the hardware - emptyPmb := NewBuilder(customHardware, customHardware, customHardware, customHardware, "test") + emptyPmb := NewBuilder(customHardware, customHardware, nil, customHardware, customHardware, "test", downloader.GetDefaultConfig()) emptyPm := emptyPmb.Build() // Verifies they're not equal @@ -621,7 +622,7 @@ func TestFindToolsRequiredFromPlatformRelease(t *testing.T) { require.NoError(t, err) defer fakePath.RemoveAll() - pmb := NewBuilder(fakePath, fakePath, fakePath, fakePath, "test") + pmb := NewBuilder(fakePath, fakePath, nil, fakePath, fakePath, "test", downloader.GetDefaultConfig()) pack := pmb.GetOrCreatePackage("arduino") { @@ -742,7 +743,7 @@ func TestFindToolsRequiredFromPlatformRelease(t *testing.T) { } func TestFindPlatformReleaseDependencies(t *testing.T) { - pmb := NewBuilder(nil, nil, nil, nil, "test") + pmb := NewBuilder(nil, nil, nil, nil, nil, "test", downloader.GetDefaultConfig()) pmb.LoadPackageIndexFromFile(paths.New("testdata", "package_tooltest_index.json")) pmb.calculateCompatibleReleases() pm := pmb.Build() @@ -758,7 +759,7 @@ func TestFindPlatformReleaseDependencies(t *testing.T) { func TestLegacyPackageConversionToPluggableDiscovery(t *testing.T) { // Pass nil, since these paths are only used for installing - pmb := NewBuilder(nil, nil, nil, nil, "test") + pmb := NewBuilder(nil, nil, nil, nil, nil, "test", downloader.GetDefaultConfig()) // Hardware from main packages directory pmb.LoadHardwareFromDirectory(dataDir1.Join("packages")) pm := pmb.Build() @@ -828,7 +829,7 @@ func TestLegacyPackageConversionToPluggableDiscovery(t *testing.T) { func TestVariantAndCoreSelection(t *testing.T) { // Pass nil, since these paths are only used for installing - pmb := NewBuilder(nil, nil, nil, nil, "test") + pmb := NewBuilder(nil, nil, nil, nil, nil, "test", downloader.GetDefaultConfig()) // Hardware from main packages directory pmb.LoadHardwareFromDirectory(dataDir1.Join("packages")) pm := pmb.Build() @@ -923,7 +924,7 @@ func TestVariantAndCoreSelection(t *testing.T) { } func TestRunScript(t *testing.T) { - pmb := NewBuilder(nil, nil, nil, nil, "test") + pmb := NewBuilder(nil, nil, nil, nil, nil, "test", downloader.GetDefaultConfig()) pm := pmb.Build() pme, release := pm.NewExplorer() defer release() diff --git a/internal/arduino/cores/packagemanager/profiles.go b/internal/arduino/cores/packagemanager/profiles.go index 2422766e899..9d3bfba047a 100644 --- a/internal/arduino/cores/packagemanager/profiles.go +++ b/internal/arduino/cores/packagemanager/profiles.go @@ -16,6 +16,7 @@ package packagemanager import ( + "context" "fmt" "net/url" @@ -32,7 +33,7 @@ import ( // LoadHardwareForProfile load the hardware platforms for the given profile. // If installMissing is true then possibly missing tools and platforms will be downloaded and installed. -func (pmb *Builder) LoadHardwareForProfile(p *sketch.Profile, installMissing bool, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) []error { +func (pmb *Builder) LoadHardwareForProfile(ctx context.Context, p *sketch.Profile, installMissing bool, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB, settings *configuration.Settings) []error { pmb.profile = p // Load required platforms @@ -40,7 +41,7 @@ func (pmb *Builder) LoadHardwareForProfile(p *sketch.Profile, installMissing boo var platformReleases []*cores.PlatformRelease indexURLs := map[string]*url.URL{} for _, platformRef := range p.Platforms { - if platformRelease, err := pmb.loadProfilePlatform(platformRef, installMissing, downloadCB, taskCB); err != nil { + if platformRelease, err := pmb.loadProfilePlatform(ctx, platformRef, installMissing, downloadCB, taskCB, settings); err != nil { merr = append(merr, fmt.Errorf("%s: %w", tr("loading required platform %s", platformRef), err)) logrus.WithField("platform", platformRef).WithError(err).Debugf("Error loading platform for profile") } else { @@ -56,7 +57,7 @@ func (pmb *Builder) LoadHardwareForProfile(p *sketch.Profile, installMissing boo for _, toolDep := range platformRelease.ToolDependencies { indexURL := indexURLs[toolDep.ToolPackager] - if err := pmb.loadProfileTool(toolDep, indexURL, installMissing, downloadCB, taskCB); err != nil { + if err := pmb.loadProfileTool(toolDep, indexURL, installMissing, downloadCB, taskCB, settings); err != nil { merr = append(merr, fmt.Errorf("%s: %w", tr("loading required tool %s", toolDep), err)) logrus.WithField("tool", toolDep).WithField("index_url", indexURL).WithError(err).Debugf("Error loading tool for profile") } else { @@ -68,30 +69,30 @@ func (pmb *Builder) LoadHardwareForProfile(p *sketch.Profile, installMissing boo return merr } -func (pmb *Builder) loadProfilePlatform(platformRef *sketch.ProfilePlatformReference, installMissing bool, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) (*cores.PlatformRelease, error) { +func (pmb *Builder) loadProfilePlatform(ctx context.Context, platformRef *sketch.ProfilePlatformReference, installMissing bool, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB, settings *configuration.Settings) (*cores.PlatformRelease, error) { targetPackage := pmb.packages.GetOrCreatePackage(platformRef.Packager) platform := targetPackage.GetOrCreatePlatform(platformRef.Architecture) release := platform.GetOrCreateRelease(platformRef.Version) uid := platformRef.InternalUniqueIdentifier() - destDir := configuration.ProfilesCacheDir(configuration.Settings).Join(uid) + destDir := settings.ProfilesCacheDir().Join(uid) if !destDir.IsDir() && installMissing { // Try installing the missing platform - if err := pmb.installMissingProfilePlatform(platformRef, destDir, downloadCB, taskCB); err != nil { + if err := pmb.installMissingProfilePlatform(ctx, platformRef, destDir, downloadCB, taskCB); err != nil { return nil, err } } return release, pmb.loadPlatformRelease(release, destDir) } -func (pmb *Builder) installMissingProfilePlatform(platformRef *sketch.ProfilePlatformReference, destDir *paths.Path, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) error { +func (pmb *Builder) installMissingProfilePlatform(ctx context.Context, platformRef *sketch.ProfilePlatformReference, destDir *paths.Path, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) error { // Instantiate a temporary package manager only for platform installation _ = pmb.tempDir.MkdirAll() tmp, err := paths.MkTempDir(pmb.tempDir.String(), "") if err != nil { return fmt.Errorf("installing missing platform: could not create temp dir %s", err) } - tmpPmb := NewBuilder(tmp, tmp, pmb.DownloadDir, tmp, pmb.userAgent) + tmpPmb := NewBuilder(tmp, tmp, nil, pmb.DownloadDir, tmp, pmb.userAgent, pmb.downloaderConfig) defer tmp.RemoveAll() // Download the main index and parse it @@ -103,7 +104,7 @@ func (pmb *Builder) installMissingProfilePlatform(platformRef *sketch.ProfilePla } for _, indexURL := range indexesToDownload { indexResource := resources.IndexResource{URL: indexURL} - if err := indexResource.Download(tmpPmb.IndexDir, downloadCB); err != nil { + if err := indexResource.Download(ctx, tmpPmb.IndexDir, downloadCB, pmb.downloaderConfig); err != nil { taskCB(&rpc.TaskProgress{Name: tr("Error downloading %s", indexURL)}) return &cmderrors.FailedDownloadError{Message: tr("Error downloading %s", indexURL), Cause: err} } @@ -121,7 +122,7 @@ func (pmb *Builder) installMissingProfilePlatform(platformRef *sketch.ProfilePla tmpPme, tmpRelease := tmpPm.NewExplorer() defer tmpRelease() - if err := tmpPme.DownloadPlatformRelease(tmpPlatformRelease, nil, downloadCB); err != nil { + if err := tmpPme.DownloadPlatformRelease(tmpPlatformRelease, downloadCB); err != nil { taskCB(&rpc.TaskProgress{Name: tr("Error downloading platform %s", tmpPlatformRelease)}) return &cmderrors.FailedInstallError{Message: tr("Error downloading platform %s", tmpPlatformRelease), Cause: err} } @@ -137,12 +138,12 @@ func (pmb *Builder) installMissingProfilePlatform(platformRef *sketch.ProfilePla return nil } -func (pmb *Builder) loadProfileTool(toolRef *cores.ToolDependency, indexURL *url.URL, installMissing bool, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) error { +func (pmb *Builder) loadProfileTool(toolRef *cores.ToolDependency, indexURL *url.URL, installMissing bool, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB, settings *configuration.Settings) error { targetPackage := pmb.packages.GetOrCreatePackage(toolRef.ToolPackager) tool := targetPackage.GetOrCreateTool(toolRef.ToolName) uid := toolRef.InternalUniqueIdentifier(indexURL) - destDir := configuration.ProfilesCacheDir(configuration.Settings).Join(uid) + destDir := settings.ProfilesCacheDir().Join(uid) if !destDir.IsDir() && installMissing { // Try installing the missing tool @@ -172,7 +173,7 @@ func (pmb *Builder) installMissingProfileTool(toolRelease *cores.ToolRelease, de return &cmderrors.InvalidVersionError{Cause: fmt.Errorf(tr("version %s not available for this operating system", toolRelease))} } taskCB(&rpc.TaskProgress{Name: tr("Downloading tool %s", toolRelease)}) - if err := toolResource.Download(pmb.DownloadDir, nil, toolRelease.String(), downloadCB, ""); err != nil { + if err := toolResource.Download(pmb.DownloadDir, pmb.downloaderConfig, toolRelease.String(), downloadCB, ""); err != nil { taskCB(&rpc.TaskProgress{Name: tr("Error downloading tool %s", toolRelease)}) return &cmderrors.FailedInstallError{Message: tr("Error installing tool %s", toolRelease), Cause: err} } diff --git a/internal/arduino/httpclient/httpclient.go b/internal/arduino/httpclient/httpclient.go index ec4b4acc4a6..0e904dddf8f 100644 --- a/internal/arduino/httpclient/httpclient.go +++ b/internal/arduino/httpclient/httpclient.go @@ -16,12 +16,9 @@ package httpclient import ( - "net/http" - "net/url" "time" "github.com/arduino/arduino-cli/commands/cmderrors" - "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/arduino/arduino-cli/internal/i18n" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/go-paths-helper" @@ -34,7 +31,7 @@ var tr = i18n.Tr // DownloadFile downloads a file from a URL into the specified path. An optional config and options may be passed (or nil to use the defaults). // A DownloadProgressCB callback function must be passed to monitor download progress. // If a not empty queryParameter is passed, it is appended to the URL for analysis purposes. -func DownloadFile(path *paths.Path, URL string, queryParameter string, label string, downloadCB rpc.DownloadProgressCB, config *downloader.Config, options ...downloader.DownloadOptions) (returnedError error) { +func DownloadFile(path *paths.Path, URL string, queryParameter string, label string, downloadCB rpc.DownloadProgressCB, config downloader.Config, options ...downloader.DownloadOptions) (returnedError error) { if queryParameter != "" { URL = URL + "?query=" + queryParameter } @@ -48,15 +45,7 @@ func DownloadFile(path *paths.Path, URL string, queryParameter string, label str } }() - if config == nil { - c, err := GetDownloaderConfig() - if err != nil { - return err - } - config = c - } - - d, err := downloader.DownloadWithConfig(path.String(), URL, *config, options...) + d, err := downloader.DownloadWithConfig(path.String(), URL, config, options...) if err != nil { return err } @@ -76,52 +65,3 @@ func DownloadFile(path *paths.Path, URL string, queryParameter string, label str return nil } - -// Config is the configuration of the http client -type Config struct { - UserAgent string - Proxy *url.URL -} - -// New returns a default http client for use in the arduino-cli -func New() (*http.Client, error) { - userAgent := configuration.UserAgent(configuration.Settings) - proxy, err := configuration.NetworkProxy(configuration.Settings) - if err != nil { - return nil, err - } - return NewWithConfig(&Config{UserAgent: userAgent, Proxy: proxy}), nil -} - -// NewWithConfig creates a http client for use in the arduino-cli, with a given configuration -func NewWithConfig(config *Config) *http.Client { - return &http.Client{ - Transport: &httpClientRoundTripper{ - transport: &http.Transport{ - Proxy: http.ProxyURL(config.Proxy), - }, - userAgent: config.UserAgent, - }, - } -} - -// GetDownloaderConfig returns the downloader configuration based on current settings. -func GetDownloaderConfig() (*downloader.Config, error) { - httpClient, err := New() - if err != nil { - return nil, &cmderrors.InvalidArgumentError{Message: tr("Could not connect via HTTP"), Cause: err} - } - return &downloader.Config{ - HttpClient: *httpClient, - }, nil -} - -type httpClientRoundTripper struct { - transport http.RoundTripper - userAgent string -} - -func (h *httpClientRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { - req.Header.Add("User-Agent", h.userAgent) - return h.transport.RoundTrip(req) -} diff --git a/internal/arduino/resources/download.go b/internal/arduino/resources/download.go index 4f1df1ad5b3..19b37df48ac 100644 --- a/internal/arduino/resources/download.go +++ b/internal/arduino/resources/download.go @@ -28,7 +28,7 @@ import ( // Download performs a download loop using the provided downloader.Config. // Messages are passed back to the DownloadProgressCB using label as text for the File field. // queryParameter is passed for analysis purposes. -func (r *DownloadResource) Download(downloadDir *paths.Path, config *downloader.Config, label string, downloadCB rpc.DownloadProgressCB, queryParameter string) error { +func (r *DownloadResource) Download(downloadDir *paths.Path, config downloader.Config, label string, downloadCB rpc.DownloadProgressCB, queryParameter string) error { path, err := r.ArchivePath(downloadDir) if err != nil { return fmt.Errorf(tr("getting archive path: %s"), err) diff --git a/internal/arduino/resources/helpers_test.go b/internal/arduino/resources/helpers_test.go index e56747d8b40..75961bec26f 100644 --- a/internal/arduino/resources/helpers_test.go +++ b/internal/arduino/resources/helpers_test.go @@ -22,11 +22,10 @@ import ( "strings" "testing" - "github.com/arduino/arduino-cli/internal/arduino/httpclient" + "github.com/arduino/arduino-cli/internal/cli/configuration" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/go-paths-helper" "github.com/stretchr/testify/require" - "go.bug.st/downloader/v2" ) type EchoHandler struct{} @@ -37,8 +36,7 @@ func (h *EchoHandler) ServeHTTP(writer http.ResponseWriter, request *http.Reques } func TestDownloadApplyUserAgentHeaderUsingConfig(t *testing.T) { - goldUserAgentValue := "arduino-cli/0.0.0-test.preview (amd64; linux; go1.12.4) Commit:deadbeef/Build:2019-06-12 11:11:11.111" - goldUserAgentString := "User-Agent: " + goldUserAgentValue + goldUserAgentValue := "arduino-cli/0.0.0-test.preview" tmp, err := paths.MkTempDir("", "") require.NoError(t, err) @@ -54,9 +52,11 @@ func TestDownloadApplyUserAgentHeaderUsingConfig(t *testing.T) { URL: srv.URL, } - httpClient := httpclient.NewWithConfig(&httpclient.Config{UserAgent: goldUserAgentValue}) - - err = r.Download(tmp, &downloader.Config{HttpClient: *httpClient}, "", func(progress *rpc.DownloadProgress) {}, "") + settings := configuration.NewSettings() + settings.Set("network.user_agent_ext", goldUserAgentValue) + config, err := settings.DownloaderConfig() + require.NoError(t, err) + err = r.Download(tmp, config, "", func(progress *rpc.DownloadProgress) {}, "") require.NoError(t, err) // leverage the download helper to download the echo for the request made by the downloader itself @@ -71,12 +71,11 @@ func TestDownloadApplyUserAgentHeaderUsingConfig(t *testing.T) { require.NoError(t, err) requestLines := strings.Split(string(b), "\r\n") - userAgentHeaderString := "" + userAgentHeader := "" for _, line := range requestLines { if strings.Contains(line, "User-Agent: ") { - userAgentHeaderString = line + userAgentHeader = line } } - require.Equal(t, goldUserAgentString, userAgentHeaderString) - + require.Contains(t, userAgentHeader, goldUserAgentValue) } diff --git a/internal/arduino/resources/index.go b/internal/arduino/resources/index.go index 4740c6f12f0..e9198905e5e 100644 --- a/internal/arduino/resources/index.go +++ b/internal/arduino/resources/index.go @@ -58,7 +58,7 @@ func (res *IndexResource) IndexFileName() (string, error) { // Download will download the index and possibly check the signature using the Arduino's public key. // If the file is in .gz format it will be unpacked first. -func (res *IndexResource) Download(destDir *paths.Path, downloadCB rpc.DownloadProgressCB) error { +func (res *IndexResource) Download(ctx context.Context, destDir *paths.Path, downloadCB rpc.DownloadProgressCB, config downloader.Config) error { // Create destination directory if err := destDir.MkdirAll(); err != nil { return &cmderrors.PermissionDeniedError{Message: tr("Can't create data directory %s", destDir), Cause: err} @@ -78,7 +78,7 @@ func (res *IndexResource) Download(destDir *paths.Path, downloadCB rpc.DownloadP return err } tmpIndexPath := tmp.Join(downloadFileName) - if err := httpclient.DownloadFile(tmpIndexPath, res.URL.String(), "", tr("Downloading index: %s", downloadFileName), downloadCB, nil, downloader.NoResume); err != nil { + if err := httpclient.DownloadFile(tmpIndexPath, res.URL.String(), "", tr("Downloading index: %s", downloadFileName), downloadCB, config, downloader.NoResume); err != nil { return &cmderrors.FailedDownloadError{Message: tr("Error downloading index '%s'", res.URL), Cause: err} } @@ -100,7 +100,7 @@ func (res *IndexResource) Download(destDir *paths.Path, downloadCB rpc.DownloadP defer f.Close() tmpArchivePath := tmp.Join("archive") _ = tmpArchivePath.MkdirAll() - if err := extract.Bz2(context.Background(), f, tmpArchivePath.String(), nil); err != nil { + if err := extract.Bz2(ctx, f, tmpArchivePath.String(), nil); err != nil { return &cmderrors.PermissionDeniedError{Message: tr("Error extracting %s", tmpIndexPath), Cause: err} } @@ -133,7 +133,7 @@ func (res *IndexResource) Download(destDir *paths.Path, downloadCB rpc.DownloadP // Download signature signaturePath = destDir.Join(signatureFileName) tmpSignaturePath = tmp.Join(signatureFileName) - if err := httpclient.DownloadFile(tmpSignaturePath, res.SignatureURL.String(), "", tr("Downloading index signature: %s", signatureFileName), downloadCB, nil, downloader.NoResume); err != nil { + if err := httpclient.DownloadFile(tmpSignaturePath, res.SignatureURL.String(), "", tr("Downloading index signature: %s", signatureFileName), downloadCB, config, downloader.NoResume); err != nil { return &cmderrors.FailedDownloadError{Message: tr("Error downloading index signature '%s'", res.SignatureURL), Cause: err} } diff --git a/internal/arduino/resources/resources_test.go b/internal/arduino/resources/resources_test.go index 9cf8ae54509..3e218c1f89d 100644 --- a/internal/arduino/resources/resources_test.go +++ b/internal/arduino/resources/resources_test.go @@ -16,6 +16,7 @@ package resources import ( + "context" "crypto" "encoding/hex" "fmt" @@ -49,7 +50,7 @@ func TestDownloadAndChecksums(t *testing.T) { require.NoError(t, err) downloadAndTestChecksum := func() { - err := r.Download(tmp, &downloader.Config{}, "", func(*rpc.DownloadProgress) {}, "") + err := r.Download(tmp, downloader.Config{}, "", func(*rpc.DownloadProgress) {}, "") require.NoError(t, err) data, err := testFile.ReadFile() @@ -63,7 +64,7 @@ func TestDownloadAndChecksums(t *testing.T) { downloadAndTestChecksum() // Download with cached file - err = r.Download(tmp, &downloader.Config{}, "", func(*rpc.DownloadProgress) {}, "") + err = r.Download(tmp, downloader.Config{}, "", func(*rpc.DownloadProgress) {}, "") require.NoError(t, err) // Download if cached file has data in excess (redownload) @@ -116,6 +117,7 @@ func TestDownloadAndChecksums(t *testing.T) { } func TestIndexDownloadAndSignatureWithinArchive(t *testing.T) { + ctx := context.Background() // Spawn test webserver mux := http.NewServeMux() fs := http.FileServer(http.Dir("testdata")) @@ -132,7 +134,7 @@ func TestIndexDownloadAndSignatureWithinArchive(t *testing.T) { destDir, err := paths.MkTempDir("", "") require.NoError(t, err) defer destDir.RemoveAll() - err = idxResource.Download(destDir, func(curr *rpc.DownloadProgress) {}) + err = idxResource.Download(ctx, destDir, func(curr *rpc.DownloadProgress) {}, downloader.GetDefaultConfig()) require.NoError(t, err) require.True(t, destDir.Join("package_index.json").Exist()) require.True(t, destDir.Join("package_index.json.sig").Exist()) @@ -143,7 +145,7 @@ func TestIndexDownloadAndSignatureWithinArchive(t *testing.T) { invDestDir, err := paths.MkTempDir("", "") require.NoError(t, err) defer invDestDir.RemoveAll() - err = invIdxResource.Download(invDestDir, func(curr *rpc.DownloadProgress) {}) + err = invIdxResource.Download(ctx, invDestDir, func(curr *rpc.DownloadProgress) {}, downloader.GetDefaultConfig()) require.Error(t, err) require.Contains(t, err.Error(), "invalid signature") require.False(t, invDestDir.Join("package_index.json").Exist()) diff --git a/internal/cli/arguments/completion.go b/internal/cli/arguments/completion.go index 4243a0fd409..a8867c51cf2 100644 --- a/internal/cli/arguments/completion.go +++ b/internal/cli/arguments/completion.go @@ -18,10 +18,7 @@ package arguments import ( "context" - "github.com/arduino/arduino-cli/commands/board" - "github.com/arduino/arduino-cli/commands/core" - "github.com/arduino/arduino-cli/commands/lib" - "github.com/arduino/arduino-cli/commands/upload" + f "github.com/arduino/arduino-cli/internal/algorithms" "github.com/arduino/arduino-cli/internal/cli/instance" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" ) @@ -29,10 +26,10 @@ import ( // GetInstalledBoards is an helper function useful to autocomplete. // It returns a list of fqbn // it's taken from cli/board/listall.go -func GetInstalledBoards() []string { - inst := instance.CreateAndInit() +func GetInstalledBoards(ctx context.Context, srv rpc.ArduinoCoreServiceServer) []string { + inst := instance.CreateAndInit(ctx, srv) - list, _ := board.ListAll(context.Background(), &rpc.BoardListAllRequest{ + list, _ := srv.BoardListAll(ctx, &rpc.BoardListAllRequest{ Instance: inst, SearchArgs: nil, IncludeHiddenBoards: false, @@ -47,8 +44,8 @@ func GetInstalledBoards() []string { // GetInstalledProgrammers is an helper function useful to autocomplete. // It returns a list of programmers available based on the installed boards -func GetInstalledProgrammers() []string { - inst := instance.CreateAndInit() +func GetInstalledProgrammers(ctx context.Context, srv rpc.ArduinoCoreServiceServer) []string { + inst := instance.CreateAndInit(ctx, srv) // we need the list of the available fqbn in order to get the list of the programmers listAllReq := &rpc.BoardListAllRequest{ @@ -56,11 +53,11 @@ func GetInstalledProgrammers() []string { SearchArgs: nil, IncludeHiddenBoards: false, } - list, _ := board.ListAll(context.Background(), listAllReq) + list, _ := srv.BoardListAll(ctx, listAllReq) installedProgrammers := make(map[string]string) for _, board := range list.GetBoards() { - programmers, _ := upload.ListProgrammersAvailableForUpload(context.Background(), &rpc.ListProgrammersAvailableForUploadRequest{ + programmers, _ := srv.ListProgrammersAvailableForUpload(ctx, &rpc.ListProgrammersAvailableForUploadRequest{ Instance: inst, Fqbn: board.GetFqbn(), }) @@ -80,10 +77,10 @@ func GetInstalledProgrammers() []string { // GetUninstallableCores is an helper function useful to autocomplete. // It returns a list of cores which can be uninstalled -func GetUninstallableCores() []string { - inst := instance.CreateAndInit() +func GetUninstallableCores(ctx context.Context, srv rpc.ArduinoCoreServiceServer) []string { + inst := instance.CreateAndInit(ctx, srv) - platforms, _ := core.PlatformSearch(&rpc.PlatformSearchRequest{ + platforms, _ := srv.PlatformSearch(ctx, &rpc.PlatformSearchRequest{ Instance: inst, ManuallyInstalled: true, }) @@ -101,10 +98,10 @@ func GetUninstallableCores() []string { // GetInstallableCores is an helper function useful to autocomplete. // It returns a list of cores which can be installed/downloaded -func GetInstallableCores() []string { - inst := instance.CreateAndInit() +func GetInstallableCores(ctx context.Context, srv rpc.ArduinoCoreServiceServer) []string { + inst := instance.CreateAndInit(ctx, srv) - platforms, _ := core.PlatformSearch(&rpc.PlatformSearchRequest{ + platforms, _ := srv.PlatformSearch(ctx, &rpc.PlatformSearchRequest{ Instance: inst, SearchArgs: "", }) @@ -120,19 +117,19 @@ func GetInstallableCores() []string { // GetInstalledLibraries is an helper function useful to autocomplete. // It returns a list of libs which are currently installed, including the builtin ones -func GetInstalledLibraries() []string { - return getLibraries(true) +func GetInstalledLibraries(ctx context.Context, srv rpc.ArduinoCoreServiceServer) []string { + return getLibraries(ctx, srv, true) } // GetUninstallableLibraries is an helper function useful to autocomplete. // It returns a list of libs which can be uninstalled -func GetUninstallableLibraries() []string { - return getLibraries(false) +func GetUninstallableLibraries(ctx context.Context, srv rpc.ArduinoCoreServiceServer) []string { + return getLibraries(ctx, srv, false) } -func getLibraries(all bool) []string { - inst := instance.CreateAndInit() - libs, _ := lib.LibraryList(context.Background(), &rpc.LibraryListRequest{ +func getLibraries(ctx context.Context, srv rpc.ArduinoCoreServiceServer, all bool) []string { + inst := instance.CreateAndInit(ctx, srv) + libs, _ := srv.LibraryList(ctx, &rpc.LibraryListRequest{ Instance: inst, All: all, Updatable: false, @@ -149,10 +146,10 @@ func getLibraries(all bool) []string { // GetInstallableLibs is an helper function useful to autocomplete. // It returns a list of libs which can be installed/downloaded -func GetInstallableLibs() []string { - inst := instance.CreateAndInit() +func GetInstallableLibs(ctx context.Context, srv rpc.ArduinoCoreServiceServer) []string { + inst := instance.CreateAndInit(ctx, srv) - libs, _ := lib.LibrarySearch(context.Background(), &rpc.LibrarySearchRequest{ + libs, _ := srv.LibrarySearch(ctx, &rpc.LibrarySearchRequest{ Instance: inst, SearchArgs: "", // if no query is specified all the libs are returned }) @@ -167,16 +164,11 @@ func GetInstallableLibs() []string { // GetAvailablePorts is an helper function useful to autocomplete. // It returns a list of upload port of the boards which are currently connected. // It will not suggests network ports because the timeout is not set. -func GetAvailablePorts() []*rpc.Port { - inst := instance.CreateAndInit() +func GetAvailablePorts(ctx context.Context, srv rpc.ArduinoCoreServiceServer) []*rpc.Port { + // Get the port list + inst := instance.CreateAndInit(ctx, srv) + list, _ := srv.BoardList(ctx, &rpc.BoardListRequest{Instance: inst}) - list, _, _ := board.List(&rpc.BoardListRequest{ - Instance: inst, - }) - var res []*rpc.Port - // transform the data structure for the completion - for _, i := range list { - res = append(res, i.GetPort()) - } - return res + // Transform the data structure for the completion (DetectedPort -> Port) + return f.Map(list.GetPorts(), (*rpc.DetectedPort).GetPort) } diff --git a/internal/cli/arguments/fqbn.go b/internal/cli/arguments/fqbn.go index 01e1079d8ab..e1cb338c77d 100644 --- a/internal/cli/arguments/fqbn.go +++ b/internal/cli/arguments/fqbn.go @@ -16,6 +16,7 @@ package arguments import ( + "context" "strings" "github.com/arduino/arduino-cli/commands/cmderrors" @@ -33,10 +34,10 @@ type Fqbn struct { } // AddToCommand adds the flags used to set fqbn to the specified Command -func (f *Fqbn) AddToCommand(cmd *cobra.Command) { +func (f *Fqbn) AddToCommand(cmd *cobra.Command, srv rpc.ArduinoCoreServiceServer) { cmd.Flags().StringVarP(&f.fqbn, "fqbn", "b", "", tr("Fully Qualified Board Name, e.g.: arduino:avr:uno")) cmd.RegisterFlagCompletionFunc("fqbn", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return GetInstalledBoards(), cobra.ShellCompDirectiveDefault + return GetInstalledBoards(cmd.Context(), srv), cobra.ShellCompDirectiveDefault }) cmd.Flags().StringSliceVar(&f.boardOptions, "board-options", []string{}, tr("List of board options separated by commas. Or can be used multiple times for multiple options.")) @@ -69,7 +70,7 @@ func (f *Fqbn) Set(fqbn string) { // - the port is not found, in this case nil is returned // - the FQBN autodetection fail, in this case the function prints an error and // terminates the execution -func CalculateFQBNAndPort(portArgs *Port, fqbnArg *Fqbn, instance *rpc.Instance, defaultFQBN, defaultAddress, defaultProtocol string) (string, *rpc.Port) { +func CalculateFQBNAndPort(ctx context.Context, portArgs *Port, fqbnArg *Fqbn, instance *rpc.Instance, srv rpc.ArduinoCoreServiceServer, defaultFQBN, defaultAddress, defaultProtocol string) (string, *rpc.Port) { fqbn := fqbnArg.String() if fqbn == "" { fqbn = defaultFQBN @@ -78,14 +79,14 @@ func CalculateFQBNAndPort(portArgs *Port, fqbnArg *Fqbn, instance *rpc.Instance, if portArgs == nil || portArgs.address == "" { feedback.FatalError(&cmderrors.MissingFQBNError{}, feedback.ErrGeneric) } - fqbn, port := portArgs.DetectFQBN(instance) + fqbn, port := portArgs.DetectFQBN(ctx, instance, srv) if fqbn == "" { feedback.FatalError(&cmderrors.MissingFQBNError{}, feedback.ErrGeneric) } return fqbn, port } - port, err := portArgs.GetPort(instance, defaultAddress, defaultProtocol) + port, err := portArgs.GetPort(ctx, instance, srv, defaultAddress, defaultProtocol) if err != nil { feedback.Fatal(tr("Error getting port metadata: %v", err), feedback.ErrGeneric) } diff --git a/internal/cli/arguments/port.go b/internal/cli/arguments/port.go index f10c6bf70a0..1d138043fbc 100644 --- a/internal/cli/arguments/port.go +++ b/internal/cli/arguments/port.go @@ -20,7 +20,7 @@ import ( "fmt" "time" - "github.com/arduino/arduino-cli/commands/board" + "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/commands/cmderrors" f "github.com/arduino/arduino-cli/internal/algorithms" "github.com/arduino/arduino-cli/internal/cli/feedback" @@ -39,14 +39,14 @@ type Port struct { } // AddToCommand adds the flags used to set port and protocol to the specified Command -func (p *Port) AddToCommand(cmd *cobra.Command) { +func (p *Port) AddToCommand(cmd *cobra.Command, srv rpc.ArduinoCoreServiceServer) { cmd.Flags().StringVarP(&p.address, "port", "p", "", tr("Upload port address, e.g.: COM3 or /dev/ttyACM2")) cmd.RegisterFlagCompletionFunc("port", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return f.Map(GetAvailablePorts(), (*rpc.Port).GetAddress), cobra.ShellCompDirectiveDefault + return f.Map(GetAvailablePorts(cmd.Context(), srv), (*rpc.Port).GetAddress), cobra.ShellCompDirectiveDefault }) cmd.Flags().StringVarP(&p.protocol, "protocol", "l", "", tr("Upload port protocol, e.g: serial")) cmd.RegisterFlagCompletionFunc("protocol", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return f.Map(GetAvailablePorts(), (*rpc.Port).GetProtocol), cobra.ShellCompDirectiveDefault + return f.Map(GetAvailablePorts(cmd.Context(), srv), (*rpc.Port).GetProtocol), cobra.ShellCompDirectiveDefault }) p.timeout.AddToCommand(cmd) } @@ -56,12 +56,12 @@ func (p *Port) AddToCommand(cmd *cobra.Command) { // This method allows will bypass the discoveries if: // - a nil instance is passed: in this case the plain port and protocol arguments are returned (even if empty) // - a protocol is specified: in this case the discoveries are not needed to autodetect the protocol. -func (p *Port) GetPortAddressAndProtocol(instance *rpc.Instance, defaultAddress, defaultProtocol string) (string, string, error) { +func (p *Port) GetPortAddressAndProtocol(ctx context.Context, instance *rpc.Instance, srv rpc.ArduinoCoreServiceServer, defaultAddress, defaultProtocol string) (string, string, error) { if p.protocol != "" || instance == nil { return p.address, p.protocol, nil } - port, err := p.GetPort(instance, defaultAddress, defaultProtocol) + port, err := p.GetPort(ctx, instance, srv, defaultAddress, defaultProtocol) if err != nil { return "", "", err } @@ -70,8 +70,7 @@ func (p *Port) GetPortAddressAndProtocol(instance *rpc.Instance, defaultAddress, // GetPort returns the Port obtained by parsing command line arguments. // The extra metadata for the ports is obtained using the pluggable discoveries. -func (p *Port) GetPort(instance *rpc.Instance, defaultAddress, defaultProtocol string) (*rpc.Port, error) { - +func (p *Port) GetPort(ctx context.Context, instance *rpc.Instance, srv rpc.ArduinoCoreServiceServer, defaultAddress, defaultProtocol string) (*rpc.Port, error) { address := p.address protocol := p.protocol if address == "" && (defaultAddress != "" || defaultProtocol != "") { @@ -89,9 +88,12 @@ func (p *Port) GetPort(instance *rpc.Instance, defaultAddress, defaultProtocol s } logrus.WithField("port", address).Tracef("Upload port") - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithCancel(ctx) defer cancel() - watcher, err := board.Watch(ctx, &rpc.BoardListWatchRequest{Instance: instance}) + + stream, watcher := commands.BoardListWatchProxyToChan(ctx) + err := srv.BoardListWatch(&rpc.BoardListWatchRequest{Instance: instance}, stream) + if err != nil { return nil, err } @@ -129,15 +131,15 @@ func (p *Port) GetSearchTimeout() time.Duration { // DetectFQBN tries to identify the board connected to the port and returns the // discovered Port object together with the FQBN. If the port does not match // exactly 1 board, -func (p *Port) DetectFQBN(inst *rpc.Instance) (string, *rpc.Port) { - detectedPorts, _, err := board.List(&rpc.BoardListRequest{ +func (p *Port) DetectFQBN(ctx context.Context, inst *rpc.Instance, srv rpc.ArduinoCoreServiceServer) (string, *rpc.Port) { + detectedPorts, err := srv.BoardList(ctx, &rpc.BoardListRequest{ Instance: inst, Timeout: p.timeout.Get().Milliseconds(), }) if err != nil { feedback.Fatal(tr("Error during FQBN detection: %v", err), feedback.ErrGeneric) } - for _, detectedPort := range detectedPorts { + for _, detectedPort := range detectedPorts.GetPorts() { port := detectedPort.GetPort() if p.address != port.GetAddress() { continue diff --git a/internal/cli/arguments/profiles.go b/internal/cli/arguments/profiles.go index 6516cbdec08..59d56656fa6 100644 --- a/internal/cli/arguments/profiles.go +++ b/internal/cli/arguments/profiles.go @@ -15,7 +15,10 @@ package arguments -import "github.com/spf13/cobra" +import ( + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" + "github.com/spf13/cobra" +) // Profile contains the profile flag data. // This is useful so all flags used by commands that need @@ -25,14 +28,14 @@ type Profile struct { } // AddToCommand adds the flags used to set fqbn to the specified Command -func (f *Profile) AddToCommand(cmd *cobra.Command) { +func (f *Profile) AddToCommand(cmd *cobra.Command, srv rpc.ArduinoCoreServiceServer) { cmd.Flags().StringVarP(&f.profile, "profile", "m", "", tr("Sketch profile to use")) cmd.RegisterFlagCompletionFunc("profile", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { var sketchProfile string if len(args) > 0 { sketchProfile = args[0] } - return GetSketchProfiles(sketchProfile), cobra.ShellCompDirectiveDefault + return GetSketchProfiles(cmd.Context(), srv, sketchProfile), cobra.ShellCompDirectiveDefault }) } diff --git a/internal/cli/arguments/programmer.go b/internal/cli/arguments/programmer.go index a5267ef804e..6f590b85164 100644 --- a/internal/cli/arguments/programmer.go +++ b/internal/cli/arguments/programmer.go @@ -18,8 +18,7 @@ package arguments import ( "context" - "github.com/arduino/arduino-cli/commands/board" - "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/spf13/cobra" ) @@ -31,23 +30,23 @@ type Programmer struct { } // AddToCommand adds the flags used to set the programmer to the specified Command -func (p *Programmer) AddToCommand(cmd *cobra.Command) { +func (p *Programmer) AddToCommand(cmd *cobra.Command, srv rpc.ArduinoCoreServiceServer) { cmd.Flags().StringVarP(&p.programmer, "programmer", "P", "", tr("Programmer to use, e.g: atmel_ice")) cmd.RegisterFlagCompletionFunc("programmer", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return GetInstalledProgrammers(), cobra.ShellCompDirectiveDefault + return GetInstalledProgrammers(cmd.Context(), srv), cobra.ShellCompDirectiveDefault }) } // String returns the programmer specified by the user, or the default programmer // for the given board if defined. -func (p *Programmer) String(inst *commands.Instance, fqbn string) string { +func (p *Programmer) String(ctx context.Context, inst *rpc.Instance, srv rpc.ArduinoCoreServiceServer, fqbn string) string { if p.programmer != "" { return p.programmer } if inst == nil || fqbn == "" { return "" } - details, err := board.Details(context.Background(), &commands.BoardDetailsRequest{ + details, err := srv.BoardDetails(ctx, &rpc.BoardDetailsRequest{ Instance: inst, Fqbn: fqbn, }) diff --git a/internal/cli/arguments/reference.go b/internal/cli/arguments/reference.go index bf4e327fed1..613a16912fa 100644 --- a/internal/cli/arguments/reference.go +++ b/internal/cli/arguments/reference.go @@ -16,11 +16,11 @@ package arguments import ( + "context" "fmt" "strings" "github.com/arduino/arduino-cli/commands/cmderrors" - "github.com/arduino/arduino-cli/commands/core" "github.com/arduino/arduino-cli/internal/cli/instance" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/sirupsen/logrus" @@ -43,10 +43,11 @@ func (r *Reference) String() string { // ParseReferences is a convenient wrapper that operates on a slice of strings and // calls ParseReference for each of them. It returns at the first invalid argument. -func ParseReferences(args []string) ([]*Reference, error) { +func ParseReferences(ctx context.Context, srv rpc.ArduinoCoreServiceServer, args []string) ([]*Reference, error) { ret := []*Reference{} for _, arg := range args { - reference, err := ParseReference(arg) + // TODO: This is quite resource consuming (since it creates a new instance for each call) + reference, err := ParseReference(ctx, srv, arg) if err != nil { return nil, err } @@ -60,7 +61,7 @@ func ParseReferences(args []string) ([]*Reference, error) { // To achieve that, it tries to use github.com/arduino/arduino-cli/commands/core.GetPlatform // Note that the Reference is returned rightaway if the arg inserted by the user matches perfectly one in the response of core.GetPlatform // A MultiplePlatformsError is returned if the platform searched by the user matches multiple platforms -func ParseReference(arg string) (*Reference, error) { +func ParseReference(ctx context.Context, srv rpc.ArduinoCoreServiceServer, arg string) (*Reference, error) { logrus.Infof("Parsing reference %s", arg) ret := &Reference{} if arg == "" { @@ -95,8 +96,8 @@ func ParseReference(arg string) (*Reference, error) { // Now that we have the required informations in `ret` we can // try to use core.PlatformList to optimize what the user typed // (by replacing the PackageName and Architecture in ret with the content of core.GetPlatform()) - platforms, _ := core.PlatformSearch(&rpc.PlatformSearchRequest{ - Instance: instance.CreateAndInit(), + platforms, _ := srv.PlatformSearch(ctx, &rpc.PlatformSearchRequest{ + Instance: instance.CreateAndInit(ctx, srv), }) foundPlatforms := []string{} for _, platform := range platforms.GetSearchOutput() { diff --git a/internal/cli/arguments/reference_test.go b/internal/cli/arguments/reference_test.go index 047d3fc9e5c..e55051cc768 100644 --- a/internal/cli/arguments/reference_test.go +++ b/internal/cli/arguments/reference_test.go @@ -16,10 +16,11 @@ package arguments_test import ( + "context" "testing" + "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/internal/cli/arguments" - "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -46,10 +47,6 @@ var badCores = []struct { {"", nil}, } -func init() { - configuration.Settings = configuration.Init("") -} - func TestArgsStringify(t *testing.T) { for _, core := range goodCores { require.Equal(t, core.in, core.expected.String()) @@ -57,14 +54,16 @@ func TestArgsStringify(t *testing.T) { } func TestParseReferenceCores(t *testing.T) { + srv := commands.NewArduinoCoreServer() + ctx := context.Background() for _, tt := range goodCores { - actual, err := arguments.ParseReference(tt.in) + actual, err := arguments.ParseReference(ctx, srv, tt.in) assert.Nil(t, err) assert.Equal(t, tt.expected, actual) } for _, tt := range badCores { - actual, err := arguments.ParseReference(tt.in) + actual, err := arguments.ParseReference(ctx, srv, tt.in) require.NotNil(t, err, "Testing bad core '%s'", tt.in) require.Equal(t, tt.expected, actual, "Testing bad core '%s'", tt.in) } @@ -76,7 +75,8 @@ func TestParseArgs(t *testing.T) { input = append(input, tt.in) } - refs, err := arguments.ParseReferences(input) + srv := commands.NewArduinoCoreServer() + refs, err := arguments.ParseReferences(context.Background(), srv, input) assert.Nil(t, err) assert.Equal(t, len(goodCores), len(refs)) diff --git a/internal/cli/arguments/sketch.go b/internal/cli/arguments/sketch.go index 3b6d9d2c26d..e946c4fd6a2 100644 --- a/internal/cli/arguments/sketch.go +++ b/internal/cli/arguments/sketch.go @@ -18,7 +18,6 @@ package arguments import ( "context" - "github.com/arduino/arduino-cli/commands/sketch" f "github.com/arduino/arduino-cli/internal/algorithms" "github.com/arduino/arduino-cli/internal/cli/feedback" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" @@ -44,7 +43,7 @@ func InitSketchPath(path string) (sketchPath *paths.Path) { // GetSketchProfiles is an helper function useful to autocomplete. // It returns the profile names set in the sketch.yaml -func GetSketchProfiles(sketchPath string) []string { +func GetSketchProfiles(ctx context.Context, srv rpc.ArduinoCoreServiceServer, sketchPath string) []string { if sketchPath == "" { if wd, _ := paths.Getwd(); wd != nil && wd.String() != "" { sketchPath = wd.String() @@ -52,10 +51,10 @@ func GetSketchProfiles(sketchPath string) []string { return nil } } - sk, err := sketch.LoadSketch(context.Background(), &rpc.LoadSketchRequest{SketchPath: sketchPath}) + resp, err := srv.LoadSketch(ctx, &rpc.LoadSketchRequest{SketchPath: sketchPath}) if err != nil { return nil } - profiles := sk.GetProfiles() + profiles := resp.GetSketch().GetProfiles() return f.Map(profiles, (*rpc.SketchProfile).GetName) } diff --git a/internal/cli/board/attach.go b/internal/cli/board/attach.go index b340193e609..c8b8dca3e28 100644 --- a/internal/cli/board/attach.go +++ b/internal/cli/board/attach.go @@ -20,14 +20,13 @@ import ( "fmt" "os" - "github.com/arduino/arduino-cli/commands/sketch" "github.com/arduino/arduino-cli/internal/cli/arguments" "github.com/arduino/arduino-cli/internal/cli/feedback" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/spf13/cobra" ) -func initAttachCommand() *cobra.Command { +func initAttachCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { var port arguments.Port var fqbn arguments.Fqbn var programmer arguments.Programmer @@ -41,25 +40,26 @@ func initAttachCommand() *cobra.Command { " " + os.Args[0] + " board attach -P atmel_ice", Args: cobra.MaximumNArgs(1), Run: func(cmd *cobra.Command, args []string) { + ctx := cmd.Context() sketchPath := "" if len(args) > 0 { sketchPath = args[0] } - runAttachCommand(sketchPath, &port, fqbn.String(), &programmer) + runAttachCommand(ctx, srv, sketchPath, &port, fqbn.String(), &programmer) }, } - fqbn.AddToCommand(attachCommand) - port.AddToCommand(attachCommand) - programmer.AddToCommand(attachCommand) + fqbn.AddToCommand(attachCommand, srv) + port.AddToCommand(attachCommand, srv) + programmer.AddToCommand(attachCommand, srv) return attachCommand } -func runAttachCommand(path string, port *arguments.Port, fqbn string, programmer *arguments.Programmer) { +func runAttachCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, path string, port *arguments.Port, fqbn string, programmer *arguments.Programmer) { sketchPath := arguments.InitSketchPath(path) - portAddress, portProtocol, _ := port.GetPortAddressAndProtocol(nil, "", "") - newDefaults, err := sketch.SetSketchDefaults(context.Background(), &rpc.SetSketchDefaultsRequest{ + portAddress, portProtocol, _ := port.GetPortAddressAndProtocol(ctx, nil, srv, "", "") + newDefaults, err := srv.SetSketchDefaults(ctx, &rpc.SetSketchDefaultsRequest{ SketchPath: sketchPath.String(), DefaultFqbn: fqbn, DefaultProgrammer: programmer.GetProgrammer(), diff --git a/internal/cli/board/board.go b/internal/cli/board/board.go index 8cdba86743c..f07ff8c0ac3 100644 --- a/internal/cli/board/board.go +++ b/internal/cli/board/board.go @@ -19,13 +19,14 @@ import ( "os" "github.com/arduino/arduino-cli/internal/i18n" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/spf13/cobra" ) var tr = i18n.Tr // NewCommand created a new `board` command -func NewCommand() *cobra.Command { +func NewCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { boardCommand := &cobra.Command{ Use: "board", Short: tr("Arduino board commands."), @@ -34,11 +35,11 @@ func NewCommand() *cobra.Command { " " + os.Args[0] + " board list", } - boardCommand.AddCommand(initAttachCommand()) - boardCommand.AddCommand(initDetailsCommand()) - boardCommand.AddCommand(initListCommand()) - boardCommand.AddCommand(initListAllCommand()) - boardCommand.AddCommand(initSearchCommand()) + boardCommand.AddCommand(initAttachCommand(srv)) + boardCommand.AddCommand(initDetailsCommand(srv)) + boardCommand.AddCommand(initListCommand(srv)) + boardCommand.AddCommand(initListAllCommand(srv)) + boardCommand.AddCommand(initSearchCommand(srv)) return boardCommand } diff --git a/internal/cli/board/details.go b/internal/cli/board/details.go index 14f48414ea9..4e7d8579f19 100644 --- a/internal/cli/board/details.go +++ b/internal/cli/board/details.go @@ -20,7 +20,6 @@ import ( "fmt" "os" - "github.com/arduino/arduino-cli/commands/board" "github.com/arduino/arduino-cli/internal/cli/arguments" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/cli/feedback/result" @@ -32,7 +31,7 @@ import ( "github.com/spf13/cobra" ) -func initDetailsCommand() *cobra.Command { +func initDetailsCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { var showFullDetails bool var listProgrammers bool var fqbn arguments.Fqbn @@ -44,11 +43,11 @@ func initDetailsCommand() *cobra.Command { Example: " " + os.Args[0] + " board details -b arduino:avr:nano", Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { - runDetailsCommand(fqbn.String(), showFullDetails, listProgrammers, showProperties) + runDetailsCommand(cmd.Context(), srv, fqbn.String(), showFullDetails, listProgrammers, showProperties) }, } - fqbn.AddToCommand(detailsCommand) + fqbn.AddToCommand(detailsCommand, srv) detailsCommand.Flags().BoolVarP(&showFullDetails, "full", "f", false, tr("Show full board details")) detailsCommand.Flags().BoolVarP(&listProgrammers, "list-programmers", "", false, tr("Show list of available programmers")) detailsCommand.MarkFlagRequired("fqbn") @@ -56,8 +55,8 @@ func initDetailsCommand() *cobra.Command { return detailsCommand } -func runDetailsCommand(fqbn string, showFullDetails, listProgrammers bool, showProperties arguments.ShowProperties) { - inst := instance.CreateAndInit() +func runDetailsCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, fqbn string, showFullDetails, listProgrammers bool, showProperties arguments.ShowProperties) { + inst := instance.CreateAndInit(ctx, srv) logrus.Info("Executing `arduino-cli board details`") @@ -65,7 +64,7 @@ func runDetailsCommand(fqbn string, showFullDetails, listProgrammers bool, showP if err != nil { feedback.Fatal(err.Error(), feedback.ErrBadArgument) } - res, err := board.Details(context.Background(), &rpc.BoardDetailsRequest{ + res, err := srv.BoardDetails(ctx, &rpc.BoardDetailsRequest{ Instance: inst, Fqbn: fqbn, DoNotExpandBuildProperties: showPropertiesMode == arguments.ShowPropertiesUnexpanded, diff --git a/internal/cli/board/list.go b/internal/cli/board/list.go index f864768fc8d..20001f2ad82 100644 --- a/internal/cli/board/list.go +++ b/internal/cli/board/list.go @@ -22,7 +22,7 @@ import ( "os" "sort" - "github.com/arduino/arduino-cli/commands/board" + "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/commands/cmderrors" "github.com/arduino/arduino-cli/internal/arduino/cores" "github.com/arduino/arduino-cli/internal/cli/arguments" @@ -35,7 +35,7 @@ import ( "github.com/spf13/cobra" ) -func initListCommand() *cobra.Command { +func initListCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { var timeoutArg arguments.DiscoveryTimeout var watch bool var fqbn arguments.Fqbn @@ -46,32 +46,34 @@ func initListCommand() *cobra.Command { Example: " " + os.Args[0] + " board list --discovery-timeout 10s", Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { - runListCommand(watch, timeoutArg.Get().Milliseconds(), fqbn.String()) + runListCommand(cmd.Context(), srv, watch, timeoutArg.Get().Milliseconds(), fqbn.String()) }, } timeoutArg.AddToCommand(listCommand) - fqbn.AddToCommand(listCommand) + fqbn.AddToCommand(listCommand, srv) listCommand.Flags().BoolVarP(&watch, "watch", "w", false, tr("Command keeps running and prints list of connected boards whenever there is a change.")) return listCommand } // runListCommand detects and lists the connected arduino boards -func runListCommand(watch bool, timeout int64, fqbn string) { - inst := instance.CreateAndInit() +func runListCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, watch bool, timeout int64, fqbn string) { + inst := instance.CreateAndInit(ctx, srv) logrus.Info("Executing `arduino-cli board list`") if watch { - watchList(inst) + watchList(ctx, inst, srv) return } - ports, discoveryErrors, err := board.List(&rpc.BoardListRequest{ + list, err := srv.BoardList(ctx, &rpc.BoardListRequest{ Instance: inst, Timeout: timeout, Fqbn: fqbn, }) + ports := list.GetPorts() + discoveryErrors := list.GetWarnings() var invalidFQBNErr *cmderrors.InvalidFQBNError if errors.As(err, &invalidFQBNErr) { feedback.Fatal(tr(err.Error()), feedback.ErrBadArgument) @@ -86,8 +88,9 @@ func runListCommand(watch bool, timeout int64, fqbn string) { feedback.PrintResult(listResult{result.NewDetectedPorts(ports)}) } -func watchList(inst *rpc.Instance) { - eventsChan, err := board.Watch(context.Background(), &rpc.BoardListWatchRequest{Instance: inst}) +func watchList(ctx context.Context, inst *rpc.Instance, srv rpc.ArduinoCoreServiceServer) { + stream, eventsChan := commands.BoardListWatchProxyToChan(ctx) + err := srv.BoardListWatch(&rpc.BoardListWatchRequest{Instance: inst}, stream) if err != nil { feedback.Fatal(tr("Error detecting boards: %v", err), feedback.ErrNetwork) } diff --git a/internal/cli/board/listall.go b/internal/cli/board/listall.go index a4782b41d3c..55381ba2ffc 100644 --- a/internal/cli/board/listall.go +++ b/internal/cli/board/listall.go @@ -21,7 +21,6 @@ import ( "os" "sort" - "github.com/arduino/arduino-cli/commands/board" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/cli/feedback/result" "github.com/arduino/arduino-cli/internal/cli/feedback/table" @@ -33,7 +32,7 @@ import ( var showHiddenBoard bool -func initListAllCommand() *cobra.Command { +func initListAllCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { var listAllCommand = &cobra.Command{ Use: fmt.Sprintf("listall [%s]", tr("boardname")), Short: tr("List all known boards and their corresponding FQBN."), @@ -43,19 +42,21 @@ for a specific board if you specify the board name`), " " + os.Args[0] + " board listall\n" + " " + os.Args[0] + " board listall zero", Args: cobra.ArbitraryArgs, - Run: runListAllCommand, + Run: func(cmd *cobra.Command, args []string) { + runListAllCommand(cmd.Context(), args, srv) + }, } listAllCommand.Flags().BoolVarP(&showHiddenBoard, "show-hidden", "a", false, tr("Show also boards marked as 'hidden' in the platform")) return listAllCommand } // runListAllCommand list all installed boards -func runListAllCommand(cmd *cobra.Command, args []string) { - inst := instance.CreateAndInit() +func runListAllCommand(ctx context.Context, args []string, srv rpc.ArduinoCoreServiceServer) { + inst := instance.CreateAndInit(ctx, srv) logrus.Info("Executing `arduino-cli board listall`") - list, err := board.ListAll(context.Background(), &rpc.BoardListAllRequest{ + list, err := srv.BoardListAll(ctx, &rpc.BoardListAllRequest{ Instance: inst, SearchArgs: args, IncludeHiddenBoards: showHiddenBoard, diff --git a/internal/cli/board/search.go b/internal/cli/board/search.go index b590ddb6e15..42b85785788 100644 --- a/internal/cli/board/search.go +++ b/internal/cli/board/search.go @@ -22,7 +22,6 @@ import ( "sort" "strings" - "github.com/arduino/arduino-cli/commands/board" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/cli/feedback/result" "github.com/arduino/arduino-cli/internal/cli/feedback/table" @@ -32,7 +31,7 @@ import ( "github.com/spf13/cobra" ) -func initSearchCommand() *cobra.Command { +func initSearchCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { var searchCommand = &cobra.Command{ Use: fmt.Sprintf("search [%s]", tr("boardname")), Short: tr("Search for a board in the Boards Manager."), @@ -41,18 +40,20 @@ func initSearchCommand() *cobra.Command { " " + os.Args[0] + " board search\n" + " " + os.Args[0] + " board search zero", Args: cobra.ArbitraryArgs, - Run: runSearchCommand, + Run: func(cmd *cobra.Command, args []string) { + runSearchCommand(cmd.Context(), srv, args) + }, } searchCommand.Flags().BoolVarP(&showHiddenBoard, "show-hidden", "a", false, tr("Show also boards marked as 'hidden' in the platform")) return searchCommand } -func runSearchCommand(cmd *cobra.Command, args []string) { - inst := instance.CreateAndInit() +func runSearchCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, args []string) { + inst := instance.CreateAndInit(ctx, srv) logrus.Info("Executing `arduino-cli board search`") - res, err := board.Search(context.Background(), &rpc.BoardSearchRequest{ + res, err := srv.BoardSearch(ctx, &rpc.BoardSearchRequest{ Instance: inst, SearchArgs: strings.Join(args, " "), IncludeHiddenBoards: showHiddenBoard, diff --git a/internal/cli/burnbootloader/burnbootloader.go b/internal/cli/burnbootloader/burnbootloader.go index c14c63eeb42..7c8f4c16896 100644 --- a/internal/cli/burnbootloader/burnbootloader.go +++ b/internal/cli/burnbootloader/burnbootloader.go @@ -20,8 +20,8 @@ import ( "errors" "os" + "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/commands/cmderrors" - "github.com/arduino/arduino-cli/commands/upload" "github.com/arduino/arduino-cli/internal/cli/arguments" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/cli/instance" @@ -42,19 +42,21 @@ var ( ) // NewCommand created a new `burn-bootloader` command -func NewCommand() *cobra.Command { +func NewCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { burnBootloaderCommand := &cobra.Command{ Use: "burn-bootloader", Short: tr("Upload the bootloader."), Long: tr("Upload the bootloader on the board using an external programmer."), Example: " " + os.Args[0] + " burn-bootloader -b arduino:avr:uno -P atmel_ice", - Args: cobra.MaximumNArgs(1), - Run: runBootloaderCommand, + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, args []string) { + runBootloaderCommand(cmd.Context(), srv) + }, } - fqbn.AddToCommand(burnBootloaderCommand) - port.AddToCommand(burnBootloaderCommand) - programmer.AddToCommand(burnBootloaderCommand) + fqbn.AddToCommand(burnBootloaderCommand, srv) + port.AddToCommand(burnBootloaderCommand, srv) + programmer.AddToCommand(burnBootloaderCommand, srv) burnBootloaderCommand.Flags().BoolVarP(&verify, "verify", "t", false, tr("Verify uploaded binary after the upload.")) burnBootloaderCommand.Flags().BoolVarP(&verbose, "verbose", "v", false, tr("Turns on verbose mode.")) burnBootloaderCommand.Flags().BoolVar(&dryRun, "dry-run", false, tr("Do not perform the actual upload, just log out actions")) @@ -63,27 +65,28 @@ func NewCommand() *cobra.Command { return burnBootloaderCommand } -func runBootloaderCommand(command *cobra.Command, args []string) { - instance := instance.CreateAndInit() +func runBootloaderCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer) { + instance := instance.CreateAndInit(ctx, srv) logrus.Info("Executing `arduino-cli burn-bootloader`") // We don't need a Sketch to upload a board's bootloader - discoveryPort, err := port.GetPort(instance, "", "") + discoveryPort, err := port.GetPort(ctx, instance, srv, "", "") if err != nil { feedback.Fatal(tr("Error during Upload: %v", err), feedback.ErrGeneric) } stdOut, stdErr, res := feedback.OutputStreams() - if _, err := upload.BurnBootloader(context.Background(), &rpc.BurnBootloaderRequest{ + stream := commands.BurnBootloaderToServerStreams(ctx, stdOut, stdErr) + if err := srv.BurnBootloader(&rpc.BurnBootloaderRequest{ Instance: instance, Fqbn: fqbn.String(), Port: discoveryPort, Verbose: verbose, Verify: verify, - Programmer: programmer.String(instance, fqbn.String()), + Programmer: programmer.String(ctx, instance, srv, fqbn.String()), DryRun: dryRun, - }, stdOut, stdErr); err != nil { + }, stream); err != nil { errcode := feedback.ErrGeneric if errors.Is(err, &cmderrors.ProgrammerRequiredForUploadError{}) { errcode = feedback.ErrMissingProgrammer diff --git a/internal/cli/cache/cache.go b/internal/cli/cache/cache.go index 43c9d775234..e6b14ebe90d 100644 --- a/internal/cli/cache/cache.go +++ b/internal/cli/cache/cache.go @@ -19,13 +19,14 @@ import ( "os" "github.com/arduino/arduino-cli/internal/i18n" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/spf13/cobra" ) var tr = i18n.Tr // NewCommand created a new `cache` command -func NewCommand() *cobra.Command { +func NewCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { cacheCommand := &cobra.Command{ Use: "cache", Short: tr("Arduino cache commands."), @@ -34,7 +35,7 @@ func NewCommand() *cobra.Command { " " + os.Args[0] + " cache clean\n\n", } - cacheCommand.AddCommand(initCleanCommand()) + cacheCommand.AddCommand(initCleanCommand(srv)) return cacheCommand } diff --git a/internal/cli/cache/clean.go b/internal/cli/cache/clean.go index 32f788e87ed..184d2e65b90 100644 --- a/internal/cli/cache/clean.go +++ b/internal/cli/cache/clean.go @@ -19,29 +19,30 @@ import ( "context" "os" - "github.com/arduino/arduino-cli/commands/cache" "github.com/arduino/arduino-cli/internal/cli/feedback" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) -func initCleanCommand() *cobra.Command { +func initCleanCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { cleanCommand := &cobra.Command{ Use: "clean", Short: tr("Delete Boards/Library Manager download cache."), - Long: tr("Delete contents of the `directories.downloads` folder, where archive files are staged during installation of libraries and boards platforms."), + Long: tr("Delete contents of the downloads cache folder, where archive files are staged during installation of libraries and boards platforms."), Example: " " + os.Args[0] + " cache clean", Args: cobra.NoArgs, - Run: runCleanCommand, + Run: func(cmd *cobra.Command, args []string) { + runCleanCommand(cmd.Context(), srv) + }, } return cleanCommand } -func runCleanCommand(cmd *cobra.Command, args []string) { +func runCleanCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer) { logrus.Info("Executing `arduino-cli cache clean`") - _, err := cache.CleanDownloadCacheDirectory(context.Background(), &rpc.CleanDownloadCacheDirectoryRequest{}) + _, err := srv.CleanDownloadCacheDirectory(ctx, &rpc.CleanDownloadCacheDirectoryRequest{}) if err != nil { feedback.Fatal(tr("Error cleaning caches: %v", err), feedback.ErrGeneric) } diff --git a/internal/cli/cli.go b/internal/cli/cli.go index da265d46f59..c2333c1827b 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -22,14 +22,12 @@ import ( "os" "strings" - "github.com/arduino/arduino-cli/commands/updatecheck" "github.com/arduino/arduino-cli/internal/cli/board" "github.com/arduino/arduino-cli/internal/cli/burnbootloader" "github.com/arduino/arduino-cli/internal/cli/cache" "github.com/arduino/arduino-cli/internal/cli/compile" "github.com/arduino/arduino-cli/internal/cli/completion" "github.com/arduino/arduino-cli/internal/cli/config" - "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/arduino/arduino-cli/internal/cli/core" "github.com/arduino/arduino-cli/internal/cli/daemon" "github.com/arduino/arduino-cli/internal/cli/debug" @@ -56,36 +54,68 @@ import ( semver "go.bug.st/relaxed-semver" ) -var ( - verbose bool - jsonOutput bool - outputFormat string - configFile string -) - // NewCommand creates a new ArduinoCli command root -func NewCommand() *cobra.Command { +func NewCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { cobra.AddTemplateFunc("tr", i18n.Tr) var updaterMessageChan chan *semver.Version - // ArduinoCli is the root command - arduinoCli := &cobra.Command{ + var ( + verbose bool + noColor bool + logLevel string + logFile string + logFormat string + jsonOutput bool + outputFormat string + configFile string + additionalUrls []string + ) + + resp, err := srv.ConfigurationGet(context.Background(), &rpc.ConfigurationGetRequest{}) + if err != nil { + panic("Error creating configuration: " + err.Error()) + } + settings := resp.GetConfiguration() + + defaultLogFile := settings.GetLogging().GetFile() + defaultLogFormat := settings.GetLogging().GetFormat() + defaultLogLevel := settings.GetLogging().GetLevel() + defaultAdditionalURLs := settings.GetBoardManager().GetAdditionalUrls() + defaultOutputNoColor := settings.GetOutput().GetNoColor() + + cmd := &cobra.Command{ Use: "arduino-cli", Short: tr("Arduino CLI."), Long: tr("Arduino Command Line Interface (arduino-cli)."), Example: fmt.Sprintf(" %s <%s> [%s...]", os.Args[0], tr("command"), tr("flags")), PersistentPreRun: func(cmd *cobra.Command, args []string) { + ctx := cmd.Context() + + config.ApplyGlobalFlagsToConfiguration(ctx, cmd, srv) + if jsonOutput { outputFormat = "json" } + if outputFormat != "text" { + cmd.SetHelpFunc(func(cmd *cobra.Command, args []string) { + feedback.Fatal(tr("Should show help message, but it is available only in TEXT mode."), feedback.ErrBadArgument) + }) + } + + preRun(verbose, outputFormat, logLevel, logFile, logFormat, noColor, settings) - preRun(cmd, args) + // Log the configuration file used + if configFile := config.GetConfigFile(ctx); configFile != "" { + logrus.Infof("Using config file: %s", configFile) + } else { + logrus.Info("Config file not found, using default values") + } if cmd.Name() != "version" { updaterMessageChan = make(chan *semver.Version) go func() { - res, err := updatecheck.CheckForArduinoCLIUpdates(context.Background(), &rpc.CheckForArduinoCLIUpdatesRequest{}) + res, err := srv.CheckForArduinoCLIUpdates(ctx, &rpc.CheckForArduinoCLIUpdatesRequest{}) if err != nil { logrus.Warnf("Error checking for updates: %v", err) updaterMessageChan <- nil @@ -112,60 +142,48 @@ func NewCommand() *cobra.Command { }, } - arduinoCli.SetUsageTemplate(getUsageTemplate()) - - createCliCommandTree(arduinoCli) + cmd.SetUsageTemplate(getUsageTemplate()) - return arduinoCli -} - -// this is here only for testing -func createCliCommandTree(cmd *cobra.Command) { - cmd.AddCommand(board.NewCommand()) - cmd.AddCommand(cache.NewCommand()) - cmd.AddCommand(compile.NewCommand()) + cmd.AddCommand(board.NewCommand(srv)) + cmd.AddCommand(cache.NewCommand(srv)) + cmd.AddCommand(compile.NewCommand(srv, settings)) cmd.AddCommand(completion.NewCommand()) - cmd.AddCommand(config.NewCommand()) - cmd.AddCommand(core.NewCommand()) - cmd.AddCommand(daemon.NewCommand()) + cmd.AddCommand(config.NewCommand(srv, settings)) + cmd.AddCommand(core.NewCommand(srv)) + cmd.AddCommand(daemon.NewCommand(srv, settings)) cmd.AddCommand(generatedocs.NewCommand()) - cmd.AddCommand(lib.NewCommand()) - cmd.AddCommand(monitor.NewCommand()) - cmd.AddCommand(outdated.NewCommand()) - cmd.AddCommand(sketch.NewCommand()) - cmd.AddCommand(update.NewCommand()) - cmd.AddCommand(upgrade.NewCommand()) - cmd.AddCommand(upload.NewCommand()) - cmd.AddCommand(debug.NewCommand()) - cmd.AddCommand(burnbootloader.NewCommand()) - cmd.AddCommand(version.NewCommand()) + cmd.AddCommand(lib.NewCommand(srv, settings)) + cmd.AddCommand(monitor.NewCommand(srv)) + cmd.AddCommand(outdated.NewCommand(srv)) + cmd.AddCommand(sketch.NewCommand(srv)) + cmd.AddCommand(update.NewCommand(srv)) + cmd.AddCommand(upgrade.NewCommand(srv)) + cmd.AddCommand(upload.NewCommand(srv)) + cmd.AddCommand(debug.NewCommand(srv)) + cmd.AddCommand(burnbootloader.NewCommand(srv)) + cmd.AddCommand(version.NewCommand(srv)) cmd.AddCommand(feedback.NewCommand()) cmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, tr("Print the logs on the standard output.")) cmd.Flag("verbose").Hidden = true cmd.PersistentFlags().BoolVar(&verbose, "log", false, tr("Print the logs on the standard output.")) validLogLevels := []string{"trace", "debug", "info", "warn", "error", "fatal", "panic"} - cmd.PersistentFlags().String("log-level", "", tr("Messages with this level and above will be logged. Valid levels are: %s", strings.Join(validLogLevels, ", "))) - cmd.RegisterFlagCompletionFunc("log-level", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return validLogLevels, cobra.ShellCompDirectiveDefault - }) - cmd.PersistentFlags().String("log-file", "", tr("Path to the file where logs will be written.")) + cmd.PersistentFlags().StringVar(&logLevel, "log-level", defaultLogLevel, tr("Messages with this level and above will be logged. Valid levels are: %s", strings.Join(validLogLevels, ", "))) + cmd.RegisterFlagCompletionFunc("log-level", cobra.FixedCompletions(validLogLevels, cobra.ShellCompDirectiveDefault)) + cmd.PersistentFlags().StringVar(&logFile, "log-file", defaultLogFile, tr("Path to the file where logs will be written.")) validLogFormats := []string{"text", "json"} - cmd.PersistentFlags().String("log-format", "", tr("The output format for the logs, can be: %s", strings.Join(validLogFormats, ", "))) - cmd.RegisterFlagCompletionFunc("log-format", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return validLogFormats, cobra.ShellCompDirectiveDefault - }) + cmd.PersistentFlags().StringVar(&logFormat, "log-format", defaultLogFormat, tr("The output format for the logs, can be: %s", strings.Join(validLogFormats, ", "))) + cmd.RegisterFlagCompletionFunc("log-format", cobra.FixedCompletions(validLogFormats, cobra.ShellCompDirectiveDefault)) validOutputFormats := []string{"text", "json", "jsonmini"} cmd.PersistentFlags().StringVar(&outputFormat, "format", "text", tr("The command output format, can be: %s", strings.Join(validOutputFormats, ", "))) - cmd.RegisterFlagCompletionFunc("format", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return validOutputFormats, cobra.ShellCompDirectiveDefault - }) + cmd.RegisterFlagCompletionFunc("format", cobra.FixedCompletions(validOutputFormats, cobra.ShellCompDirectiveDefault)) cmd.Flag("format").Hidden = true cmd.PersistentFlags().BoolVar(&jsonOutput, "json", false, tr("Print the output in JSON format.")) cmd.PersistentFlags().StringVar(&configFile, "config-file", "", tr("The custom config file (if not specified the default will be used).")) - cmd.PersistentFlags().StringSlice("additional-urls", []string{}, tr("Comma-separated list of additional URLs for the Boards Manager.")) - cmd.PersistentFlags().Bool("no-color", false, "Disable colored output.") - configuration.BindFlags(cmd, configuration.Settings) + cmd.PersistentFlags().StringSliceVar(&additionalUrls, "additional-urls", defaultAdditionalURLs, tr("Comma-separated list of additional URLs for the Boards Manager.")) + cmd.PersistentFlags().BoolVar(&noColor, "no-color", defaultOutputNoColor, "Disable colored output.") + + return cmd } // convert the string passed to the `--log-level` option to the corresponding @@ -184,22 +202,23 @@ func toLogLevel(s string) (t logrus.Level, found bool) { return } -func preRun(cmd *cobra.Command, args []string) { - configFile := configuration.Settings.ConfigFileUsed() - - // initialize inventory - err := inventory.Init(configuration.DataDir(configuration.Settings).String()) - if err != nil { - feedback.Fatal(fmt.Sprintf("Error: %v", err), feedback.ErrInitializingInventory) - } - - // https://no-color.org/ - color.NoColor = configuration.Settings.GetBool("output.no_color") || os.Getenv("NO_COLOR") != "" +func preRun(verbose bool, outputFormat string, logLevel, logFile, logFormat string, noColor bool, settings *rpc.Configuration) { + // + // Prepare the Feedback system + // // Set default feedback output to colorable + color.NoColor = noColor || os.Getenv("NO_COLOR") != "" // https://no-color.org/ feedback.SetOut(colorable.NewColorableStdout()) feedback.SetErr(colorable.NewColorableStderr()) + // use the output format to configure the Feedback + format, ok := feedback.ParseOutputFormat(outputFormat) + if !ok { + feedback.Fatal(tr("Invalid output format: %s", outputFormat), feedback.ErrBadArgument) + } + feedback.SetFormat(format) + // // Prepare logging // @@ -217,13 +236,12 @@ func preRun(cmd *cobra.Command, args []string) { } // set the Logger format - logFormat := strings.ToLower(configuration.Settings.GetString("logging.format")) + logFormat = strings.ToLower(logFormat) if logFormat == "json" { logrus.SetFormatter(&logrus.JSONFormatter{}) } // should we log to file? - logFile := configuration.Settings.GetString("logging.file") if logFile != "" { file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) if err != nil { @@ -239,41 +257,20 @@ func preRun(cmd *cobra.Command, args []string) { } // configure logging filter - if lvl, found := toLogLevel(configuration.Settings.GetString("logging.level")); !found { - feedback.Fatal(tr("Invalid option for --log-level: %s", configuration.Settings.GetString("logging.level")), feedback.ErrBadArgument) + if logrusLevel, found := toLogLevel(logLevel); !found { + feedback.Fatal(tr("Invalid logging level: %s", logLevel), feedback.ErrBadArgument) } else { - logrus.SetLevel(lvl) - } - - // - // Prepare the Feedback system - // - - // check the right output format was passed - format, found := feedback.ParseOutputFormat(outputFormat) - if !found { - feedback.Fatal(tr("Invalid output format: %s", outputFormat), feedback.ErrBadArgument) + logrus.SetLevel(logrusLevel) } - // use the output format to configure the Feedback - feedback.SetFormat(format) - - // // Print some status info and check command is consistent - // - - if configFile != "" { - logrus.Infof("Using config file: %s", configFile) - } else { - logrus.Info("Config file not found, using default values") - } - logrus.Info(versioninfo.VersionInfo.Application + " version " + versioninfo.VersionInfo.VersionString) - if outputFormat != "text" { - cmd.SetHelpFunc(func(cmd *cobra.Command, args []string) { - logrus.Warn("Calling help on JSON format") - feedback.Fatal(tr("Invalid Call : should show Help, but it is available only in TEXT mode."), feedback.ErrBadArgument) - }) + // + // Initialize inventory + // + err := inventory.Init(settings.GetDirectories().GetData()) + if err != nil { + feedback.Fatal(fmt.Sprintf("Error: %v", err), feedback.ErrInitializingInventory) } } diff --git a/internal/cli/compile/compile.go b/internal/cli/compile/compile.go index 2b607b78536..f04f01e8fc7 100644 --- a/internal/cli/compile/compile.go +++ b/internal/cli/compile/compile.go @@ -16,7 +16,6 @@ package compile import ( - "context" "encoding/json" "errors" "fmt" @@ -24,13 +23,9 @@ import ( "os" "strings" + "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/commands/cmderrors" - "github.com/arduino/arduino-cli/commands/compile" - "github.com/arduino/arduino-cli/commands/core" - "github.com/arduino/arduino-cli/commands/sketch" - "github.com/arduino/arduino-cli/commands/upload" "github.com/arduino/arduino-cli/internal/cli/arguments" - "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/cli/feedback/result" "github.com/arduino/arduino-cli/internal/cli/feedback/table" @@ -61,7 +56,7 @@ var ( uploadAfterCompile bool // Upload the binary after the compilation. portArgs arguments.Port // Upload port, e.g.: COM10 or /dev/ttyACM0. verify bool // Upload, verify uploaded binary after the upload. - exportBinaries bool // + exportBinaries bool // If set built binaries will be exported to the sketch folder exportDir string // The compiled binary is written to this file optimizeForDebug bool // Optimize compile output for debug, not for release programmer arguments.Programmer // Use the specified programmer to upload @@ -80,7 +75,7 @@ var ( ) // NewCommand created a new `compile` command -func NewCommand() *cobra.Command { +func NewCommand(srv rpc.ArduinoCoreServiceServer, settings *rpc.Configuration) *cobra.Command { compileCommand := &cobra.Command{ Use: "compile", Short: tr("Compiles Arduino sketches."), @@ -91,16 +86,18 @@ func NewCommand() *cobra.Command { " " + os.Args[0] + ` compile -b arduino:avr:uno --build-property "build.extra_flags=-DPIN=2 \"-DMY_DEFINE=\"hello world\"\"" /home/user/Arduino/MySketch` + "\n" + " " + os.Args[0] + ` compile -b arduino:avr:uno --build-property build.extra_flags=-DPIN=2 --build-property "compiler.cpp.extra_flags=\"-DSSID=\"hello world\"\"" /home/user/Arduino/MySketch` + "\n", Args: cobra.MaximumNArgs(1), - Run: runCompileCommand, + Run: func(cmd *cobra.Command, args []string) { + runCompileCommand(cmd, args, srv) + }, } - fqbnArg.AddToCommand(compileCommand) - profileArg.AddToCommand(compileCommand) + fqbnArg.AddToCommand(compileCommand, srv) + profileArg.AddToCommand(compileCommand, srv) compileCommand.Flags().BoolVar(&dumpProfile, "dump-profile", false, tr("Create and print a profile configuration from the build.")) showPropertiesArg.AddToCommand(compileCommand) compileCommand.Flags().BoolVar(&preprocess, "preprocess", false, tr("Print preprocessed code to stdout instead of compiling.")) compileCommand.Flags().StringVar(&buildCachePath, "build-cache-path", "", tr("Builds of 'core.a' are saved into this path to be cached and reused.")) - compileCommand.Flags().StringVarP(&exportDir, "output-dir", "", "", tr("Save build artifacts in this directory.")) + compileCommand.Flags().StringVar(&exportDir, "output-dir", "", tr("Save build artifacts in this directory.")) compileCommand.Flags().StringVar(&buildPath, "build-path", "", tr("Path where to save compiled files. If omitted, a directory will be created in the default temporary path of your OS.")) compileCommand.Flags().StringSliceVar(&buildProperties, "build-properties", []string{}, @@ -118,31 +115,32 @@ func NewCommand() *cobra.Command { compileCommand.Flags().BoolVarP(&verbose, "verbose", "v", false, tr("Optional, turns on verbose mode.")) compileCommand.Flags().BoolVar(&quiet, "quiet", false, tr("Optional, suppresses almost every output.")) compileCommand.Flags().BoolVarP(&uploadAfterCompile, "upload", "u", false, tr("Upload the binary after the compilation.")) - portArgs.AddToCommand(compileCommand) + portArgs.AddToCommand(compileCommand, srv) compileCommand.Flags().BoolVarP(&verify, "verify", "t", false, tr("Verify uploaded binary after the upload.")) compileCommand.Flags().StringSliceVar(&library, "library", []string{}, tr("Path to a single library’s root folder. Can be used multiple times or entries can be comma separated.")) compileCommand.Flags().StringSliceVar(&libraries, "libraries", []string{}, tr("Path to a collection of libraries. Can be used multiple times or entries can be comma separated.")) compileCommand.Flags().BoolVar(&optimizeForDebug, "optimize-for-debug", false, tr("Optional, optimize compile output for debugging, rather than for release.")) - programmer.AddToCommand(compileCommand) + programmer.AddToCommand(compileCommand, srv) compileCommand.Flags().BoolVar(&compilationDatabaseOnly, "only-compilation-database", false, tr("Just produce the compilation database, without actually compiling. All build commands are skipped except pre* hooks.")) compileCommand.Flags().BoolVar(&clean, "clean", false, tr("Optional, cleanup the build folder and do not use any cached build.")) - compileCommand.Flags().BoolVarP(&exportBinaries, "export-binaries", "e", false, tr("If set built binaries will be exported to the sketch folder.")) + compileCommand.Flags().BoolVarP(&exportBinaries, "export-binaries", "e", settings.GetSketch().GetAlwaysExportBinaries(), + tr("If set built binaries will be exported to the sketch folder.")) compileCommand.Flags().StringVar(&sourceOverrides, "source-override", "", tr("Optional. Path to a .json file that contains a set of replacements of the sketch source code.")) compileCommand.Flag("source-override").Hidden = true compileCommand.Flags().BoolVar(&skipLibrariesDiscovery, "skip-libraries-discovery", false, "Skip libraries discovery. This flag is provided only for use in language server and other, very specific, use cases. Do not use for normal compiles") compileCommand.Flag("skip-libraries-discovery").Hidden = true compileCommand.Flags().Int32VarP(&jobs, "jobs", "j", 0, tr("Max number of parallel compiles. If set to 0 the number of available CPUs cores will be used.")) - configuration.Settings.BindPFlag("sketch.always_export_binaries", compileCommand.Flags().Lookup("export-binaries")) compileCommand.Flags().MarkDeprecated("build-properties", tr("please use --build-property instead.")) return compileCommand } -func runCompileCommand(cmd *cobra.Command, args []string) { +func runCompileCommand(cmd *cobra.Command, args []string, srv rpc.ArduinoCoreServiceServer) { logrus.Info("Executing `arduino-cli compile`") + ctx := cmd.Context() if profileArg.Get() != "" { if len(libraries) > 0 { @@ -159,26 +157,27 @@ func runCompileCommand(cmd *cobra.Command, args []string) { } sketchPath := arguments.InitSketchPath(path) - sk, err := sketch.LoadSketch(context.Background(), &rpc.LoadSketchRequest{SketchPath: sketchPath.String()}) + resp, err := srv.LoadSketch(ctx, &rpc.LoadSketchRequest{SketchPath: sketchPath.String()}) if err != nil { feedback.FatalError(err, feedback.ErrGeneric) } + sk := resp.GetSketch() feedback.WarnAboutDeprecatedFiles(sk) var inst *rpc.Instance var profile *rpc.SketchProfile if profileArg.Get() == "" { - inst, profile = instance.CreateAndInitWithProfile(sk.GetDefaultProfile().GetName(), sketchPath) + inst, profile = instance.CreateAndInitWithProfile(ctx, srv, sk.GetDefaultProfile().GetName(), sketchPath) } else { - inst, profile = instance.CreateAndInitWithProfile(profileArg.Get(), sketchPath) + inst, profile = instance.CreateAndInitWithProfile(ctx, srv, profileArg.Get(), sketchPath) } if fqbnArg.String() == "" { fqbnArg.Set(profile.GetFqbn()) } - fqbn, port := arguments.CalculateFQBNAndPort(&portArgs, &fqbnArg, inst, sk.GetDefaultFqbn(), sk.GetDefaultPort(), sk.GetDefaultProtocol()) + fqbn, port := arguments.CalculateFQBNAndPort(ctx, &portArgs, &fqbnArg, inst, srv, sk.GetDefaultFqbn(), sk.GetDefaultPort(), sk.GetDefaultProtocol()) if keysKeychain != "" || signKey != "" || encryptKey != "" { arguments.CheckFlagsMandatory(cmd, "keys-keychain", "sign-key", "encrypt-key") @@ -232,6 +231,7 @@ func runCompileCommand(cmd *cobra.Command, args []string) { Warnings: warnings, Verbose: verbose, Quiet: quiet, + ExportBinaries: &exportBinaries, ExportDir: exportDir, Libraries: libraries, OptimizeForDebug: optimizeForDebug, @@ -246,11 +246,13 @@ func runCompileCommand(cmd *cobra.Command, args []string) { DoNotExpandBuildProperties: showProperties == arguments.ShowPropertiesUnexpanded, Jobs: jobs, } - builderRes, compileError := compile.Compile(context.Background(), compileRequest, stdOut, stdErr, nil) + server, builderResCB := commands.CompilerServerToStreams(ctx, stdOut, stdErr) + compileError := srv.Compile(compileRequest, server) + builderRes := builderResCB() var uploadRes *rpc.UploadResult if compileError == nil && uploadAfterCompile { - userFieldRes, err := upload.SupportedUserFields(context.Background(), &rpc.SupportedUserFieldsRequest{ + userFieldRes, err := srv.SupportedUserFields(ctx, &rpc.SupportedUserFieldsRequest{ Instance: inst, Fqbn: fqbn, Protocol: port.GetProtocol(), @@ -271,7 +273,7 @@ func runCompileCommand(cmd *cobra.Command, args []string) { prog := profile.GetProgrammer() if prog == "" || programmer.GetProgrammer() != "" { - prog = programmer.String(inst, fqbn) + prog = programmer.String(ctx, inst, srv, fqbn) } if prog == "" { prog = sk.GetDefaultProgrammer() @@ -289,7 +291,8 @@ func runCompileCommand(cmd *cobra.Command, args []string) { UserFields: fields, } - if res, err := upload.Upload(context.Background(), uploadRequest, stdOut, stdErr); err != nil { + stream, streamRes := commands.UploadToServerStreams(ctx, stdOut, stdErr) + if err := srv.Upload(uploadRequest, stream); err != nil { errcode := feedback.ErrGeneric if errors.Is(err, &cmderrors.ProgrammerRequiredForUploadError{}) { errcode = feedback.ErrMissingProgrammer @@ -299,7 +302,7 @@ func runCompileCommand(cmd *cobra.Command, args []string) { } feedback.Fatal(tr("Error during Upload: %v", err), errcode) } else { - uploadRes = res + uploadRes = streamRes() } } @@ -384,7 +387,7 @@ func runCompileCommand(cmd *cobra.Command, args []string) { if profileArg.String() == "" { res.Error += fmt.Sprintln() - if platform, err := core.PlatformSearch(&rpc.PlatformSearchRequest{ + if platform, err := srv.PlatformSearch(ctx, &rpc.PlatformSearchRequest{ Instance: inst, SearchArgs: platformErr.Platform, }); err != nil { diff --git a/internal/cli/config/add.go b/internal/cli/config/add.go index 5018b0440de..059ecad07c1 100644 --- a/internal/cli/config/add.go +++ b/internal/cli/config/add.go @@ -16,35 +16,18 @@ package config import ( + "context" + "encoding/json" "os" - "reflect" + "slices" - "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/arduino/arduino-cli/internal/cli/feedback" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) -func uniquify[T comparable](s []T) []T { - // use a map, which enforces unique keys - inResult := make(map[T]bool) - var result []T - // loop through input slice **in order**, - // to ensure output retains that order - // (except that it removes duplicates) - for i := 0; i < len(s); i++ { - // attempt to use the element as a key - if _, ok := inResult[s[i]]; !ok { - // if key didn't exist in map, - // add to map and append to result - inResult[s[i]] = true - result = append(result, s[i]) - } - } - return result -} - -func initAddCommand() *cobra.Command { +func initAddCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { addCommand := &cobra.Command{ Use: "add", Short: tr("Adds one or more values to a setting."), @@ -53,30 +36,45 @@ func initAddCommand() *cobra.Command { " " + os.Args[0] + " config add board_manager.additional_urls https://example.com/package_example_index.json\n" + " " + os.Args[0] + " config add board_manager.additional_urls https://example.com/package_example_index.json https://another-url.com/package_another_index.json\n", Args: cobra.MinimumNArgs(2), - Run: runAddCommand, + Run: func(cmd *cobra.Command, args []string) { + ctx := cmd.Context() + runAddCommand(ctx, srv, args) + }, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return GetConfigurationKeys(), cobra.ShellCompDirectiveDefault + ctx := cmd.Context() + return getAllArraySettingsKeys(ctx, srv), cobra.ShellCompDirectiveDefault }, } return addCommand } -func runAddCommand(cmd *cobra.Command, args []string) { +func runAddCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, args []string) { logrus.Info("Executing `arduino-cli config add`") key := args[0] - kind := validateKey(key) - if kind != reflect.Slice { + if !slices.Contains(getAllArraySettingsKeys(ctx, srv), key) { msg := tr("The key '%[1]v' is not a list of items, can't add to it.\nMaybe use '%[2]s'?", key, "config set") feedback.Fatal(msg, feedback.ErrGeneric) } - v := configuration.Settings.GetStringSlice(key) - v = append(v, args[1:]...) - v = uniquify(v) - configuration.Settings.Set(key, v) + var currentValues []string + if resp, err := srv.SettingsGetValue(ctx, &rpc.SettingsGetValueRequest{Key: key}); err != nil { + feedback.Fatal(tr("Cannot get the configuration key %[1]s: %[2]v", key, err), feedback.ErrGeneric) + } else if err := json.Unmarshal([]byte(resp.GetEncodedValue()), ¤tValues); err != nil { + feedback.Fatal(tr("Cannot get the configuration key %[1]s: %[2]v", key, err), feedback.ErrGeneric) + } - if err := configuration.Settings.WriteConfig(); err != nil { - feedback.Fatal(tr("Can't write config file: %v", err), feedback.ErrGeneric) + for _, arg := range args[1:] { + if !slices.Contains(currentValues, arg) { + currentValues = append(currentValues, arg) + } } + + if newValuesJSON, err := json.Marshal(currentValues); err != nil { + feedback.Fatal(tr("Cannot remove the configuration key %[1]s: %[2]v", key, err), feedback.ErrGeneric) + } else if _, err := srv.SettingsSetValue(ctx, &rpc.SettingsSetValueRequest{Key: key, EncodedValue: string(newValuesJSON)}); err != nil { + feedback.Fatal(tr("Cannot remove the configuration key %[1]s: %[2]v", key, err), feedback.ErrGeneric) + } + + saveConfiguration(ctx, srv) } diff --git a/internal/cli/config/config.go b/internal/cli/config/config.go index c59714478d3..2662befe322 100644 --- a/internal/cli/config/config.go +++ b/internal/cli/config/config.go @@ -16,45 +16,80 @@ package config import ( + "context" "os" - "reflect" + "strings" - "github.com/arduino/arduino-cli/internal/cli/configuration" + f "github.com/arduino/arduino-cli/internal/algorithms" + "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/i18n" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" + "github.com/arduino/go-paths-helper" "github.com/spf13/cobra" ) var tr = i18n.Tr // NewCommand created a new `config` command -func NewCommand() *cobra.Command { +func NewCommand(srv rpc.ArduinoCoreServiceServer, settings *rpc.Configuration) *cobra.Command { configCommand := &cobra.Command{ Use: "config", Short: tr("Arduino configuration commands."), Example: " " + os.Args[0] + " config init", } - configCommand.AddCommand(initAddCommand()) - configCommand.AddCommand(initDeleteCommand()) - configCommand.AddCommand(initDumpCommand()) - configCommand.AddCommand(initGetCommand()) + configCommand.AddCommand(initAddCommand(srv)) + configCommand.AddCommand(initDeleteCommand(srv)) + configCommand.AddCommand(initDumpCommand(srv)) + configCommand.AddCommand(initGetCommand(srv)) configCommand.AddCommand(initInitCommand()) - configCommand.AddCommand(initRemoveCommand()) - configCommand.AddCommand(initSetCommand()) + configCommand.AddCommand(initRemoveCommand(srv)) + configCommand.AddCommand(initSetCommand(srv)) return configCommand } -// GetConfigurationKeys is an helper function useful to autocomplete. -// It returns a list of configuration keys which can be changed -func GetConfigurationKeys() []string { - var res []string - keys := configuration.Settings.AllKeys() - for _, key := range keys { - kind, _ := typeOf(key) - if kind == reflect.Slice { - res = append(res, key) - } +func getAllSettingsKeys(ctx context.Context, srv rpc.ArduinoCoreServiceServer) []string { + res, _ := srv.SettingsEnumerate(ctx, &rpc.SettingsEnumerateRequest{}) + allKeys := f.Map(res.GetEntries(), (*rpc.SettingsEnumerateResponse_Entry).GetKey) + return allKeys +} + +func getAllArraySettingsKeys(ctx context.Context, srv rpc.ArduinoCoreServiceServer) []string { + res, _ := srv.SettingsEnumerate(ctx, &rpc.SettingsEnumerateRequest{}) + arrayEntries := f.Filter(res.GetEntries(), func(e *rpc.SettingsEnumerateResponse_Entry) bool { + return strings.HasPrefix(e.GetType(), "[]") + }) + arrayKeys := f.Map(arrayEntries, (*rpc.SettingsEnumerateResponse_Entry).GetKey) + return arrayKeys +} + +type ctxValue string + +// GetConfigFile returns the configuration file path from the context +func GetConfigFile(ctx context.Context) string { + res := ctx.Value(ctxValue("config_file")) + if res == nil { + return "" + } + return res.(string) +} + +// SetConfigFile sets the configuration file path in the context +func SetConfigFile(ctx context.Context, configFile string) context.Context { + return context.WithValue(ctx, ctxValue("config_file"), configFile) +} + +func saveConfiguration(ctx context.Context, srv rpc.ArduinoCoreServiceServer) { + var outConfig []byte + if res, err := srv.ConfigurationSave(ctx, &rpc.ConfigurationSaveRequest{SettingsFormat: "yaml"}); err != nil { + feedback.Fatal(tr("Error writing to file: %v", err), feedback.ErrGeneric) + } else { + outConfig = []byte(res.GetEncodedSettings()) + } + + configFile := GetConfigFile(ctx) + if err := paths.New(configFile).WriteFile(outConfig); err != nil { + feedback.Fatal(tr("Error writing to file: %v", err), feedback.ErrGeneric) } - return res } diff --git a/internal/cli/config/delete.go b/internal/cli/config/delete.go index ae5bd7976b5..7ba5437cda2 100644 --- a/internal/cli/config/delete.go +++ b/internal/cli/config/delete.go @@ -16,17 +16,16 @@ package config import ( + "context" "os" - "github.com/arduino/arduino-cli/commands/daemon" - "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/arduino/arduino-cli/internal/cli/feedback" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) -func initDeleteCommand() *cobra.Command { +func initDeleteCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { deleteCommand := &cobra.Command{ Use: "delete", Short: tr("Deletes a settings key and all its sub keys."), @@ -35,25 +34,25 @@ func initDeleteCommand() *cobra.Command { " " + os.Args[0] + " config delete board_manager\n" + " " + os.Args[0] + " config delete board_manager.additional_urls", Args: cobra.ExactArgs(1), - Run: runDeleteCommand, + Run: func(cmd *cobra.Command, args []string) { + ctx := cmd.Context() + runDeleteCommand(ctx, srv, args) + }, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return configuration.Settings.AllKeys(), cobra.ShellCompDirectiveDefault + ctx := cmd.Context() + return getAllSettingsKeys(ctx, srv), cobra.ShellCompDirectiveDefault }, } return deleteCommand } -func runDeleteCommand(cmd *cobra.Command, args []string) { +func runDeleteCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, args []string) { logrus.Info("Executing `arduino-cli config delete`") - toDelete := args[0] - svc := daemon.ArduinoCoreServerImpl{} - _, err := svc.SettingsDelete(cmd.Context(), &rpc.SettingsDeleteRequest{Key: toDelete}) - if err != nil { - feedback.Fatal(tr("Cannot delete the key %[1]s: %[2]v", toDelete, err), feedback.ErrGeneric) - } - _, err = svc.SettingsWrite(cmd.Context(), &rpc.SettingsWriteRequest{FilePath: configuration.Settings.ConfigFileUsed()}) - if err != nil { - feedback.Fatal(tr("Cannot write the file %[1]s: %[2]v", configuration.Settings.ConfigFileUsed(), err), feedback.ErrGeneric) + key := args[0] + if _, err := srv.SettingsSetValue(ctx, &rpc.SettingsSetValueRequest{Key: key, EncodedValue: ""}); err != nil { + feedback.Fatal(tr("Cannot delete the key %[1]s: %[2]v", key, err), feedback.ErrGeneric) } + + saveConfiguration(ctx, srv) } diff --git a/internal/cli/config/dump.go b/internal/cli/config/dump.go index 9e10bf34f9b..5212ec9d1fa 100644 --- a/internal/cli/config/dump.go +++ b/internal/cli/config/dump.go @@ -18,34 +18,56 @@ package config import ( "os" - "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/arduino/arduino-cli/internal/cli/feedback" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "gopkg.in/yaml.v3" ) -func initDumpCommand() *cobra.Command { +func initDumpCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { var dumpCommand = &cobra.Command{ Use: "dump", Short: tr("Prints the current configuration"), Long: tr("Prints the current configuration."), Example: " " + os.Args[0] + " config dump", Args: cobra.NoArgs, - Run: runDumpCommand, + Run: func(cmd *cobra.Command, args []string) { + logrus.Info("Executing `arduino-cli config dump`") + res := &rawResult{} + switch feedback.GetFormat() { + case feedback.JSON, feedback.MinifiedJSON: + resp, err := srv.ConfigurationSave(cmd.Context(), &rpc.ConfigurationSaveRequest{SettingsFormat: "json"}) + if err != nil { + logrus.Fatalf("Error creating configuration: %v", err) + } + res.rawJSON = []byte(resp.GetEncodedSettings()) + case feedback.Text: + resp, err := srv.ConfigurationSave(cmd.Context(), &rpc.ConfigurationSaveRequest{SettingsFormat: "yaml"}) + if err != nil { + logrus.Fatalf("Error creating configuration: %v", err) + } + res.rawYAML = []byte(resp.GetEncodedSettings()) + default: + logrus.Fatalf("Unsupported format: %v", feedback.GetFormat()) + } + feedback.PrintResult(dumpResult{Config: res}) + }, } return dumpCommand } -func runDumpCommand(cmd *cobra.Command, args []string) { - logrus.Info("Executing `arduino-cli config dump`") - feedback.PrintResult(dumpResult{configuration.Settings.AllSettings()}) +type rawResult struct { + rawJSON []byte + rawYAML []byte +} + +func (r *rawResult) MarshalJSON() ([]byte, error) { + // it is already encoded in rawJSON field + return r.rawJSON, nil } -// output from this command requires special formatting, let's create a dedicated -// feedback.Result implementation type dumpResult struct { - Config map[string]interface{} `json:"config"` + Config *rawResult `json:"config"` } func (dr dumpResult) Data() interface{} { @@ -53,10 +75,6 @@ func (dr dumpResult) Data() interface{} { } func (dr dumpResult) String() string { - bs, err := yaml.Marshal(dr.Config) - if err != nil { - // Should never happen - panic(tr("unable to marshal config to YAML: %v", err)) - } - return string(bs) + // In case of text output do not wrap the output in outer JSON or YAML structure + return string(dr.Config.rawYAML) } diff --git a/internal/cli/config/get.go b/internal/cli/config/get.go index cb2a5cb0072..3dfb5345c20 100644 --- a/internal/cli/config/get.go +++ b/internal/cli/config/get.go @@ -16,12 +16,11 @@ package config import ( + "context" "encoding/json" "fmt" "os" - "github.com/arduino/arduino-cli/commands/daemon" - "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/arduino/arduino-cli/internal/cli/feedback" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/sirupsen/logrus" @@ -29,7 +28,7 @@ import ( "gopkg.in/yaml.v3" ) -func initGetCommand() *cobra.Command { +func initGetCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { getCommand := &cobra.Command{ Use: "get", Short: tr("Gets a settings key value."), @@ -39,26 +38,27 @@ func initGetCommand() *cobra.Command { " " + os.Args[0] + " config get daemon.port\n" + " " + os.Args[0] + " config get board_manager.additional_urls", Args: cobra.MinimumNArgs(1), - Run: runGetCommand, + Run: func(cmd *cobra.Command, args []string) { + runGetCommand(cmd.Context(), srv, args) + }, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return configuration.Settings.AllKeys(), cobra.ShellCompDirectiveDefault + ctx := cmd.Context() + return getAllSettingsKeys(ctx, srv), cobra.ShellCompDirectiveDefault }, } return getCommand } -func runGetCommand(cmd *cobra.Command, args []string) { +func runGetCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, args []string) { logrus.Info("Executing `arduino-cli config get`") - svc := daemon.ArduinoCoreServerImpl{} for _, toGet := range args { - resp, err := svc.SettingsGetValue(cmd.Context(), &rpc.SettingsGetValueRequest{Key: toGet}) + resp, err := srv.SettingsGetValue(ctx, &rpc.SettingsGetValueRequest{Key: toGet}) if err != nil { feedback.Fatal(tr("Cannot get the configuration key %[1]s: %[2]v", toGet, err), feedback.ErrGeneric) } var result getResult - err = json.Unmarshal([]byte(resp.GetJsonData()), &result.resp) - if err != nil { + if err := json.Unmarshal([]byte(resp.GetEncodedValue()), &result.resp); err != nil { // Should never happen... panic(fmt.Sprintf("Cannot parse JSON for key %[1]s: %[2]v", toGet, err)) } diff --git a/internal/cli/config/init.go b/internal/cli/config/init.go index afb05f74279..4e297fda7c5 100644 --- a/internal/cli/config/init.go +++ b/internal/cli/config/init.go @@ -16,16 +16,18 @@ package config import ( + "context" + "encoding/json" "os" "strings" + "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/internal/cli/arguments" - "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/arduino/arduino-cli/internal/cli/feedback" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/go-paths-helper" "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "github.com/spf13/viper" ) var ( @@ -50,7 +52,9 @@ func initInitCommand() *cobra.Command { PreRun: func(cmd *cobra.Command, args []string) { arguments.CheckFlagsConflicts(cmd, "dest-file", "dest-dir") }, - Run: runInitCommand, + Run: func(cmd *cobra.Command, args []string) { + runInitCommand(cmd.Context(), cmd) + }, } initCommand.Flags().StringVar(&destDir, "dest-dir", "", tr("Sets where to save the configuration file.")) initCommand.Flags().StringVar(&destFile, "dest-file", "", tr("Sets where to save the configuration file.")) @@ -58,11 +62,11 @@ func initInitCommand() *cobra.Command { return initCommand } -func runInitCommand(cmd *cobra.Command, args []string) { +func runInitCommand(ctx context.Context, cmd *cobra.Command) { logrus.Info("Executing `arduino-cli config init`") var configFileAbsPath *paths.Path - var absPath *paths.Path + var configFileDir *paths.Path var err error switch { @@ -71,46 +75,95 @@ func runInitCommand(cmd *cobra.Command, args []string) { if err != nil { feedback.Fatal(tr("Cannot find absolute path: %v", err), feedback.ErrGeneric) } + configFileDir = configFileAbsPath.Parent() - absPath = configFileAbsPath.Parent() - case destDir == "": - destDir = configuration.Settings.GetString("directories.Data") - fallthrough - default: - absPath, err = paths.New(destDir).Abs() + case destDir != "": + configFileDir, err = paths.New(destDir).Abs() if err != nil { feedback.Fatal(tr("Cannot find absolute path: %v", err), feedback.ErrGeneric) } - configFileAbsPath = absPath.Join(defaultFileName) + configFileAbsPath = configFileDir.Join(defaultFileName) + + default: + configFileAbsPath = paths.New(GetConfigFile(ctx)) + configFileDir = configFileAbsPath.Parent() } if !overwrite && configFileAbsPath.Exist() { feedback.Fatal(tr("Config file already exists, use --overwrite to discard the existing one."), feedback.ErrGeneric) } - logrus.Infof("Writing config file to: %s", absPath) + logrus.Infof("Writing config file to: %s", configFileDir) - if err := absPath.MkdirAll(); err != nil { + if err := configFileDir.MkdirAll(); err != nil { feedback.Fatal(tr("Cannot create config file directory: %v", err), feedback.ErrGeneric) } - newSettings := viper.New() - configuration.SetDefaults(newSettings) - configuration.BindFlags(cmd, newSettings) + tmpSrv := commands.NewArduinoCoreServer() - for _, url := range newSettings.GetStringSlice("board_manager.additional_urls") { - if strings.Contains(url, ",") { - feedback.Fatal(tr("Urls cannot contain commas. Separate multiple urls exported as env var with a space:\n%s", url), - feedback.ErrGeneric) - } + if _, err := tmpSrv.ConfigurationOpen(ctx, &rpc.ConfigurationOpenRequest{SettingsFormat: "yaml", EncodedSettings: ""}); err != nil { + feedback.Fatal(tr("Error creating configuration: %v", err), feedback.ErrGeneric) } - if err := newSettings.WriteConfigAs(configFileAbsPath.String()); err != nil { + // Ensure to always output an empty array for additional urls + if _, err := tmpSrv.SettingsSetValue(ctx, &rpc.SettingsSetValueRequest{ + Key: "board_manager.additional_urls", EncodedValue: "[]", + }); err != nil { + feedback.Fatal(tr("Error creating configuration: %v", err), feedback.ErrGeneric) + } + + ApplyGlobalFlagsToConfiguration(ctx, cmd, tmpSrv) + + resp, err := tmpSrv.ConfigurationSave(ctx, &rpc.ConfigurationSaveRequest{SettingsFormat: "yaml"}) + if err != nil { + feedback.Fatal(tr("Error creating configuration: %v", err), feedback.ErrGeneric) + } + + if err := configFileAbsPath.WriteFile([]byte(resp.GetEncodedSettings())); err != nil { feedback.Fatal(tr("Cannot create config file: %v", err), feedback.ErrGeneric) } + feedback.PrintResult(initResult{ConfigFileAbsPath: configFileAbsPath}) } +// ApplyGlobalFlagsToConfiguration overrides server settings with the flags from the command line +func ApplyGlobalFlagsToConfiguration(ctx context.Context, cmd *cobra.Command, srv rpc.ArduinoCoreServiceServer) { + set := func(k string, v any) { + if jsonValue, err := json.Marshal(v); err != nil { + feedback.Fatal(tr("Error creating configuration: %v", err), feedback.ErrGeneric) + } else if _, err := srv.SettingsSetValue(ctx, &rpc.SettingsSetValueRequest{ + Key: k, EncodedValue: string(jsonValue), + }); err != nil { + feedback.Fatal(tr("Error creating configuration: %v", err), feedback.ErrGeneric) + } + + } + + if f := cmd.Flags().Lookup("log-level"); f.Changed { + logLevel, _ := cmd.Flags().GetString("log-level") + set("logging.level", logLevel) + } + if f := cmd.Flags().Lookup("log-file"); f.Changed { + logFile, _ := cmd.Flags().GetString("log-file") + set("logging.file", logFile) + } + if f := cmd.Flags().Lookup("no-color"); f.Changed { + noColor, _ := cmd.Flags().GetBool("no-color") + set("output.no_color", noColor) + } + if f := cmd.Flags().Lookup("additional-urls"); f.Changed { + urls, _ := cmd.Flags().GetStringSlice("additional-urls") + for _, url := range urls { + if strings.Contains(url, ",") { + feedback.Fatal( + tr("Urls cannot contain commas. Separate multiple urls exported as env var with a space:\n%s", url), + feedback.ErrBadArgument) + } + } + set("board_manager.additional_urls", urls) + } +} + // output from this command requires special formatting, let's create a dedicated // feedback.Result implementation type initResult struct { diff --git a/internal/cli/config/remove.go b/internal/cli/config/remove.go index d7870172b0c..4114a146a45 100644 --- a/internal/cli/config/remove.go +++ b/internal/cli/config/remove.go @@ -16,16 +16,18 @@ package config import ( + "context" + "encoding/json" "os" - "reflect" + "slices" - "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/arduino/arduino-cli/internal/cli/feedback" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) -func initRemoveCommand() *cobra.Command { +func initRemoveCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { removeCommand := &cobra.Command{ Use: "remove", Short: tr("Removes one or more values from a setting."), @@ -34,38 +36,43 @@ func initRemoveCommand() *cobra.Command { " " + os.Args[0] + " config remove board_manager.additional_urls https://example.com/package_example_index.json\n" + " " + os.Args[0] + " config remove board_manager.additional_urls https://example.com/package_example_index.json https://another-url.com/package_another_index.json\n", Args: cobra.MinimumNArgs(2), - Run: runRemoveCommand, + Run: func(cmd *cobra.Command, args []string) { + ctx := cmd.Context() + runRemoveCommand(ctx, srv, args) + }, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return GetConfigurationKeys(), cobra.ShellCompDirectiveDefault + ctx := cmd.Context() + return getAllArraySettingsKeys(ctx, srv), cobra.ShellCompDirectiveDefault }, } return removeCommand } -func runRemoveCommand(cmd *cobra.Command, args []string) { +func runRemoveCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, args []string) { logrus.Info("Executing `arduino-cli config remove`") key := args[0] - kind := validateKey(key) - if kind != reflect.Slice { + if !slices.Contains(getAllArraySettingsKeys(ctx, srv), key) { msg := tr("The key '%[1]v' is not a list of items, can't remove from it.\nMaybe use '%[2]s'?", key, "config delete") feedback.Fatal(msg, feedback.ErrGeneric) } - mappedValues := map[string]bool{} - for _, v := range configuration.Settings.GetStringSlice(key) { - mappedValues[v] = true + var currentValues []string + if resp, err := srv.SettingsGetValue(ctx, &rpc.SettingsGetValueRequest{Key: key}); err != nil { + feedback.Fatal(tr("Cannot get the configuration key %[1]s: %[2]v", key, err), feedback.ErrGeneric) + } else if err := json.Unmarshal([]byte(resp.GetEncodedValue()), ¤tValues); err != nil { + feedback.Fatal(tr("Cannot get the configuration key %[1]s: %[2]v", key, err), feedback.ErrGeneric) } + for _, arg := range args[1:] { - delete(mappedValues, arg) + currentValues = slices.DeleteFunc(currentValues, func(in string) bool { return in == arg }) } - values := []string{} - for k := range mappedValues { - values = append(values, k) - } - configuration.Settings.Set(key, values) - if err := configuration.Settings.WriteConfig(); err != nil { - feedback.Fatal(tr("Can't write config file: %v", err), feedback.ErrGeneric) + if newValuesJSON, err := json.Marshal(currentValues); err != nil { + feedback.Fatal(tr("Cannot remove the configuration key %[1]s: %[2]v", key, err), feedback.ErrGeneric) + } else if _, err := srv.SettingsSetValue(ctx, &rpc.SettingsSetValueRequest{Key: key, EncodedValue: string(newValuesJSON)}); err != nil { + feedback.Fatal(tr("Cannot remove the configuration key %[1]s: %[2]v", key, err), feedback.ErrGeneric) } + + saveConfiguration(ctx, srv) } diff --git a/internal/cli/config/set.go b/internal/cli/config/set.go index 8fd002277da..a722f3f176b 100644 --- a/internal/cli/config/set.go +++ b/internal/cli/config/set.go @@ -16,17 +16,18 @@ package config import ( + "context" + "encoding/json" "os" - "reflect" - "strconv" - "github.com/arduino/arduino-cli/internal/cli/configuration" + f "github.com/arduino/arduino-cli/internal/algorithms" "github.com/arduino/arduino-cli/internal/cli/feedback" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) -func initSetCommand() *cobra.Command { +func initSetCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { setCommand := &cobra.Command{ Use: "set", Short: tr("Sets a setting value."), @@ -37,40 +38,40 @@ func initSetCommand() *cobra.Command { " " + os.Args[0] + " config set sketch.always_export_binaries true\n" + " " + os.Args[0] + " config set board_manager.additional_urls https://example.com/package_example_index.json https://another-url.com/package_another_index.json", Args: cobra.MinimumNArgs(2), - Run: runSetCommand, + Run: func(cmd *cobra.Command, args []string) { + runSetCommand(cmd.Context(), srv, args) + }, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return configuration.Settings.AllKeys(), cobra.ShellCompDirectiveDefault + ctx := cmd.Context() + return getAllSettingsKeys(ctx, srv), cobra.ShellCompDirectiveDefault }, } return setCommand } -func runSetCommand(cmd *cobra.Command, args []string) { +func runSetCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, args []string) { logrus.Info("Executing `arduino-cli config set`") - key := args[0] - kind := validateKey(key) - if kind != reflect.Slice && len(args) > 2 { - feedback.Fatal(tr("Can't set multiple values in key %v", key), feedback.ErrGeneric) + req := &rpc.SettingsSetValueRequest{ + Key: args[0], } - - var value interface{} - switch kind { - case reflect.Slice: - value = uniquify(args[1:]) - case reflect.String: - value = args[1] - case reflect.Bool: - var err error - value, err = strconv.ParseBool(args[1]) + if len(args) == 2 { + // Single value + req.EncodedValue = args[1] + req.ValueFormat = "cli" + } else { + // Uniq Array + jsonValues, err := json.Marshal(f.Uniq(args[1:])) if err != nil { - feedback.Fatal(tr("error parsing value: %v", err), feedback.ErrGeneric) + feedback.Fatal(tr("Error setting value: %v", err), feedback.ErrGeneric) } + req.EncodedValue = string(jsonValues) + req.ValueFormat = "json" } - configuration.Settings.Set(key, value) - - if err := configuration.Settings.WriteConfig(); err != nil { - feedback.Fatal(tr("Writing config file: %v", err), feedback.ErrGeneric) + if _, err := srv.SettingsSetValue(ctx, req); err != nil { + feedback.Fatal(tr("Error setting value: %v", err), feedback.ErrGeneric) } + + saveConfiguration(ctx, srv) } diff --git a/internal/cli/config/validate.go b/internal/cli/config/validate.go deleted file mode 100644 index f494bfaac0b..00000000000 --- a/internal/cli/config/validate.go +++ /dev/null @@ -1,61 +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 config - -import ( - "fmt" - "reflect" - - "github.com/arduino/arduino-cli/internal/cli/feedback" -) - -var validMap = map[string]reflect.Kind{ - "board_manager.additional_urls": reflect.Slice, - "daemon.port": reflect.String, - "directories.data": reflect.String, - "directories.downloads": reflect.String, - "directories.user": reflect.String, - "directories.builtin.tools": reflect.String, - "directories.builtin.libraries": reflect.String, - "library.enable_unsafe_install": reflect.Bool, - "locale": reflect.String, - "logging.file": reflect.String, - "logging.format": reflect.String, - "logging.level": reflect.String, - "sketch.always_export_binaries": reflect.Bool, - "metrics.addr": reflect.String, - "metrics.enabled": reflect.Bool, - "network.proxy": reflect.String, - "network.user_agent_ext": reflect.String, - "output.no_color": reflect.Bool, - "updater.enable_notification": reflect.Bool, -} - -func typeOf(key string) (reflect.Kind, error) { - t, ok := validMap[key] - if !ok { - return reflect.Invalid, fmt.Errorf(tr("Settings key doesn't exist")) - } - return t, nil -} - -func validateKey(key string) reflect.Kind { - kind, err := typeOf(key) - if err != nil { - feedback.FatalError(err, feedback.ErrGeneric) - } - return kind -} diff --git a/internal/cli/configuration/board_manager.go b/internal/cli/configuration/board_manager.go new file mode 100644 index 00000000000..22f982f5404 --- /dev/null +++ b/internal/cli/configuration/board_manager.go @@ -0,0 +1,23 @@ +// This file is part of arduino-cli. +// +// Copyright 2024 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 configuration + +func (settings *Settings) BoardManagerAdditionalUrls() []string { + if urls, ok, _ := settings.GetStringSliceOk("board_manager.additional_urls"); ok { + return urls + } + return settings.Defaults.GetStringSlice("board_manager.additional_urls") +} diff --git a/internal/cli/configuration/build_cache.go b/internal/cli/configuration/build_cache.go new file mode 100644 index 00000000000..a4bca322307 --- /dev/null +++ b/internal/cli/configuration/build_cache.go @@ -0,0 +1,34 @@ +// This file is part of arduino-cli. +// +// Copyright 2024 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 configuration + +import "time" + +// GetCompilationsBeforeBuildCachePurge returns the number of compilations before the build cache is purged. +func (s *Settings) GetCompilationsBeforeBuildCachePurge() uint { + if res, ok, _ := s.GetUintOk("build_cache.compilations_before_purge"); ok { + return res + } + return s.Defaults.GetUint("build_cache.compilations_before_purge") +} + +// GetBuildCacheTTL returns the time-to-live of the build cache (i.e. the minimum age to wait before purging the cache). +func (s *Settings) GetBuildCacheTTL() time.Duration { + if res, ok, _ := s.GetDurationOk("build_cache.ttl"); ok { + return res + } + return s.Defaults.GetDuration("build_cache.ttl") +} diff --git a/internal/cli/configuration/configuration.go b/internal/cli/configuration/configuration.go index e251b80c514..5176acc9a28 100644 --- a/internal/cli/configuration/configuration.go +++ b/internal/cli/configuration/configuration.go @@ -19,63 +19,29 @@ import ( "os" "path/filepath" "runtime" - "strings" "github.com/arduino/arduino-cli/internal/cli/feedback" + "github.com/arduino/arduino-cli/internal/go-configmap" "github.com/arduino/arduino-cli/internal/i18n" - paths "github.com/arduino/go-paths-helper" "github.com/arduino/go-win32-utils" - "github.com/spf13/cobra" - "github.com/spf13/viper" ) -// Settings is a global instance of viper holding configurations for the CLI and the gRPC consumers -var Settings *viper.Viper - var tr = i18n.Tr -// Init initialize defaults and read the configuration file. -// Please note the logging system hasn't been configured yet, -// so logging shouldn't be used here. -func Init(configFile string) *viper.Viper { - // Create a new viper instance with default values for all the settings - settings := viper.New() - SetDefaults(settings) - - // Set config name and config path - if configFilePath := paths.New(configFile); configFilePath != nil { - settings.SetConfigName(strings.TrimSuffix(configFilePath.Base(), configFilePath.Ext())) - settings.AddConfigPath(configFilePath.Parent().String()) - } else { - configDir := settings.GetString("directories.Data") - // Get default data path if none was provided - if configDir == "" { - configDir = getDefaultArduinoDataDir() - } - - settings.SetConfigName("arduino-cli") - settings.AddConfigPath(configDir) - } - - // Attempt to read config file - if err := settings.ReadInConfig(); err != nil { - // ConfigFileNotFoundError is acceptable, anything else - // should be reported to the user - if _, ok := err.(viper.ConfigFileNotFoundError); !ok { - feedback.Warning(tr("Error reading config file: %v", err)) - } - } - - return settings +// Settings contains the configuration of the Arduino CLI core service +type Settings struct { + *configmap.Map + Defaults *configmap.Map } -// BindFlags creates all the flags binding between the cobra Command and the instance of viper -func BindFlags(cmd *cobra.Command, settings *viper.Viper) { - settings.BindPFlag("logging.level", cmd.Flag("log-level")) - settings.BindPFlag("logging.file", cmd.Flag("log-file")) - settings.BindPFlag("logging.format", cmd.Flag("log-format")) - settings.BindPFlag("board_manager.additional_urls", cmd.Flag("additional-urls")) - settings.BindPFlag("output.no_color", cmd.Flag("no-color")) +// NewSettings creates a new instance of Settings with the default values set +func NewSettings() *Settings { + res := &Settings{ + Map: configmap.New(), + Defaults: configmap.New(), + } + SetDefaults(res) + return res } // getDefaultArduinoDataDir returns the full path to the default arduino folder @@ -128,11 +94,6 @@ func getDefaultUserDir() string { } } -// GetDefaultBuiltinLibrariesDir returns the full path to the default builtin libraries dir -func GetDefaultBuiltinLibrariesDir() string { - return filepath.Join(getDefaultArduinoDataDir(), "libraries") -} - // FindConfigFileInArgsFallbackOnEnv returns the config file path using the // argument '--config-file' (if specified), if empty looks for the ARDUINO_CONFIG_FILE env, // or looking in the current working dir @@ -145,5 +106,14 @@ func FindConfigFileInArgsFallbackOnEnv(args []string) string { } } } - return os.Getenv("ARDUINO_CONFIG_FILE") + if p, ok := os.LookupEnv("ARDUINO_CONFIG_FILE"); ok { + return p + } + if p, ok := os.LookupEnv("ARDUINO_DIRECTORIES_DATA"); ok { + return filepath.Join(p, "arduino-cli.yaml") + } + if p, ok := os.LookupEnv("ARDUINO_DATA_DIR"); ok { + return filepath.Join(p, "arduino-cli.yaml") + } + return filepath.Join(getDefaultArduinoDataDir(), "arduino-cli.yaml") } diff --git a/internal/cli/configuration/configuration_test.go b/internal/cli/configuration/configuration_test.go index 8b0fd3938cc..23af3a964e1 100644 --- a/internal/cli/configuration/configuration_test.go +++ b/internal/cli/configuration/configuration_test.go @@ -16,52 +16,35 @@ package configuration import ( - "fmt" - "os" "path/filepath" "testing" "github.com/stretchr/testify/require" ) -func tmpDirOrDie() string { - dir, err := os.MkdirTemp(os.TempDir(), "cli_test") - if err != nil { - panic(fmt.Sprintf("error creating tmp dir: %v", err)) - } - // Symlinks are evaluated becase the temp folder on Mac OS is inside /var, it's not writable - // and is a symlink to /private/var, we want the full path so we do this - dir, err = filepath.EvalSymlinks(dir) - if err != nil { - panic(fmt.Sprintf("error evaluating tmp dir symlink: %v", err)) - } - return dir -} - func TestInit(t *testing.T) { - tmp := tmpDirOrDie() - defer os.RemoveAll(tmp) - settings := Init(filepath.Join(tmp, "arduino-cli.yaml")) - require.NotNil(t, settings) + settings := NewSettings() - require.Equal(t, "info", settings.GetString("logging.level")) - require.Equal(t, "text", settings.GetString("logging.format")) + require.Equal(t, "info", settings.Defaults.GetString("logging.level")) + require.Equal(t, "text", settings.Defaults.GetString("logging.format")) - require.Empty(t, settings.GetStringSlice("board_manager.additional_urls")) + require.Empty(t, settings.Defaults.GetStringSlice("board_manager.additional_urls")) - require.NotEmpty(t, settings.GetString("directories.Data")) - require.NotEmpty(t, settings.GetString("directories.Downloads")) - require.NotEmpty(t, settings.GetString("directories.User")) + require.NotEmpty(t, settings.Defaults.GetString("directories.data")) + require.Empty(t, settings.Defaults.GetString("directories.downloads")) + require.NotEmpty(t, settings.DownloadsDir().String()) + require.NotEmpty(t, settings.Defaults.GetString("directories.user")) - require.Equal(t, "50051", settings.GetString("daemon.port")) + require.Equal(t, "50051", settings.Defaults.GetString("daemon.port")) - require.Equal(t, true, settings.GetBool("metrics.enabled")) - require.Equal(t, ":9090", settings.GetString("metrics.addr")) + require.Equal(t, true, settings.Defaults.GetBool("metrics.enabled")) + require.Equal(t, ":9090", settings.Defaults.GetString("metrics.addr")) } func TestFindConfigFile(t *testing.T) { + defaultConfigFile := filepath.Join(getDefaultArduinoDataDir(), "arduino-cli.yaml") configFile := FindConfigFileInArgsFallbackOnEnv([]string{"--config-file"}) - require.Equal(t, "", configFile) + require.Equal(t, defaultConfigFile, configFile) configFile = FindConfigFileInArgsFallbackOnEnv([]string{"--config-file", "some/path/to/config"}) require.Equal(t, "some/path/to/config", configFile) @@ -70,7 +53,7 @@ func TestFindConfigFile(t *testing.T) { require.Equal(t, "some/path/to/config/arduino-cli.yaml", configFile) configFile = FindConfigFileInArgsFallbackOnEnv([]string{}) - require.Equal(t, "", configFile) + require.Equal(t, defaultConfigFile, configFile) t.Setenv("ARDUINO_CONFIG_FILE", "some/path/to/config") configFile = FindConfigFileInArgsFallbackOnEnv([]string{}) diff --git a/internal/cli/configuration/daemon.go b/internal/cli/configuration/daemon.go new file mode 100644 index 00000000000..9ae053c2aee --- /dev/null +++ b/internal/cli/configuration/daemon.go @@ -0,0 +1,23 @@ +// This file is part of arduino-cli. +// +// Copyright 2024 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 configuration + +func (s *Settings) DaemonPort() string { + if port, ok, _ := s.GetStringOk("daemon.port"); ok { + return port + } + return s.Defaults.GetString("daemon.port") +} diff --git a/internal/cli/configuration/defaults.go b/internal/cli/configuration/defaults.go index be1a0088a62..8c05b793d19 100644 --- a/internal/cli/configuration/defaults.go +++ b/internal/cli/configuration/defaults.go @@ -16,57 +16,78 @@ package configuration import ( - "path/filepath" - "strings" + "os" "time" - - "github.com/spf13/viper" ) // SetDefaults sets the default values for certain keys -func SetDefaults(settings *viper.Viper) { +func SetDefaults(settings *Settings) { + setKeyTypeSchema := func(k string, v any) { + settings.SetKeyTypeSchema(k, v) + settings.Defaults.SetKeyTypeSchema(k, v) + } + setDefaultValueAndKeyTypeSchema := func(k string, v any) { + setKeyTypeSchema(k, v) + settings.Defaults.Set(k, v) + } + // logging - settings.SetDefault("logging.level", "info") - settings.SetDefault("logging.format", "text") + setDefaultValueAndKeyTypeSchema("logging.level", "info") + setDefaultValueAndKeyTypeSchema("logging.format", "text") + setKeyTypeSchema("logging.file", "") // Libraries - settings.SetDefault("library.enable_unsafe_install", false) + setDefaultValueAndKeyTypeSchema("library.enable_unsafe_install", false) // Boards Manager - settings.SetDefault("board_manager.additional_urls", []string{}) + setDefaultValueAndKeyTypeSchema("board_manager.additional_urls", []string{}) // arduino directories - settings.SetDefault("directories.Data", getDefaultArduinoDataDir()) - settings.SetDefault("directories.Downloads", filepath.Join(getDefaultArduinoDataDir(), "staging")) - settings.SetDefault("directories.User", getDefaultUserDir()) + setDefaultValueAndKeyTypeSchema("directories.data", getDefaultArduinoDataDir()) + setDefaultValueAndKeyTypeSchema("directories.downloads", "") + setDefaultValueAndKeyTypeSchema("directories.user", getDefaultUserDir()) + setKeyTypeSchema("directories.builtin.libraries", "") // Sketch compilation - settings.SetDefault("sketch.always_export_binaries", false) - settings.SetDefault("build_cache.ttl", time.Hour*24*30) - settings.SetDefault("build_cache.compilations_before_purge", 10) + setDefaultValueAndKeyTypeSchema("sketch.always_export_binaries", false) + setDefaultValueAndKeyTypeSchema("build_cache.ttl", (time.Hour * 24 * 30).String()) + setDefaultValueAndKeyTypeSchema("build_cache.compilations_before_purge", uint(10)) // daemon settings - settings.SetDefault("daemon.port", "50051") + setDefaultValueAndKeyTypeSchema("daemon.port", "50051") // metrics settings - settings.SetDefault("metrics.enabled", true) - settings.SetDefault("metrics.addr", ":9090") + setDefaultValueAndKeyTypeSchema("metrics.enabled", true) + setDefaultValueAndKeyTypeSchema("metrics.addr", ":9090") // output settings - settings.SetDefault("output.no_color", false) + setDefaultValueAndKeyTypeSchema("output.no_color", false) // updater settings - settings.SetDefault("updater.enable_notification", true) + setDefaultValueAndKeyTypeSchema("updater.enable_notification", true) + + // network settings + setKeyTypeSchema("network.proxy", "") + setKeyTypeSchema("network.user_agent_ext", "") + + // locale + setDefaultValueAndKeyTypeSchema("locale", "en") +} +// InjectEnvVars change settings based on the environment variables values +func InjectEnvVars(settings *Settings) { // Bind env vars - settings.SetEnvPrefix("ARDUINO") - settings.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) - settings.AutomaticEnv() + settings.InjectEnvVars(os.Environ(), "ARDUINO") // 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") - settings.BindEnv("sketch.always_export_binaries", "ARDUINO_SKETCH_ALWAYS_EXPORT_BINARIES") + setIfEnvExists := func(key, env string) { + if v, ok := os.LookupEnv(env); ok { + settings.SetFromCLIArgs(key, v) + } + } + setIfEnvExists("library.enable_unsafe_install", "ARDUINO_ENABLE_UNSAFE_LIBRARY_INSTALL") + setIfEnvExists("directories.user", "ARDUINO_SKETCHBOOK_DIR") + setIfEnvExists("directories.downloads", "ARDUINO_DOWNLOADS_DIR") + setIfEnvExists("directories.data", "ARDUINO_DATA_DIR") + setIfEnvExists("sketch.always_export_binaries", "ARDUINO_SKETCH_ALWAYS_EXPORT_BINARIES") } diff --git a/internal/cli/configuration/directories.go b/internal/cli/configuration/directories.go index 27669e9017c..a58aa21f885 100644 --- a/internal/cli/configuration/directories.go +++ b/internal/cli/configuration/directories.go @@ -17,24 +17,18 @@ package configuration import ( "github.com/arduino/go-paths-helper" - "github.com/spf13/viper" ) // HardwareDirectories returns all paths that may contains hardware packages. -func HardwareDirectories(settings *viper.Viper) paths.PathList { +func (settings *Settings) HardwareDirectories() paths.PathList { res := paths.PathList{} - if settings.IsSet("directories.Data") { - packagesDir := PackagesDir(Settings) - if packagesDir.IsDir() { - res.Add(packagesDir) - } + if packagesDir := settings.PackagesDir(); packagesDir.IsDir() { + res.Add(packagesDir) } - if settings.IsSet("directories.User") { - skDir := paths.New(settings.GetString("directories.User")) - hwDir := skDir.Join("hardware") - if hwDir.IsDir() { + if userDir, ok, _ := settings.GetStringOk("directories.user"); ok { + if hwDir := paths.New(userDir, "hardware"); hwDir.IsDir() { res.Add(hwDir) } } @@ -44,34 +38,51 @@ func HardwareDirectories(settings *viper.Viper) paths.PathList { // IDEBuiltinLibrariesDir returns the IDE-bundled libraries path. Usually // this directory is present in the Arduino IDE. -func IDEBuiltinLibrariesDir(settings *viper.Viper) *paths.Path { - return paths.New(Settings.GetString("directories.builtin.Libraries")) +func (settings *Settings) IDEBuiltinLibrariesDir() *paths.Path { + if builtinLibsDir, ok, _ := settings.GetStringOk("directories.builtin.libraries"); ok { + return paths.New(builtinLibsDir) + } + return nil } // LibrariesDir returns the full path to the user directory containing // custom libraries -func LibrariesDir(settings *viper.Viper) *paths.Path { - return paths.New(settings.GetString("directories.User")).Join("libraries") +func (settings *Settings) LibrariesDir() *paths.Path { + return settings.UserDir().Join("libraries") +} + +// UserDir returns the full path to the user directory +func (settings *Settings) UserDir() *paths.Path { + if userDir, ok, _ := settings.GetStringOk("directories.user"); ok { + return paths.New(userDir) + } + return paths.New(settings.Defaults.GetString("directories.user")) } // PackagesDir returns the full path to the packages folder -func PackagesDir(settings *viper.Viper) *paths.Path { - return DataDir(settings).Join("packages") +func (settings *Settings) PackagesDir() *paths.Path { + return settings.DataDir().Join("packages") } // ProfilesCacheDir returns the full path to the profiles cache directory // (it contains all the platforms and libraries used to compile a sketch // using profiles) -func ProfilesCacheDir(settings *viper.Viper) *paths.Path { - return DataDir(settings).Join("internal") +func (settings *Settings) ProfilesCacheDir() *paths.Path { + return settings.DataDir().Join("internal") } // DataDir returns the full path to the data directory -func DataDir(settings *viper.Viper) *paths.Path { - return paths.New(settings.GetString("directories.Data")) +func (settings *Settings) DataDir() *paths.Path { + if dataDir, ok, _ := settings.GetStringOk("directories.data"); ok { + return paths.New(dataDir) + } + return paths.New(settings.Defaults.GetString("directories.data")) } // DownloadsDir returns the full path to the download cache directory -func DownloadsDir(settings *viper.Viper) *paths.Path { - return paths.New(settings.GetString("directories.Downloads")) +func (settings *Settings) DownloadsDir() *paths.Path { + if downloadDir, ok, _ := settings.GetStringOk("directories.downloads"); ok { + return paths.New(downloadDir) + } + return settings.DataDir().Join("staging") } diff --git a/internal/cli/configuration/libraries.go b/internal/cli/configuration/libraries.go new file mode 100644 index 00000000000..6dc73b9b0d6 --- /dev/null +++ b/internal/cli/configuration/libraries.go @@ -0,0 +1,20 @@ +// This file is part of arduino-cli. +// +// Copyright 2024 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 configuration + +func (s *Settings) LibraryEnableUnsafeInstall() bool { + return s.GetBool("library.enable_unsafe_install") +} diff --git a/internal/cli/configuration/locale.go b/internal/cli/configuration/locale.go new file mode 100644 index 00000000000..e292ea8f170 --- /dev/null +++ b/internal/cli/configuration/locale.go @@ -0,0 +1,20 @@ +// This file is part of arduino-cli. +// +// Copyright 2024 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 configuration + +func (s *Settings) Locale() string { + return s.Defaults.GetString("locale") +} diff --git a/internal/cli/configuration/logging.go b/internal/cli/configuration/logging.go new file mode 100644 index 00000000000..73f1bacab35 --- /dev/null +++ b/internal/cli/configuration/logging.go @@ -0,0 +1,42 @@ +// This file is part of arduino-cli. +// +// Copyright 2024 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 configuration + +import "github.com/arduino/go-paths-helper" + +func (s *Settings) LoggingLevel() string { + if l, ok, _ := s.GetStringOk("logging.level"); ok { + return l + } + return s.Defaults.GetString("logging.level") +} + +func (s *Settings) LoggingFormat() string { + if l, ok, _ := s.GetStringOk("logging.format"); ok { + return l + } + return s.Defaults.GetString("logging.format") +} + +func (s *Settings) LoggingFile() *paths.Path { + if l, ok, _ := s.GetStringOk("logging.file"); ok && l != "" { + return paths.New(l) + } + if l, ok, _ := s.Defaults.GetStringOk("logging.file"); ok && l != "" { + return paths.New(l) + } + return nil +} diff --git a/internal/cli/configuration/network.go b/internal/cli/configuration/network.go index 793c67de155..7fac7ee6890 100644 --- a/internal/cli/configuration/network.go +++ b/internal/cli/configuration/network.go @@ -1,6 +1,6 @@ // This file is part of arduino-cli. // -// Copyright 2020 ARDUINO SA (http://www.arduino.cc/) +// Copyright 2024 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. @@ -17,16 +17,18 @@ package configuration import ( "fmt" + "net/http" "net/url" "os" "runtime" + "github.com/arduino/arduino-cli/commands/cmderrors" "github.com/arduino/arduino-cli/version" - "github.com/spf13/viper" + "go.bug.st/downloader/v2" ) // UserAgent returns the user agent (mainly used by HTTP clients) -func UserAgent(settings *viper.Viper) string { +func (settings *Settings) UserAgent() string { subComponent := "" if settings != nil { subComponent = settings.GetString("network.user_agent_ext") @@ -49,15 +51,14 @@ func UserAgent(settings *viper.Viper) string { extendedUA) } +// ExtraUserAgent returns the extended user-agent section provided via configuration settings +func (settings *Settings) ExtraUserAgent() string { + return settings.GetString("network.user_agent_ext") +} + // NetworkProxy returns the proxy configuration (mainly used by HTTP clients) -func NetworkProxy(settings *viper.Viper) (*url.URL, error) { - if settings == nil || !settings.IsSet("network.proxy") { - return nil, nil - } - if proxyConfig := settings.GetString("network.proxy"); proxyConfig == "" { - // empty configuration - // this workaround must be here until viper can UnSet properties: - // https://github.com/spf13/viper/pull/519 +func (settings *Settings) NetworkProxy() (*url.URL, error) { + if proxyConfig, ok, _ := settings.GetStringOk("network.proxy"); !ok { return nil, nil } else if proxy, err := url.Parse(proxyConfig); err != nil { return nil, fmt.Errorf(tr("Invalid network.proxy '%[1]s': %[2]s"), proxyConfig, err) @@ -65,3 +66,42 @@ func NetworkProxy(settings *viper.Viper) (*url.URL, error) { return proxy, nil } } + +// NewHttpClient returns a new http client for use in the arduino-cli +func (settings *Settings) NewHttpClient() (*http.Client, error) { + proxy, err := settings.NetworkProxy() + if err != nil { + return nil, err + } + return &http.Client{ + Transport: &httpClientRoundTripper{ + transport: &http.Transport{ + Proxy: http.ProxyURL(proxy), + }, + userAgent: settings.UserAgent(), + }, + }, nil +} + +type httpClientRoundTripper struct { + transport http.RoundTripper + userAgent string +} + +func (h *httpClientRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + req.Header.Add("User-Agent", h.userAgent) + return h.transport.RoundTrip(req) +} + +// DownloaderConfig returns the downloader configuration based on current settings. +func (settings *Settings) DownloaderConfig() (downloader.Config, error) { + httpClient, err := settings.NewHttpClient() + if err != nil { + return downloader.Config{}, &cmderrors.InvalidArgumentError{ + Message: tr("Could not connect via HTTP"), + Cause: err} + } + return downloader.Config{ + HttpClient: *httpClient, + }, nil +} diff --git a/internal/arduino/httpclient/httpclient_test.go b/internal/cli/configuration/network_test.go similarity index 77% rename from internal/arduino/httpclient/httpclient_test.go rename to internal/cli/configuration/network_test.go index a2dbc61c291..6c8a7def80d 100644 --- a/internal/arduino/httpclient/httpclient_test.go +++ b/internal/cli/configuration/network_test.go @@ -13,16 +13,16 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package httpclient +package configuration_test import ( "fmt" "io" "net/http" "net/http/httptest" - "net/url" "testing" + "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/stretchr/testify/require" ) @@ -32,9 +32,10 @@ func TestUserAgentHeader(t *testing.T) { })) defer ts.Close() - client := NewWithConfig(&Config{ - UserAgent: "test-user-agent", - }) + settings := configuration.NewSettings() + require.NoError(t, settings.Set("network.user_agent_ext", "test-user-agent")) + client, err := settings.NewHttpClient() + require.NoError(t, err) request, err := http.NewRequest("GET", ts.URL, nil) require.NoError(t, err) @@ -45,7 +46,8 @@ func TestUserAgentHeader(t *testing.T) { b, err := io.ReadAll(response.Body) require.NoError(t, err) - require.Equal(t, "test-user-agent", string(b)) + fmt.Println("RESPONSE:", string(b)) + require.Contains(t, string(b), "test-user-agent") } func TestProxy(t *testing.T) { @@ -54,13 +56,11 @@ func TestProxy(t *testing.T) { })) defer ts.Close() - proxyURL, err := url.Parse(ts.URL) + settings := configuration.NewSettings() + settings.Set("network.proxy", ts.URL) + client, err := settings.NewHttpClient() require.NoError(t, err) - client := NewWithConfig(&Config{ - Proxy: proxyURL, - }) - request, err := http.NewRequest("GET", "http://arduino.cc", nil) require.NoError(t, err) diff --git a/commands/board/board.go b/internal/cli/configuration/output.go similarity index 81% rename from commands/board/board.go rename to internal/cli/configuration/output.go index 3baebc26d27..67ae5e72d99 100644 --- a/commands/board/board.go +++ b/internal/cli/configuration/output.go @@ -1,6 +1,6 @@ // This file is part of arduino-cli. // -// Copyright 2020 ARDUINO SA (http://www.arduino.cc/) +// Copyright 2024 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. @@ -13,8 +13,8 @@ // Arduino software without disclosing the source code of your own applications. // To purchase a commercial license, send an email to license@arduino.cc. -package board +package configuration -import "github.com/arduino/arduino-cli/internal/i18n" - -var tr = i18n.Tr +func (s *Settings) NoColor() bool { + return s.GetBool("output.no_color") +} diff --git a/internal/cli/configuration/sketch.go b/internal/cli/configuration/sketch.go new file mode 100644 index 00000000000..27638b67df5 --- /dev/null +++ b/internal/cli/configuration/sketch.go @@ -0,0 +1,25 @@ +// This file is part of arduino-cli. +// +// Copyright 2024 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 configuration + +// SketchAlwaysExportBinaries returns true if the compile command should +// export binaries by default. +func (settings *Settings) SketchAlwaysExportBinaries() bool { + if res, ok, _ := settings.GetBoolOk("sketch.always_export_binaries"); ok { + return res + } + return settings.Defaults.GetBool("sketch.always_export_binaries") +} diff --git a/internal/cli/configuration/updater.go b/internal/cli/configuration/updater.go new file mode 100644 index 00000000000..3f553782254 --- /dev/null +++ b/internal/cli/configuration/updater.go @@ -0,0 +1,23 @@ +// This file is part of arduino-cli. +// +// Copyright 2024 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 configuration + +func (s *Settings) UpdaterEnableNotification() bool { + if en, ok, _ := s.GetBoolOk("updater.enable_notification"); ok { + return en + } + return s.GetBool("updater.enable_notification") +} diff --git a/internal/cli/core/core.go b/internal/cli/core/core.go index 3792ad13227..ac337df6c9e 100644 --- a/internal/cli/core/core.go +++ b/internal/cli/core/core.go @@ -19,13 +19,14 @@ import ( "os" "github.com/arduino/arduino-cli/internal/i18n" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/spf13/cobra" ) var tr = i18n.Tr // NewCommand created a new `core` command -func NewCommand() *cobra.Command { +func NewCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { coreCommand := &cobra.Command{ Use: "core", Short: tr("Arduino core operations."), @@ -33,13 +34,13 @@ func NewCommand() *cobra.Command { Example: " " + os.Args[0] + " core update-index", } - coreCommand.AddCommand(initDownloadCommand()) - coreCommand.AddCommand(initInstallCommand()) - coreCommand.AddCommand(initListCommand()) - coreCommand.AddCommand(initUpdateIndexCommand()) - coreCommand.AddCommand(initUpgradeCommand()) - coreCommand.AddCommand(initUninstallCommand()) - coreCommand.AddCommand(initSearchCommand()) + coreCommand.AddCommand(initDownloadCommand(srv)) + coreCommand.AddCommand(initInstallCommand(srv)) + coreCommand.AddCommand(initListCommand(srv)) + coreCommand.AddCommand(initUpdateIndexCommand(srv)) + coreCommand.AddCommand(initUpgradeCommand(srv)) + coreCommand.AddCommand(initUninstallCommand(srv)) + coreCommand.AddCommand(initSearchCommand(srv)) return coreCommand } diff --git a/internal/cli/core/download.go b/internal/cli/core/download.go index daa87f04ef2..fb5fe3f3818 100644 --- a/internal/cli/core/download.go +++ b/internal/cli/core/download.go @@ -20,7 +20,7 @@ import ( "fmt" "os" - "github.com/arduino/arduino-cli/commands/core" + "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/internal/cli/arguments" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/cli/instance" @@ -29,7 +29,7 @@ import ( "github.com/spf13/cobra" ) -func initDownloadCommand() *cobra.Command { +func initDownloadCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { downloadCommand := &cobra.Command{ Use: fmt.Sprintf("download [%s:%s[@%s]]...", tr("PACKAGER"), tr("ARCH"), tr("VERSION")), Short: tr("Downloads one or more cores and corresponding tool dependencies."), @@ -38,20 +38,22 @@ func initDownloadCommand() *cobra.Command { " " + os.Args[0] + " core download arduino:samd # " + tr("download the latest version of Arduino SAMD core.") + "\n" + " " + os.Args[0] + " core download arduino:samd@1.6.9 # " + tr("download a specific version (in this case 1.6.9)."), Args: cobra.MinimumNArgs(1), - Run: runDownloadCommand, + Run: func(cmd *cobra.Command, args []string) { + runDownloadCommand(cmd.Context(), srv, args) + }, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return arguments.GetInstallableCores(), cobra.ShellCompDirectiveDefault + return arguments.GetInstallableCores(cmd.Context(), srv), cobra.ShellCompDirectiveDefault }, } return downloadCommand } -func runDownloadCommand(cmd *cobra.Command, args []string) { - inst := instance.CreateAndInit() +func runDownloadCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, args []string) { + inst := instance.CreateAndInit(ctx, srv) logrus.Info("Executing `arduino-cli core download`") - platformsRefs, err := arguments.ParseReferences(args) + platformsRefs, err := arguments.ParseReferences(ctx, srv, args) if err != nil { feedback.Fatal(tr("Invalid argument passed: %v", err), feedback.ErrBadArgument) } @@ -63,8 +65,8 @@ func runDownloadCommand(cmd *cobra.Command, args []string) { Architecture: platformRef.Architecture, Version: platformRef.Version, } - _, err := core.PlatformDownload(context.Background(), platformDownloadreq, feedback.ProgressBar()) - if err != nil { + stream := commands.PlatformDownloadStreamResponseToCallbackFunction(ctx, feedback.ProgressBar()) + if err := srv.PlatformDownload(platformDownloadreq, stream); err != nil { feedback.Fatal(tr("Error downloading %[1]s: %[2]v", args[i], err), feedback.ErrNetwork) } } diff --git a/internal/cli/core/install.go b/internal/cli/core/install.go index 8193a8b4344..988602fc2ac 100644 --- a/internal/cli/core/install.go +++ b/internal/cli/core/install.go @@ -20,7 +20,7 @@ import ( "fmt" "os" - "github.com/arduino/arduino-cli/commands/core" + "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/internal/cli/arguments" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/cli/instance" @@ -29,7 +29,7 @@ import ( "github.com/spf13/cobra" ) -func initInstallCommand() *cobra.Command { +func initInstallCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { var noOverwrite bool var scriptFlags arguments.PrePostScriptsFlags installCommand := &cobra.Command{ @@ -45,10 +45,10 @@ func initInstallCommand() *cobra.Command { arguments.CheckFlagsConflicts(cmd, "run-post-install", "skip-post-install") }, Run: func(cmd *cobra.Command, args []string) { - runInstallCommand(args, scriptFlags, noOverwrite) + runInstallCommand(cmd.Context(), srv, args, scriptFlags, noOverwrite) }, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return arguments.GetInstallableCores(), cobra.ShellCompDirectiveDefault + return arguments.GetInstallableCores(cmd.Context(), srv), cobra.ShellCompDirectiveDefault }, } scriptFlags.AddToCommand(installCommand) @@ -56,11 +56,11 @@ func initInstallCommand() *cobra.Command { return installCommand } -func runInstallCommand(args []string, scriptFlags arguments.PrePostScriptsFlags, noOverwrite bool) { - inst := instance.CreateAndInit() +func runInstallCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, args []string, scriptFlags arguments.PrePostScriptsFlags, noOverwrite bool) { logrus.Info("Executing `arduino-cli core install`") + inst := instance.CreateAndInit(ctx, srv) - platformsRefs, err := arguments.ParseReferences(args) + platformsRefs, err := arguments.ParseReferences(ctx, srv, args) if err != nil { feedback.Fatal(tr("Invalid argument passed: %v", err), feedback.ErrBadArgument) } @@ -75,8 +75,8 @@ func runInstallCommand(args []string, scriptFlags arguments.PrePostScriptsFlags, NoOverwrite: noOverwrite, SkipPreUninstall: scriptFlags.DetectSkipPreUninstallValue(), } - _, err := core.PlatformInstall(context.Background(), platformInstallRequest, feedback.ProgressBar(), feedback.TaskProgress()) - if err != nil { + stream := commands.PlatformInstallStreamResponseToCallbackFunction(ctx, feedback.ProgressBar(), feedback.TaskProgress()) + if err := srv.PlatformInstall(platformInstallRequest, stream); err != nil { feedback.Fatal(tr("Error during install: %v", err), feedback.ErrGeneric) } } diff --git a/internal/cli/core/list.go b/internal/cli/core/list.go index a2434ab676f..3c2dd954818 100644 --- a/internal/cli/core/list.go +++ b/internal/cli/core/list.go @@ -16,9 +16,9 @@ package core import ( + "context" "os" - "github.com/arduino/arduino-cli/commands/core" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/cli/feedback/result" "github.com/arduino/arduino-cli/internal/cli/feedback/table" @@ -28,7 +28,7 @@ import ( "github.com/spf13/cobra" ) -func initListCommand() *cobra.Command { +func initListCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { var updatableOnly bool var all bool listCommand := &cobra.Command{ @@ -38,7 +38,7 @@ func initListCommand() *cobra.Command { Example: " " + os.Args[0] + " core list", Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { - runListCommand(args, all, updatableOnly) + runListCommand(cmd.Context(), srv, all, updatableOnly) }, } listCommand.Flags().BoolVar(&updatableOnly, "updatable", false, tr("List updatable platforms.")) @@ -46,21 +46,21 @@ func initListCommand() *cobra.Command { return listCommand } -func runListCommand(args []string, all bool, updatableOnly bool) { - inst := instance.CreateAndInit() +func runListCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, all bool, updatableOnly bool) { + inst := instance.CreateAndInit(ctx, srv) logrus.Info("Executing `arduino-cli core list`") - List(inst, all, updatableOnly) + List(ctx, srv, inst, all, updatableOnly) } // List gets and prints a list of installed platforms. -func List(inst *rpc.Instance, all bool, updatableOnly bool) { - platforms := GetList(inst, all, updatableOnly) +func List(ctx context.Context, srv rpc.ArduinoCoreServiceServer, inst *rpc.Instance, all bool, updatableOnly bool) { + platforms := GetList(ctx, srv, inst, all, updatableOnly) feedback.PrintResult(newCoreListResult(platforms, updatableOnly)) } // GetList returns a list of installed platforms. -func GetList(inst *rpc.Instance, all bool, updatableOnly bool) []*rpc.PlatformSummary { - platforms, err := core.PlatformSearch(&rpc.PlatformSearchRequest{ +func GetList(ctx context.Context, srv rpc.ArduinoCoreServiceServer, inst *rpc.Instance, all bool, updatableOnly bool) []*rpc.PlatformSummary { + platforms, err := srv.PlatformSearch(ctx, &rpc.PlatformSearchRequest{ Instance: inst, ManuallyInstalled: true, }) diff --git a/internal/cli/core/search.go b/internal/cli/core/search.go index d24e486ade7..8a58cbf5a57 100644 --- a/internal/cli/core/search.go +++ b/internal/cli/core/search.go @@ -23,7 +23,6 @@ import ( "time" "github.com/arduino/arduino-cli/commands" - "github.com/arduino/arduino-cli/commands/core" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/cli/feedback/result" "github.com/arduino/arduino-cli/internal/cli/feedback/table" @@ -33,7 +32,7 @@ import ( "github.com/spf13/cobra" ) -func initSearchCommand() *cobra.Command { +func initSearchCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { var allVersions bool searchCommand := &cobra.Command{ Use: fmt.Sprintf("search <%s...>", tr("keywords")), @@ -42,7 +41,7 @@ func initSearchCommand() *cobra.Command { Example: " " + os.Args[0] + " core search MKRZero -a -v", Args: cobra.ArbitraryArgs, Run: func(cmd *cobra.Command, args []string) { - runSearchCommand(cmd, args, allVersions) + runSearchCommand(cmd.Context(), srv, args, allVersions) }, } searchCommand.Flags().BoolVarP(&allVersions, "all", "a", false, tr("Show all available core versions.")) @@ -53,20 +52,20 @@ func initSearchCommand() *cobra.Command { // indexUpdateInterval specifies the time threshold over which indexes are updated const indexUpdateInterval = 24 * time.Hour -func runSearchCommand(cmd *cobra.Command, args []string, allVersions bool) { - inst := instance.CreateAndInit() +func runSearchCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, args []string, allVersions bool) { + inst := instance.CreateAndInit(ctx, srv) - res, err := commands.UpdateIndex( - context.Background(), + stream, res := commands.UpdateIndexStreamResponseToCallbackFunction(ctx, feedback.ProgressBar()) + err := srv.UpdateIndex( &rpc.UpdateIndexRequest{Instance: inst, UpdateIfOlderThanSecs: int64(indexUpdateInterval.Seconds())}, - feedback.ProgressBar()) + stream) if err != nil { feedback.FatalError(err, feedback.ErrGeneric) } - for _, idxRes := range res.GetUpdatedIndexes() { + for _, idxRes := range res().GetUpdatedIndexes() { if idxRes.GetStatus() == rpc.IndexUpdateReport_STATUS_UPDATED { // At least one index has been updated, reinitialize the instance - instance.Init(inst) + instance.Init(ctx, srv, inst) break } } @@ -74,7 +73,7 @@ func runSearchCommand(cmd *cobra.Command, args []string, allVersions bool) { arguments := strings.ToLower(strings.Join(args, " ")) logrus.Infof("Executing `arduino-cli core search` with args: '%s'", arguments) - resp, err := core.PlatformSearch(&rpc.PlatformSearchRequest{ + resp, err := srv.PlatformSearch(ctx, &rpc.PlatformSearchRequest{ Instance: inst, SearchArgs: arguments, }) diff --git a/internal/cli/core/uninstall.go b/internal/cli/core/uninstall.go index 10992ef9bdf..a5df772935c 100644 --- a/internal/cli/core/uninstall.go +++ b/internal/cli/core/uninstall.go @@ -20,7 +20,7 @@ import ( "fmt" "os" - "github.com/arduino/arduino-cli/commands/core" + "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/internal/cli/arguments" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/cli/instance" @@ -29,7 +29,7 @@ import ( "github.com/spf13/cobra" ) -func initUninstallCommand() *cobra.Command { +func initUninstallCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { var preUninstallFlags arguments.PrePostScriptsFlags uninstallCommand := &cobra.Command{ Use: fmt.Sprintf("uninstall %s:%s ...", tr("PACKAGER"), tr("ARCH")), @@ -38,21 +38,21 @@ func initUninstallCommand() *cobra.Command { Example: " " + os.Args[0] + " core uninstall arduino:samd\n", Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { - runUninstallCommand(args, preUninstallFlags) + runUninstallCommand(cmd.Context(), srv, args, preUninstallFlags) }, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return arguments.GetUninstallableCores(), cobra.ShellCompDirectiveDefault + return arguments.GetUninstallableCores(cmd.Context(), srv), cobra.ShellCompDirectiveDefault }, } preUninstallFlags.AddToCommand(uninstallCommand) return uninstallCommand } -func runUninstallCommand(args []string, preUninstallFlags arguments.PrePostScriptsFlags) { - inst := instance.CreateAndInit() +func runUninstallCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, args []string, preUninstallFlags arguments.PrePostScriptsFlags) { logrus.Info("Executing `arduino-cli core uninstall`") + inst := instance.CreateAndInit(ctx, srv) - platformsRefs, err := arguments.ParseReferences(args) + platformsRefs, err := arguments.ParseReferences(ctx, srv, args) if err != nil { feedback.Fatal(tr("Invalid argument passed: %v", err), feedback.ErrBadArgument) } @@ -63,13 +63,14 @@ func runUninstallCommand(args []string, preUninstallFlags arguments.PrePostScrip } } for _, platformRef := range platformsRefs { - _, err := core.PlatformUninstall(context.Background(), &rpc.PlatformUninstallRequest{ + req := &rpc.PlatformUninstallRequest{ Instance: inst, PlatformPackage: platformRef.PackageName, Architecture: platformRef.Architecture, SkipPreUninstall: preUninstallFlags.DetectSkipPreUninstallValue(), - }, feedback.NewTaskProgressCB()) - if err != nil { + } + stream := commands.PlatformUninstallStreamResponseToCallbackFunction(ctx, feedback.NewTaskProgressCB()) + if err := srv.PlatformUninstall(req, stream); err != nil { feedback.Fatal(tr("Error during uninstall: %v", err), feedback.ErrGeneric) } } diff --git a/internal/cli/core/update_index.go b/internal/cli/core/update_index.go index 37bd8898a1d..c7f0129291b 100644 --- a/internal/cli/core/update_index.go +++ b/internal/cli/core/update_index.go @@ -28,33 +28,36 @@ import ( "github.com/spf13/cobra" ) -func initUpdateIndexCommand() *cobra.Command { +func initUpdateIndexCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { updateIndexCommand := &cobra.Command{ Use: "update-index", Short: tr("Updates the index of cores."), Long: tr("Updates the index of cores to the latest version."), Example: " " + os.Args[0] + " core update-index", Args: cobra.NoArgs, - Run: runUpdateIndexCommand, + Run: func(cmd *cobra.Command, args []string) { + runUpdateIndexCommand(cmd.Context(), srv) + }, } return updateIndexCommand } -func runUpdateIndexCommand(cmd *cobra.Command, args []string) { - inst := instance.CreateAndInit() +func runUpdateIndexCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer) { logrus.Info("Executing `arduino-cli core update-index`") - resp := UpdateIndex(inst) + inst := instance.CreateAndInit(ctx, srv) + resp := UpdateIndex(ctx, srv, inst) feedback.PrintResult(&updateIndexResult{result.NewUpdateIndexResponse_ResultResult(resp)}) } // UpdateIndex updates the index of platforms. -func UpdateIndex(inst *rpc.Instance) *rpc.UpdateIndexResponse_Result { - res, err := commands.UpdateIndex(context.Background(), &rpc.UpdateIndexRequest{Instance: inst}, feedback.ProgressBar()) +func UpdateIndex(ctx context.Context, srv rpc.ArduinoCoreServiceServer, inst *rpc.Instance) *rpc.UpdateIndexResponse_Result { + stream, res := commands.UpdateIndexStreamResponseToCallbackFunction(ctx, feedback.ProgressBar()) + err := srv.UpdateIndex(&rpc.UpdateIndexRequest{Instance: inst}, stream) if err != nil { feedback.FatalError(err, feedback.ErrGeneric) } - return res + return res() } type updateIndexResult struct { diff --git a/internal/cli/core/upgrade.go b/internal/cli/core/upgrade.go index e98dd72b244..03185784536 100644 --- a/internal/cli/core/upgrade.go +++ b/internal/cli/core/upgrade.go @@ -21,8 +21,8 @@ import ( "fmt" "os" + "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/commands/cmderrors" - "github.com/arduino/arduino-cli/commands/core" "github.com/arduino/arduino-cli/internal/cli/arguments" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/cli/instance" @@ -31,7 +31,7 @@ import ( "github.com/spf13/cobra" ) -func initUpgradeCommand() *cobra.Command { +func initUpgradeCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { var postInstallFlags arguments.PrePostScriptsFlags upgradeCommand := &cobra.Command{ Use: fmt.Sprintf("upgrade [%s:%s] ...", tr("PACKAGER"), tr("ARCH")), @@ -43,24 +43,24 @@ func initUpgradeCommand() *cobra.Command { " # " + tr("upgrade arduino:samd to the latest version") + "\n" + " " + os.Args[0] + " core upgrade arduino:samd", Run: func(cmd *cobra.Command, args []string) { - runUpgradeCommand(args, postInstallFlags.DetectSkipPostInstallValue(), postInstallFlags.DetectSkipPreUninstallValue()) + runUpgradeCommand(cmd.Context(), srv, args, postInstallFlags.DetectSkipPostInstallValue(), postInstallFlags.DetectSkipPreUninstallValue()) }, } postInstallFlags.AddToCommand(upgradeCommand) return upgradeCommand } -func runUpgradeCommand(args []string, skipPostInstall bool, skipPreUninstall bool) { - inst := instance.CreateAndInit() +func runUpgradeCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, args []string, skipPostInstall bool, skipPreUninstall bool) { logrus.Info("Executing `arduino-cli core upgrade`") - Upgrade(inst, args, skipPostInstall, skipPreUninstall) + inst := instance.CreateAndInit(ctx, srv) + Upgrade(ctx, srv, inst, args, skipPostInstall, skipPreUninstall) } // Upgrade upgrades one or all installed platforms to the latest version. -func Upgrade(inst *rpc.Instance, args []string, skipPostInstall bool, skipPreUninstall bool) { +func Upgrade(ctx context.Context, srv rpc.ArduinoCoreServiceServer, inst *rpc.Instance, args []string, skipPostInstall bool, skipPreUninstall bool) { // if no platform was passed, upgrade allthethings if len(args) == 0 { - platforms, err := core.PlatformSearch(&rpc.PlatformSearchRequest{ + platforms, err := srv.PlatformSearch(ctx, &rpc.PlatformSearchRequest{ Instance: inst, }) if err != nil { @@ -92,17 +92,17 @@ func Upgrade(inst *rpc.Instance, args []string, skipPostInstall bool, skipPreUni } } - warningMissingIndex := func(response *rpc.PlatformUpgradeResponse) { - if response == nil || response.GetPlatform() == nil { + warningMissingIndex := func(platform *rpc.Platform) { + if platform == nil { return } - if !response.GetPlatform().GetMetadata().GetIndexed() { - feedback.Warning(tr("missing package index for %s, future updates cannot be guaranteed", response.GetPlatform().GetMetadata().GetId())) + if !platform.GetMetadata().GetIndexed() { + feedback.Warning(tr("missing package index for %s, future updates cannot be guaranteed", platform.GetMetadata().GetId())) } } // proceed upgrading, if anything is upgradable - platformsRefs, err := arguments.ParseReferences(args) + platformsRefs, err := arguments.ParseReferences(ctx, srv, args) if err != nil { feedback.Fatal(tr("Invalid argument passed: %v", err), feedback.ErrBadArgument) } @@ -122,8 +122,9 @@ func Upgrade(inst *rpc.Instance, args []string, skipPostInstall bool, skipPreUni SkipPostInstall: skipPostInstall, SkipPreUninstall: skipPreUninstall, } - response, err := core.PlatformUpgrade(context.Background(), r, feedback.ProgressBar(), feedback.TaskProgress()) - warningMissingIndex(response) + stream, respCB := commands.PlatformUpgradeStreamResponseToCallbackFunction(ctx, feedback.ProgressBar(), feedback.TaskProgress()) + err := srv.PlatformUpgrade(r, stream) + warningMissingIndex(respCB()) if err != nil { var alreadyAtLatestVersionErr *cmderrors.PlatformAlreadyAtTheLatestVersionError if errors.As(err, &alreadyAtLatestVersionErr) { diff --git a/internal/cli/daemon/daemon.go b/internal/cli/daemon/daemon.go index fcda04024f4..76c7e9a24ef 100644 --- a/internal/cli/daemon/daemon.go +++ b/internal/cli/daemon/daemon.go @@ -21,15 +21,13 @@ import ( "fmt" "net" "os" + "path/filepath" "strings" "syscall" - "github.com/arduino/arduino-cli/commands/daemon" - "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/i18n" - srv_commands "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" - "github.com/arduino/arduino-cli/version" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/go-paths-helper" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -45,31 +43,55 @@ var ( ) // NewCommand created a new `daemon` command -func NewCommand() *cobra.Command { +func NewCommand(srv rpc.ArduinoCoreServiceServer, settings *rpc.Configuration) *cobra.Command { + var daemonPort string daemonCommand := &cobra.Command{ Use: "daemon", - Short: tr("Run as a daemon on port: %s", configuration.Settings.GetString("daemon.port")), - Long: tr("Running as a daemon the initialization of cores and libraries is done only once."), + Short: tr("Run the Arduino CLI as a gRPC daemon."), Example: " " + os.Args[0] + " daemon", Args: cobra.NoArgs, - Run: runDaemonCommand, + PreRun: func(cmd *cobra.Command, args []string) { + // Bundled libraries support is enabled by default when running as a daemon + if settings.GetDirectories().GetBuiltin().GetLibraries() == "" { + defaultBuiltinLibDir := filepath.Join(settings.GetDirectories().GetData(), "libraries") + _, err := srv.SettingsSetValue(cmd.Context(), &rpc.SettingsSetValueRequest{ + Key: "directories.builtin.libraries", + ValueFormat: "cli", + EncodedValue: defaultBuiltinLibDir, + }) + if err != nil { + // Should never happen... + panic("Failed to set default value for directories.builtin.libraries: " + err.Error()) + } + } + }, + Run: func(cmd *cobra.Command, args []string) { + runDaemonCommand(srv, daemonPort) + }, } - daemonCommand.PersistentFlags().String("port", "", tr("The TCP port the daemon will listen to")) - configuration.Settings.BindPFlag("daemon.port", daemonCommand.PersistentFlags().Lookup("port")) - daemonCommand.Flags().BoolVar(&daemonize, "daemonize", false, tr("Do not terminate daemon process if the parent process dies")) - daemonCommand.Flags().BoolVar(&debug, "debug", false, tr("Enable debug logging of gRPC calls")) - daemonCommand.Flags().StringVar(&debugFile, "debug-file", "", tr("Append debug logging to the specified file")) - daemonCommand.Flags().StringSliceVar(&debugFilters, "debug-filter", []string{}, tr("Display only the provided gRPC calls")) + defaultDaemonPort := settings.GetDaemon().GetPort() + + daemonCommand.Flags().StringVar(&daemonPort, + "port", defaultDaemonPort, + tr("The TCP port the daemon will listen to")) + daemonCommand.Flags().BoolVar(&daemonize, + "daemonize", false, + tr("Do not terminate daemon process if the parent process dies")) + daemonCommand.Flags().BoolVar(&debug, + "debug", false, + tr("Enable debug logging of gRPC calls")) + daemonCommand.Flags().StringVar(&debugFile, + "debug-file", "", + tr("Append debug logging to the specified file")) + daemonCommand.Flags().StringSliceVar(&debugFilters, + "debug-filter", []string{}, + tr("Display only the provided gRPC calls")) return daemonCommand } -func runDaemonCommand(cmd *cobra.Command, args []string) { +func runDaemonCommand(srv rpc.ArduinoCoreServiceServer, daemonPort string) { logrus.Info("Executing `arduino-cli daemon`") - // Bundled libraries support is enabled by default when running as a daemon - configuration.Settings.SetDefault("directories.builtin.Libraries", configuration.GetDefaultBuiltinLibrariesDir()) - - port := configuration.Settings.GetString("daemon.port") gRPCOptions := []grpc.ServerOption{} if debugFile != "" { if !debug { @@ -98,44 +120,40 @@ func runDaemonCommand(cmd *cobra.Command, args []string) { ) } s := grpc.NewServer(gRPCOptions...) - // Set specific user-agent for the daemon - configuration.Settings.Set("network.user_agent_ext", "daemon") // register the commands service - srv_commands.RegisterArduinoCoreServiceServer(s, &daemon.ArduinoCoreServerImpl{ - VersionString: version.VersionInfo.VersionString, - }) + rpc.RegisterArduinoCoreServiceServer(s, srv) if !daemonize { // When parent process ends terminate also the daemon go feedback.ExitWhenParentProcessEnds() } - ip := "127.0.0.1" - lis, err := net.Listen("tcp", fmt.Sprintf("%s:%s", ip, port)) + daemonIP := "127.0.0.1" + lis, err := net.Listen("tcp", fmt.Sprintf("%s:%s", daemonIP, daemonPort)) if err != nil { // Invalid port, such as "Foo" var dnsError *net.DNSError if errors.As(err, &dnsError) { - feedback.Fatal(tr("Failed to listen on TCP port: %[1]s. %[2]s is unknown name.", port, dnsError.Name), feedback.ErrBadTCPPortArgument) + feedback.Fatal(tr("Failed to listen on TCP port: %[1]s. %[2]s is unknown name.", daemonPort, dnsError.Name), feedback.ErrBadTCPPortArgument) } // Invalid port number, such as -1 var addrError *net.AddrError if errors.As(err, &addrError) { - feedback.Fatal(tr("Failed to listen on TCP port: %[1]s. %[2]s is an invalid port.", port, addrError.Addr), feedback.ErrBadTCPPortArgument) + feedback.Fatal(tr("Failed to listen on TCP port: %[1]s. %[2]s is an invalid port.", daemonPort, addrError.Addr), feedback.ErrBadTCPPortArgument) } // Port is already in use var syscallErr *os.SyscallError if errors.As(err, &syscallErr) && errors.Is(syscallErr.Err, syscall.EADDRINUSE) { - feedback.Fatal(tr("Failed to listen on TCP port: %s. Address already in use.", port), feedback.ErrFailedToListenToTCPPort) + feedback.Fatal(tr("Failed to listen on TCP port: %s. Address already in use.", daemonPort), feedback.ErrFailedToListenToTCPPort) } - feedback.Fatal(tr("Failed to listen on TCP port: %[1]s. Unexpected error: %[2]v", port, err), feedback.ErrFailedToListenToTCPPort) + feedback.Fatal(tr("Failed to listen on TCP port: %[1]s. Unexpected error: %[2]v", daemonPort, err), feedback.ErrFailedToListenToTCPPort) } // We need to retrieve the port used only if the user did not specify it // and let the OS choose it randomly, in all other cases we already know // which port is used. - if port == "0" { + if daemonPort == "0" { address := lis.Addr() split := strings.Split(address.String(), ":") @@ -143,12 +161,12 @@ func runDaemonCommand(cmd *cobra.Command, args []string) { feedback.Fatal(tr("Invalid TCP address: port is missing"), feedback.ErrBadTCPPortArgument) } - port = split[1] + daemonPort = split[1] } feedback.PrintResult(daemonResult{ - IP: ip, - Port: port, + IP: daemonIP, + Port: daemonPort, }) if err := s.Serve(lis); err != nil { diff --git a/internal/cli/debug/debug.go b/internal/cli/debug/debug.go index 8ead18d8c1b..3da3c125371 100644 --- a/internal/cli/debug/debug.go +++ b/internal/cli/debug/debug.go @@ -22,9 +22,8 @@ import ( "os" "os/signal" + "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/commands/cmderrors" - "github.com/arduino/arduino-cli/commands/debug" - "github.com/arduino/arduino-cli/commands/sketch" "github.com/arduino/arduino-cli/internal/cli/arguments" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/cli/feedback/table" @@ -39,7 +38,7 @@ import ( var tr = i18n.Tr // NewCommand created a new `upload` command -func NewCommand() *cobra.Command { +func NewCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { var ( fqbnArg arguments.Fqbn portArgs arguments.Port @@ -57,15 +56,15 @@ func NewCommand() *cobra.Command { Example: " " + os.Args[0] + " debug -b arduino:samd:mkr1000 -P atmel_ice /home/user/Arduino/MySketch", Args: cobra.MaximumNArgs(1), Run: func(cmd *cobra.Command, args []string) { - runDebugCommand(args, &portArgs, &fqbnArg, interpreter, importDir, &programmer, printInfo, &profileArg) + runDebugCommand(cmd.Context(), srv, args, &portArgs, &fqbnArg, interpreter, importDir, &programmer, printInfo, &profileArg) }, } - debugCommand.AddCommand(newDebugCheckCommand()) - fqbnArg.AddToCommand(debugCommand) - portArgs.AddToCommand(debugCommand) - programmer.AddToCommand(debugCommand) - profileArg.AddToCommand(debugCommand) + debugCommand.AddCommand(newDebugCheckCommand(srv)) + fqbnArg.AddToCommand(debugCommand, srv) + portArgs.AddToCommand(debugCommand, srv) + programmer.AddToCommand(debugCommand, srv) + profileArg.AddToCommand(debugCommand, srv) debugCommand.Flags().StringVar(&interpreter, "interpreter", "console", tr("Debug interpreter e.g.: %s", "console, mi, mi1, mi2, mi3")) debugCommand.Flags().StringVarP(&importDir, "input-dir", "", "", tr("Directory containing binaries for debug.")) debugCommand.Flags().BoolVarP(&printInfo, "info", "I", false, tr("Show metadata about the debug session instead of starting the debugger.")) @@ -73,7 +72,7 @@ func NewCommand() *cobra.Command { return debugCommand } -func runDebugCommand(args []string, portArgs *arguments.Port, fqbnArg *arguments.Fqbn, +func runDebugCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, args []string, portArgs *arguments.Port, fqbnArg *arguments.Fqbn, interpreter string, importDir string, programmer *arguments.Programmer, printInfo bool, profileArg *arguments.Profile) { logrus.Info("Executing `arduino-cli debug`") @@ -83,30 +82,31 @@ func runDebugCommand(args []string, portArgs *arguments.Port, fqbnArg *arguments } sketchPath := arguments.InitSketchPath(path) - sk, err := sketch.LoadSketch(context.Background(), &rpc.LoadSketchRequest{SketchPath: sketchPath.String()}) + resp, err := srv.LoadSketch(ctx, &rpc.LoadSketchRequest{SketchPath: sketchPath.String()}) if err != nil { feedback.FatalError(err, feedback.ErrGeneric) } + sk := resp.GetSketch() feedback.WarnAboutDeprecatedFiles(sk) var inst *rpc.Instance var profile *rpc.SketchProfile if profileArg.Get() == "" { - inst, profile = instance.CreateAndInitWithProfile(sk.GetDefaultProfile().GetName(), sketchPath) + inst, profile = instance.CreateAndInitWithProfile(ctx, srv, sk.GetDefaultProfile().GetName(), sketchPath) } else { - inst, profile = instance.CreateAndInitWithProfile(profileArg.Get(), sketchPath) + inst, profile = instance.CreateAndInitWithProfile(ctx, srv, profileArg.Get(), sketchPath) } if fqbnArg.String() == "" { fqbnArg.Set(profile.GetFqbn()) } - fqbn, port := arguments.CalculateFQBNAndPort(portArgs, fqbnArg, inst, sk.GetDefaultFqbn(), sk.GetDefaultPort(), sk.GetDefaultProtocol()) + fqbn, port := arguments.CalculateFQBNAndPort(ctx, portArgs, fqbnArg, inst, srv, sk.GetDefaultFqbn(), sk.GetDefaultPort(), sk.GetDefaultProtocol()) prog := profile.GetProgrammer() if prog == "" || programmer.GetProgrammer() != "" { - prog = programmer.String(inst, fqbn) + prog = programmer.String(ctx, inst, srv, fqbn) } if prog == "" { prog = sk.GetDefaultProgrammer() @@ -124,7 +124,7 @@ func runDebugCommand(args []string, portArgs *arguments.Port, fqbnArg *arguments if printInfo { - if res, err := debug.GetDebugConfig(context.Background(), debugConfigRequested); err != nil { + if res, err := commands.GetDebugConfig(ctx, debugConfigRequested); err != nil { errcode := feedback.ErrBadArgument if errors.Is(err, &cmderrors.MissingProgrammerError{}) { errcode = feedback.ErrMissingProgrammer @@ -144,7 +144,7 @@ func runDebugCommand(args []string, portArgs *arguments.Port, fqbnArg *arguments if err != nil { feedback.FatalError(err, feedback.ErrBadArgument) } - if _, err := debug.Debug(context.Background(), debugConfigRequested, in, out, ctrlc); err != nil { + if _, err := commands.Debug(ctx, debugConfigRequested, in, out, ctrlc); err != nil { errcode := feedback.ErrGeneric if errors.Is(err, &cmderrors.MissingProgrammerError{}) { errcode = feedback.ErrMissingProgrammer diff --git a/internal/cli/debug/debug_check.go b/internal/cli/debug/debug_check.go index 52e25d40577..c16bb13cc25 100644 --- a/internal/cli/debug/debug_check.go +++ b/internal/cli/debug/debug_check.go @@ -19,7 +19,7 @@ import ( "context" "os" - "github.com/arduino/arduino-cli/commands/debug" + "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/internal/cli/arguments" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/cli/feedback/result" @@ -29,7 +29,7 @@ import ( "github.com/spf13/cobra" ) -func newDebugCheckCommand() *cobra.Command { +func newDebugCheckCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { var ( fqbnArg arguments.Fqbn portArgs arguments.Port @@ -41,31 +41,31 @@ func newDebugCheckCommand() *cobra.Command { Short: tr("Check if the given board/programmer combination supports debugging."), Example: " " + os.Args[0] + " debug check -b arduino:samd:mkr1000 -P atmel_ice", Run: func(cmd *cobra.Command, args []string) { - runDebugCheckCommand(&portArgs, &fqbnArg, interpreter, &programmer) + runDebugCheckCommand(cmd.Context(), srv, &portArgs, &fqbnArg, interpreter, &programmer) }, } - fqbnArg.AddToCommand(debugCheckCommand) - portArgs.AddToCommand(debugCheckCommand) - programmer.AddToCommand(debugCheckCommand) + fqbnArg.AddToCommand(debugCheckCommand, srv) + portArgs.AddToCommand(debugCheckCommand, srv) + programmer.AddToCommand(debugCheckCommand, srv) debugCheckCommand.Flags().StringVar(&interpreter, "interpreter", "console", tr("Debug interpreter e.g.: %s", "console, mi, mi1, mi2, mi3")) return debugCheckCommand } -func runDebugCheckCommand(portArgs *arguments.Port, fqbnArg *arguments.Fqbn, interpreter string, programmerArg *arguments.Programmer) { - instance := instance.CreateAndInit() +func runDebugCheckCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, portArgs *arguments.Port, fqbnArg *arguments.Fqbn, interpreter string, programmerArg *arguments.Programmer) { + instance := instance.CreateAndInit(ctx, srv) logrus.Info("Executing `arduino-cli debug`") - port, err := portArgs.GetPort(instance, "", "") + port, err := portArgs.GetPort(ctx, instance, srv, "", "") if err != nil { feedback.FatalError(err, feedback.ErrBadArgument) } fqbn := fqbnArg.String() - resp, err := debug.IsDebugSupported(context.Background(), &rpc.IsDebugSupportedRequest{ + resp, err := commands.IsDebugSupported(ctx, &rpc.IsDebugSupportedRequest{ Instance: instance, Fqbn: fqbn, Port: port, Interpreter: interpreter, - Programmer: programmerArg.String(instance, fqbn), + Programmer: programmerArg.String(ctx, instance, srv, fqbn), }) if err != nil { feedback.FatalError(err, feedback.ErrGeneric) diff --git a/internal/cli/instance/instance.go b/internal/cli/instance/instance.go index ec1513105aa..28a19ad6e5a 100644 --- a/internal/cli/instance/instance.go +++ b/internal/cli/instance/instance.go @@ -16,6 +16,8 @@ package instance import ( + "context" + "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/i18n" @@ -29,26 +31,26 @@ var tr = i18n.Tr // If Create fails the CLI prints an error and exits since // to execute further operations a valid Instance is mandatory. // If Init returns errors they're printed only. -func CreateAndInit() *rpc.Instance { - inst, _ := CreateAndInitWithProfile("", nil) +func CreateAndInit(ctx context.Context, srv rpc.ArduinoCoreServiceServer) *rpc.Instance { + inst, _ := CreateAndInitWithProfile(ctx, srv, "", nil) return inst } // CreateAndInitWithProfile returns a new initialized instance using the given profile of the given sketch. // If Create fails the CLI prints an error and exits since to execute further operations a valid Instance is mandatory. // If Init returns errors they're printed only. -func CreateAndInitWithProfile(profileName string, sketchPath *paths.Path) (*rpc.Instance, *rpc.SketchProfile) { - instance, err := create() +func CreateAndInitWithProfile(ctx context.Context, srv rpc.ArduinoCoreServiceServer, profileName string, sketchPath *paths.Path) (*rpc.Instance, *rpc.SketchProfile) { + instance, err := create(ctx, srv) if err != nil { feedback.Fatal(tr("Error creating instance: %v", err), feedback.ErrGeneric) } - profile := InitWithProfile(instance, profileName, sketchPath) + profile := InitWithProfile(ctx, srv, instance, profileName, sketchPath) return instance, profile } // create and return a new Instance. -func create() (*rpc.Instance, error) { - res, err := commands.Create(&rpc.CreateRequest{}) +func create(ctx context.Context, srv rpc.ArduinoCoreServiceServer) (*rpc.Instance, error) { + res, err := srv.Create(ctx, &rpc.CreateRequest{}) if err != nil { return nil, err } @@ -60,14 +62,14 @@ func create() (*rpc.Instance, error) { // platform or library that we failed to load. // Package and library indexes files are automatically updated if the // CLI is run for the first time. -func Init(instance *rpc.Instance) { - InitWithProfile(instance, "", nil) +func Init(ctx context.Context, srv rpc.ArduinoCoreServiceServer, instance *rpc.Instance) { + InitWithProfile(ctx, srv, instance, "", nil) } // InitWithProfile initializes instance by loading libraries and platforms specified in the given profile of the given sketch. // In case of loading failures return a list of errors for each platform or library that we failed to load. // Required Package and library indexes files are automatically downloaded. -func InitWithProfile(instance *rpc.Instance, profileName string, sketchPath *paths.Path) *rpc.SketchProfile { +func InitWithProfile(ctx context.Context, srv rpc.ArduinoCoreServiceServer, instance *rpc.Instance, profileName string, sketchPath *paths.Path) *rpc.SketchProfile { downloadCallback := feedback.ProgressBar() taskCallback := feedback.TaskProgress() @@ -77,7 +79,7 @@ func InitWithProfile(instance *rpc.Instance, profileName string, sketchPath *pat initReq.Profile = profileName } var profile *rpc.SketchProfile - err := commands.Init(initReq, func(res *rpc.InitResponse) { + err := srv.Init(initReq, commands.InitStreamResponseToCallbackFunction(ctx, func(res *rpc.InitResponse) error { if st := res.GetError(); st != nil { feedback.Warning(tr("Error initializing instance: %v", st.GetMessage())) } @@ -94,7 +96,8 @@ func InitWithProfile(instance *rpc.Instance, profileName string, sketchPath *pat if p := res.GetProfile(); p != nil { profile = p } - }) + return nil + })) if err != nil { feedback.Warning(tr("Error initializing instance: %v", err)) } diff --git a/internal/cli/lib/args.go b/internal/cli/lib/args.go index 75639b8bc84..3d3546a0c7e 100644 --- a/internal/cli/lib/args.go +++ b/internal/cli/lib/args.go @@ -20,7 +20,6 @@ import ( "fmt" "strings" - "github.com/arduino/arduino-cli/commands/lib" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" ) @@ -74,9 +73,9 @@ func ParseLibraryReferenceArgs(args []string) ([]*LibraryReferenceArg, error) { // ParseLibraryReferenceArgAndAdjustCase parse a command line argument that reference a // library and possibly adjust the case of the name to match a library in the index -func ParseLibraryReferenceArgAndAdjustCase(instance *rpc.Instance, arg string) (*LibraryReferenceArg, error) { +func ParseLibraryReferenceArgAndAdjustCase(ctx context.Context, srv rpc.ArduinoCoreServiceServer, instance *rpc.Instance, arg string) (*LibraryReferenceArg, error) { libRef, _ := ParseLibraryReferenceArg(arg) - res, err := lib.LibrarySearch(context.Background(), &rpc.LibrarySearchRequest{ + res, err := srv.LibrarySearch(ctx, &rpc.LibrarySearchRequest{ Instance: instance, SearchArgs: libRef.Name, }) @@ -98,10 +97,10 @@ func ParseLibraryReferenceArgAndAdjustCase(instance *rpc.Instance, arg string) ( // ParseLibraryReferenceArgsAndAdjustCase is a convenient wrapper that operates on a slice of // strings and calls ParseLibraryReferenceArgAndAdjustCase for each of them. It returns at the first invalid argument. -func ParseLibraryReferenceArgsAndAdjustCase(instance *rpc.Instance, args []string) ([]*LibraryReferenceArg, error) { +func ParseLibraryReferenceArgsAndAdjustCase(ctx context.Context, srv rpc.ArduinoCoreServiceServer, instance *rpc.Instance, args []string) ([]*LibraryReferenceArg, error) { ret := []*LibraryReferenceArg{} for _, arg := range args { - if reference, err := ParseLibraryReferenceArgAndAdjustCase(instance, arg); err == nil { + if reference, err := ParseLibraryReferenceArgAndAdjustCase(ctx, srv, instance, arg); err == nil { ret = append(ret, reference) } else { return nil, err diff --git a/internal/cli/lib/check_deps.go b/internal/cli/lib/check_deps.go index 557983298fb..8e18e800515 100644 --- a/internal/cli/lib/check_deps.go +++ b/internal/cli/lib/check_deps.go @@ -21,7 +21,6 @@ import ( "os" "sort" - "github.com/arduino/arduino-cli/commands/lib" "github.com/arduino/arduino-cli/internal/cli/arguments" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/cli/feedback/result" @@ -32,7 +31,7 @@ import ( "github.com/spf13/cobra" ) -func initDepsCommand() *cobra.Command { +func initDepsCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { var noOverwrite bool depsCommand := &cobra.Command{ Use: fmt.Sprintf("deps %s[@%s]...", tr("LIBRARY"), tr("VERSION_NUMBER")), @@ -43,25 +42,26 @@ func initDepsCommand() *cobra.Command { " " + os.Args[0] + " lib deps AudioZero@1.0.0 # " + tr("for the specific version."), Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { - runDepsCommand(args, noOverwrite) + runDepsCommand(cmd.Context(), srv, args, noOverwrite) }, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return arguments.GetInstalledLibraries(), cobra.ShellCompDirectiveDefault + return arguments.GetInstalledLibraries(cmd.Context(), srv), cobra.ShellCompDirectiveDefault }, } depsCommand.Flags().BoolVar(&noOverwrite, "no-overwrite", false, tr("Do not try to update library dependencies if already installed.")) return depsCommand } -func runDepsCommand(args []string, noOverwrite bool) { - instance := instance.CreateAndInit() +func runDepsCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, args []string, noOverwrite bool) { + instance := instance.CreateAndInit(ctx, srv) + logrus.Info("Executing `arduino-cli lib deps`") - libRef, err := ParseLibraryReferenceArgAndAdjustCase(instance, args[0]) + libRef, err := ParseLibraryReferenceArgAndAdjustCase(ctx, srv, instance, args[0]) if err != nil { feedback.Fatal(tr("Arguments error: %v", err), feedback.ErrBadArgument) } - deps, err := lib.LibraryResolveDependencies(context.Background(), &rpc.LibraryResolveDependenciesRequest{ + deps, err := srv.LibraryResolveDependencies(ctx, &rpc.LibraryResolveDependenciesRequest{ Instance: instance, Name: libRef.Name, Version: libRef.Version, diff --git a/internal/cli/lib/download.go b/internal/cli/lib/download.go index 3616584a91d..562f8b07bb9 100644 --- a/internal/cli/lib/download.go +++ b/internal/cli/lib/download.go @@ -20,7 +20,7 @@ import ( "fmt" "os" - "github.com/arduino/arduino-cli/commands/lib" + "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/internal/cli/arguments" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/cli/instance" @@ -29,7 +29,7 @@ import ( "github.com/spf13/cobra" ) -func initDownloadCommand() *cobra.Command { +func initDownloadCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { downloadCommand := &cobra.Command{ Use: fmt.Sprintf("download [%s]...", tr("LIBRARY_NAME")), Short: tr("Downloads one or more libraries without installing them."), @@ -38,18 +38,21 @@ func initDownloadCommand() *cobra.Command { " " + os.Args[0] + " lib download AudioZero # " + tr("for the latest version.") + "\n" + " " + os.Args[0] + " lib download AudioZero@1.0.0 # " + tr("for a specific version."), Args: cobra.MinimumNArgs(1), - Run: runDownloadCommand, + Run: func(cmd *cobra.Command, args []string) { + runDownloadCommand(cmd.Context(), srv, args) + }, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return arguments.GetInstallableLibs(), cobra.ShellCompDirectiveDefault + return arguments.GetInstallableLibs(cmd.Context(), srv), cobra.ShellCompDirectiveDefault }, } return downloadCommand } -func runDownloadCommand(cmd *cobra.Command, args []string) { - instance := instance.CreateAndInit() +func runDownloadCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, args []string) { logrus.Info("Executing `arduino-cli lib download`") - refs, err := ParseLibraryReferenceArgsAndAdjustCase(instance, args) + instance := instance.CreateAndInit(ctx, srv) + + refs, err := ParseLibraryReferenceArgsAndAdjustCase(ctx, srv, instance, args) if err != nil { feedback.Fatal(tr("Invalid argument passed: %v", err), feedback.ErrBadArgument) } @@ -60,8 +63,8 @@ func runDownloadCommand(cmd *cobra.Command, args []string) { Name: library.Name, Version: library.Version, } - _, err := lib.LibraryDownload(context.Background(), libraryDownloadRequest, feedback.ProgressBar()) - if err != nil { + stream := commands.LibraryDownloadStreamResponseToCallbackFunction(ctx, feedback.ProgressBar()) + if err := srv.LibraryDownload(libraryDownloadRequest, stream); err != nil { feedback.Fatal(tr("Error downloading %[1]s: %[2]v", library, err), feedback.ErrNetwork) } } diff --git a/internal/cli/lib/examples.go b/internal/cli/lib/examples.go index 11fadebdbef..ff24e3616cb 100644 --- a/internal/cli/lib/examples.go +++ b/internal/cli/lib/examples.go @@ -22,7 +22,6 @@ import ( "sort" "strings" - "github.com/arduino/arduino-cli/commands/lib" "github.com/arduino/arduino-cli/internal/cli/arguments" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/cli/feedback/result" @@ -38,32 +37,34 @@ var ( fqbn arguments.Fqbn ) -func initExamplesCommand() *cobra.Command { +func initExamplesCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { examplesCommand := &cobra.Command{ Use: fmt.Sprintf("examples [%s]", tr("LIBRARY_NAME")), Short: tr("Shows the list of the examples for libraries."), Long: tr("Shows the list of the examples for libraries. A name may be given as argument to search a specific library."), Example: " " + os.Args[0] + " lib examples Wire", Args: cobra.MaximumNArgs(1), - Run: runExamplesCommand, + Run: func(cmd *cobra.Command, args []string) { + runExamplesCommand(cmd.Context(), srv, args) + }, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return arguments.GetInstalledLibraries(), cobra.ShellCompDirectiveDefault + return arguments.GetInstalledLibraries(cmd.Context(), srv), cobra.ShellCompDirectiveDefault }, } - fqbn.AddToCommand(examplesCommand) + fqbn.AddToCommand(examplesCommand, srv) return examplesCommand } -func runExamplesCommand(cmd *cobra.Command, args []string) { - instance := instance.CreateAndInit() +func runExamplesCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, args []string) { logrus.Info("Executing `arduino-cli lib examples`") + instance := instance.CreateAndInit(ctx, srv) name := "" if len(args) > 0 { name = args[0] } - res, err := lib.LibraryList(context.Background(), &rpc.LibraryListRequest{ + res, err := srv.LibraryList(ctx, &rpc.LibraryListRequest{ Instance: instance, All: true, Name: name, diff --git a/internal/cli/lib/install.go b/internal/cli/lib/install.go index 72a2a0592f9..fbde0cf8a6b 100644 --- a/internal/cli/lib/install.go +++ b/internal/cli/lib/install.go @@ -21,9 +21,8 @@ import ( "os" "strings" - "github.com/arduino/arduino-cli/commands/lib" + "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/internal/cli/arguments" - "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/cli/instance" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" @@ -34,12 +33,13 @@ import ( semver "go.bug.st/relaxed-semver" ) -func initInstallCommand() *cobra.Command { +func initInstallCommand(srv rpc.ArduinoCoreServiceServer, settings *rpc.Configuration) *cobra.Command { var noDeps bool var noOverwrite bool var gitURL bool var zipPath bool var useBuiltinLibrariesDir bool + enableUnsafeInstall := settings.GetLibrary().GetEnableUnsafeInstall() installCommand := &cobra.Command{ Use: fmt.Sprintf("install %s[@%s]...", tr("LIBRARY"), tr("VERSION_NUMBER")), Short: tr("Installs one or more specified libraries into the system."), @@ -52,10 +52,10 @@ func initInstallCommand() *cobra.Command { " " + os.Args[0] + " lib install --zip-path /path/to/WiFi101.zip /path/to/ArduinoBLE.zip\n", Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { - runInstallCommand(args, noDeps, noOverwrite, gitURL, zipPath, useBuiltinLibrariesDir) + runInstallCommand(cmd.Context(), srv, args, noDeps, noOverwrite, gitURL, zipPath, useBuiltinLibrariesDir, enableUnsafeInstall) }, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return arguments.GetInstallableLibs(), cobra.ShellCompDirectiveDefault + return arguments.GetInstallableLibs(cmd.Context(), srv), cobra.ShellCompDirectiveDefault }, } installCommand.Flags().BoolVar(&noDeps, "no-deps", false, tr("Do not install dependencies.")) @@ -66,12 +66,12 @@ func initInstallCommand() *cobra.Command { return installCommand } -func runInstallCommand(args []string, noDeps bool, noOverwrite bool, gitURL bool, zipPath bool, useBuiltinLibrariesDir bool) { - instance := instance.CreateAndInit() +func runInstallCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, args []string, noDeps bool, noOverwrite bool, gitURL bool, zipPath bool, useBuiltinLibrariesDir bool, enableUnsafeInstall bool) { + instance := instance.CreateAndInit(ctx, srv) logrus.Info("Executing `arduino-cli lib install`") if zipPath || gitURL { - if !configuration.Settings.GetBool("library.enable_unsafe_install") { + if !enableUnsafeInstall { documentationURL := "https://arduino.github.io/arduino-cli/latest/configuration/#configuration-keys" _, err := semver.Parse(version.VersionInfo.VersionString) if err == nil { @@ -89,12 +89,13 @@ func runInstallCommand(args []string, noDeps bool, noOverwrite bool, gitURL bool if zipPath { for _, path := range args { - err := lib.ZipLibraryInstall(context.Background(), &rpc.ZipLibraryInstallRequest{ + req := &rpc.ZipLibraryInstallRequest{ Instance: instance, Path: path, Overwrite: !noOverwrite, - }, feedback.TaskProgress()) - if err != nil { + } + stream := commands.ZipLibraryInstallStreamResponseToCallbackFunction(ctx, feedback.TaskProgress()) + if err := srv.ZipLibraryInstall(req, stream); err != nil { feedback.Fatal(tr("Error installing Zip Library: %v", err), feedback.ErrGeneric) } } @@ -110,19 +111,20 @@ func runInstallCommand(args []string, noDeps bool, noOverwrite bool, gitURL bool } url = wd.String() } - err := lib.GitLibraryInstall(context.Background(), &rpc.GitLibraryInstallRequest{ + req := &rpc.GitLibraryInstallRequest{ Instance: instance, Url: url, Overwrite: !noOverwrite, - }, feedback.TaskProgress()) - if err != nil { + } + stream := commands.GitLibraryInstallStreamResponseToCallbackFunction(ctx, feedback.TaskProgress()) + if err := srv.GitLibraryInstall(req, stream); err != nil { feedback.Fatal(tr("Error installing Git Library: %v", err), feedback.ErrGeneric) } } return } - libRefs, err := ParseLibraryReferenceArgsAndAdjustCase(instance, args) + libRefs, err := ParseLibraryReferenceArgsAndAdjustCase(ctx, srv, instance, args) if err != nil { feedback.Fatal(tr("Arguments error: %v", err), feedback.ErrBadArgument) } @@ -140,8 +142,8 @@ func runInstallCommand(args []string, noDeps bool, noOverwrite bool, gitURL bool NoOverwrite: noOverwrite, InstallLocation: installLocation, } - err := lib.LibraryInstall(context.Background(), libraryInstallRequest, feedback.ProgressBar(), feedback.TaskProgress()) - if err != nil { + stream := commands.LibraryInstallStreamResponseToCallbackFunction(ctx, feedback.ProgressBar(), feedback.TaskProgress()) + if err := srv.LibraryInstall(libraryInstallRequest, stream); err != nil { feedback.Fatal(tr("Error installing %s: %v", libRef.Name, err), feedback.ErrGeneric) } } diff --git a/internal/cli/lib/lib.go b/internal/cli/lib/lib.go index edba456bde9..5aa89cee8dd 100644 --- a/internal/cli/lib/lib.go +++ b/internal/cli/lib/lib.go @@ -19,13 +19,14 @@ import ( "os" "github.com/arduino/arduino-cli/internal/i18n" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/spf13/cobra" ) var tr = i18n.Tr // NewCommand created a new `lib` command -func NewCommand() *cobra.Command { +func NewCommand(srv rpc.ArduinoCoreServiceServer, defaultSettings *rpc.Configuration) *cobra.Command { libCommand := &cobra.Command{ Use: "lib", Short: tr("Arduino commands about libraries."), @@ -35,14 +36,14 @@ func NewCommand() *cobra.Command { " " + os.Args[0] + " lib update-index", } - libCommand.AddCommand(initDownloadCommand()) - libCommand.AddCommand(initInstallCommand()) - libCommand.AddCommand(initListCommand()) - libCommand.AddCommand(initExamplesCommand()) - libCommand.AddCommand(initSearchCommand()) - libCommand.AddCommand(initUninstallCommand()) - libCommand.AddCommand(initUpgradeCommand()) - libCommand.AddCommand(initUpdateIndexCommand()) - libCommand.AddCommand(initDepsCommand()) + libCommand.AddCommand(initDownloadCommand(srv)) + libCommand.AddCommand(initInstallCommand(srv, defaultSettings)) + libCommand.AddCommand(initListCommand(srv)) + libCommand.AddCommand(initExamplesCommand(srv)) + libCommand.AddCommand(initSearchCommand(srv)) + libCommand.AddCommand(initUninstallCommand(srv)) + libCommand.AddCommand(initUpgradeCommand(srv)) + libCommand.AddCommand(initUpdateIndexCommand(srv)) + libCommand.AddCommand(initDepsCommand(srv)) return libCommand } diff --git a/internal/cli/lib/list.go b/internal/cli/lib/list.go index dca14c6f407..f764df4985c 100644 --- a/internal/cli/lib/list.go +++ b/internal/cli/lib/list.go @@ -22,7 +22,6 @@ import ( "sort" "strings" - "github.com/arduino/arduino-cli/commands/lib" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/cli/feedback/result" "github.com/arduino/arduino-cli/internal/cli/feedback/table" @@ -32,7 +31,7 @@ import ( "github.com/spf13/cobra" ) -func initListCommand() *cobra.Command { +func initListCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { var all bool var updatable bool listCommand := &cobra.Command{ @@ -46,20 +45,21 @@ not listed, they can be listed by adding the --all flag.`), Example: " " + os.Args[0] + " lib list", Args: cobra.MaximumNArgs(1), Run: func(cmd *cobra.Command, args []string) { - instance := instance.CreateAndInit() + ctx := cmd.Context() + instance := instance.CreateAndInit(ctx, srv) logrus.Info("Executing `arduino-cli lib list`") - List(instance, args, all, updatable) + List(ctx, srv, instance, args, all, updatable) }, } listCommand.Flags().BoolVar(&all, "all", false, tr("Include built-in libraries (from platforms and IDE) in listing.")) - fqbn.AddToCommand(listCommand) + fqbn.AddToCommand(listCommand, srv) listCommand.Flags().BoolVar(&updatable, "updatable", false, tr("List updatable libraries.")) return listCommand } // List gets and prints a list of installed libraries. -func List(instance *rpc.Instance, args []string, all bool, updatable bool) { - installedLibs := GetList(instance, args, all, updatable) +func List(ctx context.Context, srv rpc.ArduinoCoreServiceServer, instance *rpc.Instance, args []string, all bool, updatable bool) { + installedLibs := GetList(ctx, srv, instance, args, all, updatable) installedLibsResult := make([]*result.InstalledLibrary, len(installedLibs)) for i, v := range installedLibs { @@ -73,18 +73,13 @@ func List(instance *rpc.Instance, args []string, all bool, updatable bool) { } // GetList returns a list of installed libraries. -func GetList( - instance *rpc.Instance, - args []string, - all bool, - updatable bool, -) []*rpc.InstalledLibrary { +func GetList(ctx context.Context, srv rpc.ArduinoCoreServiceServer, instance *rpc.Instance, args []string, all bool, updatable bool) []*rpc.InstalledLibrary { name := "" if len(args) > 0 { name = args[0] } - res, err := lib.LibraryList(context.Background(), &rpc.LibraryListRequest{ + res, err := srv.LibraryList(ctx, &rpc.LibraryListRequest{ Instance: instance, All: all, Updatable: updatable, diff --git a/internal/cli/lib/search.go b/internal/cli/lib/search.go index 913344f582d..94780be95c4 100644 --- a/internal/cli/lib/search.go +++ b/internal/cli/lib/search.go @@ -23,7 +23,6 @@ import ( "time" "github.com/arduino/arduino-cli/commands" - "github.com/arduino/arduino-cli/commands/lib" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/cli/feedback/result" "github.com/arduino/arduino-cli/internal/cli/instance" @@ -32,7 +31,7 @@ import ( "github.com/spf13/cobra" ) -func initSearchCommand() *cobra.Command { +func initSearchCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { var namesOnly bool var omitReleasesDetails bool searchCommand := &cobra.Command{ @@ -91,7 +90,7 @@ In addition to the fields listed above, QV terms can use these qualifiers: " " + os.Args[0] + " lib search dependencies=IRremote # " + tr("libraries that depend only on \"IRremote\"") + "\n", Args: cobra.ArbitraryArgs, Run: func(cmd *cobra.Command, args []string) { - runSearchCommand(args, namesOnly, omitReleasesDetails) + runSearchCommand(cmd.Context(), srv, args, namesOnly, omitReleasesDetails) }, } searchCommand.Flags().BoolVar(&namesOnly, "names", false, tr("Show library names only.")) @@ -102,24 +101,22 @@ In addition to the fields listed above, QV terms can use these qualifiers: // indexUpdateInterval specifies the time threshold over which indexes are updated const indexUpdateInterval = 60 * time.Minute -func runSearchCommand(args []string, namesOnly bool, omitReleasesDetails bool) { - inst := instance.CreateAndInit() +func runSearchCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, args []string, namesOnly bool, omitReleasesDetails bool) { + inst := instance.CreateAndInit(ctx, srv) logrus.Info("Executing `arduino-cli lib search`") - res, err := commands.UpdateLibrariesIndex( - context.Background(), - &rpc.UpdateLibrariesIndexRequest{Instance: inst, UpdateIfOlderThanSecs: int64(indexUpdateInterval.Seconds())}, - feedback.ProgressBar(), - ) - if err != nil { + stream, res := commands.UpdateLibrariesIndexStreamResponseToCallbackFunction(ctx, feedback.ProgressBar()) + req := &rpc.UpdateLibrariesIndexRequest{Instance: inst, UpdateIfOlderThanSecs: int64(indexUpdateInterval.Seconds())} + if err := srv.UpdateLibrariesIndex(req, stream); err != nil { feedback.Fatal(tr("Error updating library index: %v", err), feedback.ErrGeneric) } - if res.GetLibrariesIndex().GetStatus() == rpc.IndexUpdateReport_STATUS_UPDATED { - instance.Init(inst) + if res().GetLibrariesIndex().GetStatus() == rpc.IndexUpdateReport_STATUS_UPDATED { + instance.Init(ctx, srv, inst) } - searchResp, err := lib.LibrarySearch(context.Background(), &rpc.LibrarySearchRequest{ + // Perform library search + searchResp, err := srv.LibrarySearch(ctx, &rpc.LibrarySearchRequest{ Instance: inst, SearchArgs: strings.Join(args, " "), OmitReleasesDetails: omitReleasesDetails, diff --git a/internal/cli/lib/uninstall.go b/internal/cli/lib/uninstall.go index 897b0203320..07ca7248db2 100644 --- a/internal/cli/lib/uninstall.go +++ b/internal/cli/lib/uninstall.go @@ -20,7 +20,7 @@ import ( "fmt" "os" - "github.com/arduino/arduino-cli/commands/lib" + "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/internal/cli/arguments" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/cli/instance" @@ -29,37 +29,40 @@ import ( "github.com/spf13/cobra" ) -func initUninstallCommand() *cobra.Command { +func initUninstallCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { uninstallCommand := &cobra.Command{ Use: fmt.Sprintf("uninstall %s...", tr("LIBRARY_NAME")), Short: tr("Uninstalls one or more libraries."), Long: tr("Uninstalls one or more libraries."), Example: " " + os.Args[0] + " lib uninstall AudioZero", Args: cobra.MinimumNArgs(1), - Run: runUninstallCommand, + Run: func(cmd *cobra.Command, args []string) { + runUninstallCommand(cmd.Context(), srv, args) + }, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return arguments.GetUninstallableLibraries(), cobra.ShellCompDirectiveDefault + return arguments.GetUninstallableLibraries(cmd.Context(), srv), cobra.ShellCompDirectiveDefault }, } return uninstallCommand } -func runUninstallCommand(cmd *cobra.Command, args []string) { - instance := instance.CreateAndInit() +func runUninstallCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, args []string) { logrus.Info("Executing `arduino-cli lib uninstall`") + instance := instance.CreateAndInit(ctx, srv) - refs, err := ParseLibraryReferenceArgsAndAdjustCase(instance, args) + refs, err := ParseLibraryReferenceArgsAndAdjustCase(ctx, srv, instance, args) if err != nil { feedback.Fatal(tr("Invalid argument passed: %v", err), feedback.ErrBadArgument) } for _, library := range refs { - err := lib.LibraryUninstall(context.Background(), &rpc.LibraryUninstallRequest{ + req := &rpc.LibraryUninstallRequest{ Instance: instance, Name: library.Name, Version: library.Version, - }, feedback.TaskProgress()) - if err != nil { + } + stream := commands.LibraryUninstallStreamResponseToCallbackFunction(ctx, feedback.TaskProgress()) + if err := srv.LibraryUninstall(req, stream); err != nil { feedback.Fatal(tr("Error uninstalling %[1]s: %[2]v", library, err), feedback.ErrGeneric) } } diff --git a/internal/cli/lib/update_index.go b/internal/cli/lib/update_index.go index 462ba003240..a9c93240b10 100644 --- a/internal/cli/lib/update_index.go +++ b/internal/cli/lib/update_index.go @@ -28,34 +28,36 @@ import ( "github.com/spf13/cobra" ) -func initUpdateIndexCommand() *cobra.Command { +func initUpdateIndexCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { updateIndexCommand := &cobra.Command{ Use: "update-index", Short: tr("Updates the libraries index."), Long: tr("Updates the libraries index to the latest version."), Example: " " + os.Args[0] + " lib update-index", Args: cobra.NoArgs, - Run: runUpdateIndexCommand, + Run: func(cmd *cobra.Command, args []string) { + runUpdateIndexCommand(cmd.Context(), srv) + }, } return updateIndexCommand } -func runUpdateIndexCommand(cmd *cobra.Command, args []string) { - inst := instance.CreateAndInit() +func runUpdateIndexCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer) { + inst := instance.CreateAndInit(ctx, srv) + logrus.Info("Executing `arduino-cli lib update-index`") - resp := UpdateIndex(inst) + resp := UpdateIndex(ctx, srv, inst) feedback.PrintResult(&libUpdateIndexResult{result.NewUpdateLibrariesIndexResponse_ResultResult(resp)}) } // UpdateIndex updates the index of libraries. -func UpdateIndex(inst *rpc.Instance) *rpc.UpdateLibrariesIndexResponse_Result { - resp, err := commands.UpdateLibrariesIndex(context.Background(), &rpc.UpdateLibrariesIndexRequest{ - Instance: inst, - }, feedback.ProgressBar()) - if err != nil { +func UpdateIndex(ctx context.Context, srv rpc.ArduinoCoreServiceServer, inst *rpc.Instance) *rpc.UpdateLibrariesIndexResponse_Result { + req := &rpc.UpdateLibrariesIndexRequest{Instance: inst} + stream, resp := commands.UpdateLibrariesIndexStreamResponseToCallbackFunction(ctx, feedback.ProgressBar()) + if err := srv.UpdateLibrariesIndex(req, stream); err != nil { feedback.Fatal(tr("Error updating library index: %v", err), feedback.ErrGeneric) } - return resp + return resp() } type libUpdateIndexResult struct { diff --git a/internal/cli/lib/upgrade.go b/internal/cli/lib/upgrade.go index 5375e5d6795..bb0da0caaea 100644 --- a/internal/cli/lib/upgrade.go +++ b/internal/cli/lib/upgrade.go @@ -20,7 +20,7 @@ import ( "fmt" "os" - "github.com/arduino/arduino-cli/commands/lib" + "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/cli/instance" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" @@ -28,7 +28,7 @@ import ( "github.com/spf13/cobra" ) -func initUpgradeCommand() *cobra.Command { +func initUpgradeCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { upgradeCommand := &cobra.Command{ Use: "upgrade", Short: tr("Upgrades installed libraries."), @@ -37,31 +37,34 @@ func initUpgradeCommand() *cobra.Command { " " + os.Args[0] + " lib upgrade Audio\n" + " " + os.Args[0] + " lib upgrade Audio ArduinoJson", Args: cobra.ArbitraryArgs, - Run: runUpgradeCommand, + Run: func(cmd *cobra.Command, args []string) { + runUpgradeCommand(cmd.Context(), srv, args) + }, } return upgradeCommand } -func runUpgradeCommand(cmd *cobra.Command, args []string) { - instance := instance.CreateAndInit() +func runUpgradeCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, args []string) { logrus.Info("Executing `arduino-cli lib upgrade`") - Upgrade(instance, args) + instance := instance.CreateAndInit(ctx, srv) + Upgrade(ctx, srv, instance, args) } // Upgrade upgrades the specified libraries -func Upgrade(instance *rpc.Instance, libraries []string) { +func Upgrade(ctx context.Context, srv rpc.ArduinoCoreServiceServer, instance *rpc.Instance, libraries []string) { var upgradeErr error if len(libraries) == 0 { req := &rpc.LibraryUpgradeAllRequest{Instance: instance} - upgradeErr = lib.LibraryUpgradeAll(req, feedback.ProgressBar(), feedback.TaskProgress()) + stream := commands.LibraryUpgradeAllStreamResponseToCallbackFunction(ctx, feedback.ProgressBar(), feedback.TaskProgress()) + upgradeErr = srv.LibraryUpgradeAll(req, stream) } else { for _, libName := range libraries { req := &rpc.LibraryUpgradeRequest{ Instance: instance, Name: libName, } - upgradeErr = lib.LibraryUpgrade(context.Background(), req, feedback.ProgressBar(), feedback.TaskProgress()) - if upgradeErr != nil { + stream := commands.LibraryUpgradeStreamResponseToCallbackFunction(ctx, feedback.ProgressBar(), feedback.TaskProgress()) + if upgradeErr = srv.LibraryUpgrade(req, stream); upgradeErr != nil { break } } diff --git a/internal/cli/monitor/monitor.go b/internal/cli/monitor/monitor.go index aacf38bd9b3..eb237521445 100644 --- a/internal/cli/monitor/monitor.go +++ b/internal/cli/monitor/monitor.go @@ -26,8 +26,7 @@ import ( "strings" "time" - "github.com/arduino/arduino-cli/commands/monitor" - sk "github.com/arduino/arduino-cli/commands/sketch" + "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/internal/cli/arguments" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/cli/feedback/result" @@ -44,7 +43,7 @@ import ( var tr = i18n.Tr // NewCommand created a new `monitor` command -func NewCommand() *cobra.Command { +func NewCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { var ( portArgs arguments.Port fqbnArg arguments.Fqbn @@ -67,21 +66,22 @@ func NewCommand() *cobra.Command { if len(args) > 0 { sketchPath = args[0] } - runMonitorCmd(&portArgs, &fqbnArg, &profileArg, sketchPath, configs, describe, timestamp, quiet, raw) + runMonitorCmd(cmd.Context(), srv, &portArgs, &fqbnArg, &profileArg, sketchPath, configs, describe, timestamp, quiet, raw) }, } - portArgs.AddToCommand(monitorCommand) - profileArg.AddToCommand(monitorCommand) + portArgs.AddToCommand(monitorCommand, srv) + profileArg.AddToCommand(monitorCommand, srv) monitorCommand.Flags().BoolVar(&raw, "raw", false, tr("Set terminal in raw mode (unbuffered).")) monitorCommand.Flags().BoolVar(&describe, "describe", false, tr("Show all the settings of the communication port.")) monitorCommand.Flags().StringSliceVarP(&configs, "config", "c", []string{}, tr("Configure communication port settings. The format is =[,=]...")) monitorCommand.Flags().BoolVarP(&quiet, "quiet", "q", false, tr("Run in silent mode, show only monitor input and output.")) monitorCommand.Flags().BoolVar(×tamp, "timestamp", false, tr("Timestamp each incoming line.")) - fqbnArg.AddToCommand(monitorCommand) + fqbnArg.AddToCommand(monitorCommand, srv) return monitorCommand } func runMonitorCmd( + ctx context.Context, srv rpc.ArduinoCoreServiceServer, portArgs *arguments.Port, fqbnArg *arguments.Fqbn, profileArg *arguments.Profile, sketchPathArg string, configs []string, describe, timestamp, quiet, raw bool, ) { @@ -104,25 +104,26 @@ func runMonitorCmd( // If only --port is set we read the fqbn in the following order: default_fqbn -> discovery // If only --fqbn is set we read the port in the following order: default_port sketchPath := arguments.InitSketchPath(sketchPathArg) - sketch, err := sk.LoadSketch(context.Background(), &rpc.LoadSketchRequest{SketchPath: sketchPath.String()}) + resp, err := srv.LoadSketch(ctx, &rpc.LoadSketchRequest{SketchPath: sketchPath.String()}) if err != nil && !portArgs.IsPortFlagSet() { feedback.Fatal( tr("Error getting default port from `sketch.yaml`. Check if you're in the correct sketch folder or provide the --port flag: %s", err), feedback.ErrGeneric, ) } + sketch := resp.GetSketch() if sketch != nil { defaultPort, defaultProtocol = sketch.GetDefaultPort(), sketch.GetDefaultProtocol() } if fqbnArg.String() == "" { if profileArg.Get() == "" { - inst, profile = instance.CreateAndInitWithProfile(sketch.GetDefaultProfile().GetName(), sketchPath) + inst, profile = instance.CreateAndInitWithProfile(ctx, srv, sketch.GetDefaultProfile().GetName(), sketchPath) } else { - inst, profile = instance.CreateAndInitWithProfile(profileArg.Get(), sketchPath) + inst, profile = instance.CreateAndInitWithProfile(ctx, srv, profileArg.Get(), sketchPath) } } if inst == nil { - inst = instance.CreateAndInit() + inst = instance.CreateAndInit(ctx, srv) } // Priority on how to retrieve the fqbn // 1. from flag @@ -137,15 +138,15 @@ func runMonitorCmd( case sketch.GetDefaultFqbn() != "": fqbn = sketch.GetDefaultFqbn() default: - fqbn, _ = portArgs.DetectFQBN(inst) + fqbn, _ = portArgs.DetectFQBN(ctx, inst, srv) } - portAddress, portProtocol, err := portArgs.GetPortAddressAndProtocol(inst, defaultPort, defaultProtocol) + portAddress, portProtocol, err := portArgs.GetPortAddressAndProtocol(ctx, inst, srv, defaultPort, defaultProtocol) if err != nil { feedback.FatalError(err, feedback.ErrGeneric) } - enumerateResp, err := monitor.EnumerateMonitorPortSettings(context.Background(), &rpc.EnumerateMonitorPortSettingsRequest{ + enumerateResp, err := srv.EnumerateMonitorPortSettings(ctx, &rpc.EnumerateMonitorPortSettingsRequest{ Instance: inst, PortProtocol: portProtocol, Fqbn: fqbn, @@ -203,20 +204,6 @@ func runMonitorCmd( } } } - portProxy, _, err := monitor.Monitor(context.Background(), &rpc.MonitorPortOpenRequest{ - Instance: inst, - Port: &rpc.Port{Address: portAddress, Protocol: portProtocol}, - Fqbn: fqbn, - PortConfiguration: configuration, - }) - if err != nil { - feedback.FatalError(err, feedback.ErrGeneric) - } - defer portProxy.Close() - - if !quiet { - feedback.Print(tr("Connected to %s! Press CTRL-C to exit.", portAddress)) - } ttyIn, ttyOut, err := feedback.InteractiveStreams() if err != nil { @@ -227,7 +214,7 @@ func runMonitorCmd( ttyOut = newTimeStampWriter(ttyOut) } - ctx, cancel := cleanup.InterruptableContext(context.Background()) + ctx, cancel := cleanup.InterruptableContext(ctx) if raw { if feedback.IsInteractive() { if err := feedback.SetRawModeStdin(); err != nil { @@ -245,6 +232,22 @@ func runMonitorCmd( ttyIn = io.TeeReader(ttyIn, ctrlCDetector) } + monitorServer, portProxy := commands.MonitorServerToReadWriteCloser(ctx, &rpc.MonitorPortOpenRequest{ + Instance: inst, + Port: &rpc.Port{Address: portAddress, Protocol: portProtocol}, + Fqbn: fqbn, + PortConfiguration: configuration, + }) + go func() { + if !quiet { + feedback.Print(tr("Connecting to %s. Press CTRL-C to exit.", portAddress)) + } + if err := srv.Monitor(monitorServer); err != nil { + feedback.FatalError(err, feedback.ErrGeneric) + } + portProxy.Close() + cancel() + }() go func() { _, err := io.Copy(ttyOut, portProxy) if err != nil && !errors.Is(err, io.EOF) { diff --git a/internal/cli/outdated/outdated.go b/internal/cli/outdated/outdated.go index 4859f6159fc..7ca0a442e06 100644 --- a/internal/cli/outdated/outdated.go +++ b/internal/cli/outdated/outdated.go @@ -16,6 +16,7 @@ package outdated import ( + "context" "fmt" "os" "sort" @@ -36,7 +37,7 @@ import ( var tr = i18n.Tr // NewCommand creates a new `outdated` command -func NewCommand() *cobra.Command { +func NewCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { outdatedCommand := &cobra.Command{ Use: "outdated", Short: tr("Lists cores and libraries that can be upgraded"), @@ -44,21 +45,25 @@ func NewCommand() *cobra.Command { that can be upgraded. If nothing needs to be updated the output is empty.`), Example: " " + os.Args[0] + " outdated\n", Args: cobra.NoArgs, - Run: runOutdatedCommand, + Run: func(cmd *cobra.Command, args []string) { + runOutdatedCommand(cmd.Context(), srv) + }, } return outdatedCommand } -func runOutdatedCommand(cmd *cobra.Command, args []string) { - inst := instance.CreateAndInit() +func runOutdatedCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer) { logrus.Info("Executing `arduino-cli outdated`") - Outdated(inst) + inst := instance.CreateAndInit(ctx, srv) + Outdated(ctx, srv, inst) } // Outdated prints a list of outdated platforms and libraries -func Outdated(inst *rpc.Instance) { +func Outdated(ctx context.Context, srv rpc.ArduinoCoreServiceServer, inst *rpc.Instance) { feedback.PrintResult( - newOutdatedResult(core.GetList(inst, false, true), lib.GetList(inst, []string{}, false, true)), + newOutdatedResult( + core.GetList(ctx, srv, inst, false, true), + lib.GetList(ctx, srv, inst, []string{}, false, true)), ) } diff --git a/internal/cli/sketch/archive.go b/internal/cli/sketch/archive.go index fc0875be9fb..19b2a280651 100644 --- a/internal/cli/sketch/archive.go +++ b/internal/cli/sketch/archive.go @@ -20,7 +20,6 @@ import ( "fmt" "os" - "github.com/arduino/arduino-cli/commands/sketch" "github.com/arduino/arduino-cli/internal/cli/arguments" "github.com/arduino/arduino-cli/internal/cli/feedback" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" @@ -29,7 +28,7 @@ import ( ) // initArchiveCommand creates a new `archive` command -func initArchiveCommand() *cobra.Command { +func initArchiveCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { var includeBuildDir, overwrite bool archiveCommand := &cobra.Command{ @@ -43,7 +42,9 @@ func initArchiveCommand() *cobra.Command { " " + os.Args[0] + " archive /home/user/Arduino/MySketch\n" + " " + os.Args[0] + " archive /home/user/Arduino/MySketch /home/user/MySketchArchive.zip", Args: cobra.MaximumNArgs(2), - Run: func(cmd *cobra.Command, args []string) { runArchiveCommand(args, includeBuildDir, overwrite) }, + Run: func(cmd *cobra.Command, args []string) { + runArchiveCommand(cmd.Context(), srv, args, includeBuildDir, overwrite) + }, } archiveCommand.Flags().BoolVar(&includeBuildDir, "include-build-dir", false, tr("Includes %s directory in the archive.", "build")) @@ -52,9 +53,8 @@ func initArchiveCommand() *cobra.Command { return archiveCommand } -func runArchiveCommand(args []string, includeBuildDir bool, overwrite bool) { +func runArchiveCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, args []string, includeBuildDir bool, overwrite bool) { logrus.Info("Executing `arduino-cli sketch archive`") - sketchPathArg := "" if len(args) > 0 { sketchPathArg = args[0] @@ -66,13 +66,14 @@ func runArchiveCommand(args []string, includeBuildDir bool, overwrite bool) { } sketchPath := arguments.InitSketchPath(sketchPathArg) - sk, err := sketch.LoadSketch(context.Background(), &rpc.LoadSketchRequest{SketchPath: sketchPath.String()}) + resp, err := srv.LoadSketch(ctx, &rpc.LoadSketchRequest{SketchPath: sketchPath.String()}) if err != nil { feedback.FatalError(err, feedback.ErrGeneric) } + sk := resp.GetSketch() feedback.WarnAboutDeprecatedFiles(sk) - if _, err := sketch.ArchiveSketch(context.Background(), + if _, err := srv.ArchiveSketch(ctx, &rpc.ArchiveSketchRequest{ SketchPath: sketchPath.String(), ArchivePath: archivePathArg, diff --git a/internal/cli/sketch/new.go b/internal/cli/sketch/new.go index 3514a05556b..79dfcccc373 100644 --- a/internal/cli/sketch/new.go +++ b/internal/cli/sketch/new.go @@ -20,7 +20,6 @@ import ( "os" "strings" - sk "github.com/arduino/arduino-cli/commands/sketch" "github.com/arduino/arduino-cli/internal/arduino/globals" "github.com/arduino/arduino-cli/internal/cli/feedback" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" @@ -29,7 +28,7 @@ import ( "github.com/spf13/cobra" ) -func initNewCommand() *cobra.Command { +func initNewCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { var overwrite bool newCommand := &cobra.Command{ @@ -38,7 +37,9 @@ func initNewCommand() *cobra.Command { Long: tr("Create a new Sketch"), Example: " " + os.Args[0] + " sketch new MultiBlinker", Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { runNewCommand(args, overwrite) }, + Run: func(cmd *cobra.Command, args []string) { + runNewCommand(cmd.Context(), srv, args, overwrite) + }, } newCommand.Flags().BoolVarP(&overwrite, "overwrite", "f", false, tr("Overwrites an existing .ino sketch.")) @@ -46,7 +47,7 @@ func initNewCommand() *cobra.Command { return newCommand } -func runNewCommand(args []string, overwrite bool) { +func runNewCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, args []string, overwrite bool) { logrus.Info("Executing `arduino-cli sketch new`") // Trim to avoid issues if user creates a sketch adding the .ino extesion to the name inputSketchName := args[0] @@ -72,7 +73,7 @@ func runNewCommand(args []string, overwrite bool) { sketchName = sketchDirPath.Base() } - _, err = sk.NewSketch(context.Background(), &rpc.NewSketchRequest{ + _, err = srv.NewSketch(ctx, &rpc.NewSketchRequest{ SketchName: sketchName, SketchDir: sketchDir, Overwrite: overwrite, diff --git a/internal/cli/sketch/sketch.go b/internal/cli/sketch/sketch.go index 5d8da390eed..2530c72247d 100644 --- a/internal/cli/sketch/sketch.go +++ b/internal/cli/sketch/sketch.go @@ -19,13 +19,14 @@ import ( "os" "github.com/arduino/arduino-cli/internal/i18n" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/spf13/cobra" ) var tr = i18n.Tr // NewCommand created a new `sketch` command -func NewCommand() *cobra.Command { +func NewCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { sketchCommand := &cobra.Command{ Use: "sketch", Short: tr("Arduino CLI sketch commands."), @@ -33,8 +34,8 @@ func NewCommand() *cobra.Command { Example: " " + os.Args[0] + " sketch new MySketch", } - sketchCommand.AddCommand(initNewCommand()) - sketchCommand.AddCommand(initArchiveCommand()) + sketchCommand.AddCommand(initNewCommand(srv)) + sketchCommand.AddCommand(initArchiveCommand(srv)) return sketchCommand } diff --git a/internal/cli/update/update.go b/internal/cli/update/update.go index d516a5cc701..6fd4e7c91e0 100644 --- a/internal/cli/update/update.go +++ b/internal/cli/update/update.go @@ -16,6 +16,7 @@ package update import ( + "context" "os" "github.com/arduino/arduino-cli/internal/cli/core" @@ -23,6 +24,7 @@ import ( "github.com/arduino/arduino-cli/internal/cli/lib" "github.com/arduino/arduino-cli/internal/cli/outdated" "github.com/arduino/arduino-cli/internal/i18n" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -30,7 +32,7 @@ import ( var tr = i18n.Tr // NewCommand creates a new `update` command -func NewCommand() *cobra.Command { +func NewCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { var showOutdated bool updateCommand := &cobra.Command{ Use: "update", @@ -39,20 +41,21 @@ func NewCommand() *cobra.Command { Example: " " + os.Args[0] + " update", Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { - runUpdateCommand(showOutdated) + runUpdateCommand(cmd.Context(), srv, showOutdated) }, } updateCommand.Flags().BoolVar(&showOutdated, "show-outdated", false, tr("Show outdated cores and libraries after index update")) return updateCommand } -func runUpdateCommand(showOutdated bool) { - inst := instance.CreateAndInit() +func runUpdateCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, showOutdated bool) { logrus.Info("Executing `arduino-cli update`") - lib.UpdateIndex(inst) - core.UpdateIndex(inst) - instance.Init(inst) + inst := instance.CreateAndInit(ctx, srv) + + lib.UpdateIndex(ctx, srv, inst) + core.UpdateIndex(ctx, srv, inst) + instance.Init(ctx, srv, inst) if showOutdated { - outdated.Outdated(inst) + outdated.Outdated(ctx, srv, inst) } } diff --git a/internal/cli/upgrade/upgrade.go b/internal/cli/upgrade/upgrade.go index cf1f71448e7..4c311201dab 100644 --- a/internal/cli/upgrade/upgrade.go +++ b/internal/cli/upgrade/upgrade.go @@ -16,6 +16,7 @@ package upgrade import ( + "context" "os" "github.com/arduino/arduino-cli/internal/cli/arguments" @@ -23,6 +24,7 @@ import ( "github.com/arduino/arduino-cli/internal/cli/instance" "github.com/arduino/arduino-cli/internal/cli/lib" "github.com/arduino/arduino-cli/internal/i18n" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -30,7 +32,7 @@ import ( var tr = i18n.Tr // NewCommand creates a new `upgrade` command -func NewCommand() *cobra.Command { +func NewCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { var postInstallFlags arguments.PrePostScriptsFlags upgradeCommand := &cobra.Command{ Use: "upgrade", @@ -39,16 +41,16 @@ func NewCommand() *cobra.Command { Example: " " + os.Args[0] + " upgrade", Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { - runUpgradeCommand(postInstallFlags.DetectSkipPostInstallValue(), postInstallFlags.DetectSkipPreUninstallValue()) + runUpgradeCommand(cmd.Context(), srv, postInstallFlags.DetectSkipPostInstallValue(), postInstallFlags.DetectSkipPreUninstallValue()) }, } postInstallFlags.AddToCommand(upgradeCommand) return upgradeCommand } -func runUpgradeCommand(skipPostInstall bool, skipPreUninstall bool) { - inst := instance.CreateAndInit() +func runUpgradeCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, skipPostInstall bool, skipPreUninstall bool) { + inst := instance.CreateAndInit(ctx, srv) logrus.Info("Executing `arduino-cli upgrade`") - lib.Upgrade(inst, []string{}) - core.Upgrade(inst, []string{}, skipPostInstall, skipPreUninstall) + lib.Upgrade(ctx, srv, inst, []string{}) + core.Upgrade(ctx, srv, inst, []string{}, skipPostInstall, skipPreUninstall) } diff --git a/internal/cli/upload/upload.go b/internal/cli/upload/upload.go index 46ec877fc8b..752a1a5ccc4 100644 --- a/internal/cli/upload/upload.go +++ b/internal/cli/upload/upload.go @@ -22,10 +22,8 @@ import ( "os" "strings" + "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/commands/cmderrors" - "github.com/arduino/arduino-cli/commands/core" - sk "github.com/arduino/arduino-cli/commands/sketch" - "github.com/arduino/arduino-cli/commands/upload" "github.com/arduino/arduino-cli/internal/cli/arguments" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/cli/feedback/result" @@ -51,7 +49,7 @@ var ( ) // NewCommand created a new `upload` command -func NewCommand() *cobra.Command { +func NewCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { uploadFields := map[string]string{} uploadCommand := &cobra.Command{ Use: "upload", @@ -65,25 +63,25 @@ func NewCommand() *cobra.Command { arguments.CheckFlagsConflicts(cmd, "input-file", "input-dir") }, Run: func(cmd *cobra.Command, args []string) { - runUploadCommand(args, uploadFields) + runUploadCommand(cmd.Context(), srv, args, uploadFields) }, } - fqbnArg.AddToCommand(uploadCommand) - portArgs.AddToCommand(uploadCommand) - profileArg.AddToCommand(uploadCommand) + fqbnArg.AddToCommand(uploadCommand, srv) + portArgs.AddToCommand(uploadCommand, srv) + profileArg.AddToCommand(uploadCommand, srv) uploadCommand.Flags().StringVarP(&importDir, "input-dir", "", "", tr("Directory containing binaries to upload.")) uploadCommand.Flags().StringVarP(&importFile, "input-file", "i", "", tr("Binary file to upload.")) uploadCommand.Flags().BoolVarP(&verify, "verify", "t", false, tr("Verify uploaded binary after the upload.")) uploadCommand.Flags().BoolVarP(&verbose, "verbose", "v", false, tr("Optional, turns on verbose mode.")) - programmer.AddToCommand(uploadCommand) + programmer.AddToCommand(uploadCommand, srv) uploadCommand.Flags().BoolVar(&dryRun, "dry-run", false, tr("Do not perform the actual upload, just log out actions")) uploadCommand.Flags().MarkHidden("dry-run") arguments.AddKeyValuePFlag(uploadCommand, &uploadFields, "upload-field", "F", nil, tr("Set a value for a field required to upload.")) return uploadCommand } -func runUploadCommand(args []string, uploadFieldsArgs map[string]string) { +func runUploadCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer, args []string, uploadFieldsArgs map[string]string) { logrus.Info("Executing `arduino-cli upload`") path := "" @@ -91,7 +89,8 @@ func runUploadCommand(args []string, uploadFieldsArgs map[string]string) { path = args[0] } sketchPath := arguments.InitSketchPath(path) - sketch, err := sk.LoadSketch(context.Background(), &rpc.LoadSketchRequest{SketchPath: sketchPath.String()}) + resp, err := srv.LoadSketch(ctx, &rpc.LoadSketchRequest{SketchPath: sketchPath.String()}) + sketch := resp.GetSketch() if importDir == "" && importFile == "" { if err != nil { feedback.Fatal(tr("Error during Upload: %v", err), feedback.ErrGeneric) @@ -103,9 +102,9 @@ func runUploadCommand(args []string, uploadFieldsArgs map[string]string) { var profile *rpc.SketchProfile if profileArg.Get() == "" { - inst, profile = instance.CreateAndInitWithProfile(sketch.GetDefaultProfile().GetName(), sketchPath) + inst, profile = instance.CreateAndInitWithProfile(ctx, srv, sketch.GetDefaultProfile().GetName(), sketchPath) } else { - inst, profile = instance.CreateAndInitWithProfile(profileArg.Get(), sketchPath) + inst, profile = instance.CreateAndInitWithProfile(ctx, srv, profileArg.Get(), sketchPath) } if fqbnArg.String() == "" { @@ -115,9 +114,9 @@ func runUploadCommand(args []string, uploadFieldsArgs map[string]string) { defaultFQBN := sketch.GetDefaultFqbn() defaultAddress := sketch.GetDefaultPort() defaultProtocol := sketch.GetDefaultProtocol() - fqbn, port := arguments.CalculateFQBNAndPort(&portArgs, &fqbnArg, inst, defaultFQBN, defaultAddress, defaultProtocol) + fqbn, port := arguments.CalculateFQBNAndPort(ctx, &portArgs, &fqbnArg, inst, srv, defaultFQBN, defaultAddress, defaultProtocol) - userFieldRes, err := upload.SupportedUserFields(context.Background(), &rpc.SupportedUserFieldsRequest{ + userFieldRes, err := srv.SupportedUserFields(ctx, &rpc.SupportedUserFieldsRequest{ Instance: inst, Fqbn: fqbn, Protocol: port.GetProtocol(), @@ -135,7 +134,7 @@ func runUploadCommand(args []string, uploadFieldsArgs map[string]string) { } msg += "\n" - if platform, err := core.PlatformSearch(&rpc.PlatformSearchRequest{ + if platform, err := srv.PlatformSearch(ctx, &rpc.PlatformSearchRequest{ Instance: inst, SearchArgs: platformErr.Platform, }); err != nil { @@ -178,7 +177,7 @@ func runUploadCommand(args []string, uploadFieldsArgs map[string]string) { prog := profile.GetProgrammer() if prog == "" || programmer.GetProgrammer() != "" { - prog = programmer.String(inst, fqbn) + prog = programmer.String(ctx, inst, srv, fqbn) } if prog == "" { prog = sketch.GetDefaultProgrammer() @@ -198,7 +197,8 @@ func runUploadCommand(args []string, uploadFieldsArgs map[string]string) { DryRun: dryRun, UserFields: fields, } - if res, err := upload.Upload(context.Background(), req, stdOut, stdErr); err != nil { + stream, streamResp := commands.UploadToServerStreams(ctx, stdOut, stdErr) + if err := srv.Upload(req, stream); err != nil { errcode := feedback.ErrGeneric if errors.Is(err, &cmderrors.ProgrammerRequiredForUploadError{}) { errcode = feedback.ErrMissingProgrammer @@ -212,7 +212,7 @@ func runUploadCommand(args []string, uploadFieldsArgs map[string]string) { feedback.PrintResult(&uploadResult{ Stdout: io.Stdout, Stderr: io.Stderr, - UpdatedUploadPort: result.NewPort(res.GetUpdatedUploadPort()), + UpdatedUploadPort: result.NewPort(streamResp().GetUpdatedUploadPort()), }) } } diff --git a/internal/cli/version/version.go b/internal/cli/version/version.go index 0f0c85872e0..8c5d8eec4c1 100644 --- a/internal/cli/version/version.go +++ b/internal/cli/version/version.go @@ -20,7 +20,6 @@ import ( "os" "strings" - "github.com/arduino/arduino-cli/commands/updatecheck" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/cli/updater" "github.com/arduino/arduino-cli/internal/i18n" @@ -33,19 +32,21 @@ import ( var tr = i18n.Tr // NewCommand created a new `version` command -func NewCommand() *cobra.Command { +func NewCommand(srv rpc.ArduinoCoreServiceServer) *cobra.Command { versionCommand := &cobra.Command{ Use: "version", Short: tr("Shows version number of Arduino CLI."), Long: tr("Shows the version number of Arduino CLI which is installed on your system."), Example: " " + os.Args[0] + " version", Args: cobra.NoArgs, - Run: runVersionCommand, + Run: func(cmd *cobra.Command, args []string) { + runVersionCommand(cmd.Context(), srv) + }, } return versionCommand } -func runVersionCommand(cmd *cobra.Command, args []string) { +func runVersionCommand(ctx context.Context, srv rpc.ArduinoCoreServiceServer) { logrus.Info("Executing `arduino-cli version`") info := version.VersionInfo @@ -57,7 +58,7 @@ func runVersionCommand(cmd *cobra.Command, args []string) { } latestVersion := "" - res, err := updatecheck.CheckForArduinoCLIUpdates(context.Background(), &rpc.CheckForArduinoCLIUpdatesRequest{}) + res, err := srv.CheckForArduinoCLIUpdates(ctx, &rpc.CheckForArduinoCLIUpdatesRequest{}) if err != nil { feedback.Warning("Failed to check for updates: " + err.Error()) } else { diff --git a/internal/docsgen/main.go b/internal/docsgen/main.go index 3448c9f5f62..fa175fff583 100644 --- a/internal/docsgen/main.go +++ b/internal/docsgen/main.go @@ -18,8 +18,8 @@ package main import ( "os" + "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/internal/cli" - "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/spf13/cobra/doc" ) @@ -31,11 +31,10 @@ func main() { os.MkdirAll(os.Args[1], 0755) // Create the output folder if it doesn't already exist - configuration.Settings = configuration.Init(configuration.FindConfigFileInArgsFallbackOnEnv(os.Args)) - cli := cli.NewCommand() + srv := commands.NewArduinoCoreServer() + cli := cli.NewCommand(srv) cli.DisableAutoGenTag = true // Disable addition of auto-generated date stamp - err := doc.GenMarkdownTree(cli, os.Args[1]) - if err != nil { + if err := doc.GenMarkdownTree(cli, os.Args[1]); err != nil { panic(err) } } diff --git a/internal/go-configmap/cli.go b/internal/go-configmap/cli.go new file mode 100644 index 00000000000..d467cfc9c32 --- /dev/null +++ b/internal/go-configmap/cli.go @@ -0,0 +1,109 @@ +// This file is part of arduino-cli. +// +// Copyright 2024 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 configmap + +import ( + "fmt" + "strconv" + "strings" +) + +func (c *Map) SetFromCLIArgs(key string, args ...string) error { + if len(args) == 0 { + c.Delete(key) + return nil + } + + // in case of schemaless configuration, we don't know the type of the setting + // we will save it as a string or array of strings + if len(c.schema) == 0 { + switch len(args) { + case 1: + c.Set(key, args[0]) + default: + c.Set(key, args) + } + return nil + } + + // Find the correct type for the given setting + valueType, ok := c.schema[key] + if !ok { + return fmt.Errorf("key not found: %s", key) + } + + var value any + isArray := false + { + var conversionError error + switch valueType.String() { + case "uint": + value, conversionError = strconv.Atoi(args[0]) + case "bool": + value, conversionError = strconv.ParseBool(args[0]) + case "string": + value = args[0] + case "[]string": + value = args + isArray = true + default: + return fmt.Errorf("unhandled type: %s", valueType) + } + if conversionError != nil { + return fmt.Errorf("error setting value: %v", conversionError) + } + } + if !isArray && len(args) != 1 { + return fmt.Errorf("error setting value: key is not an array, but multiple values were provided") + } + + return c.Set(key, value) +} + +func (c *Map) InjectEnvVars(env []string, prefix string) []error { + if prefix != "" { + prefix = strings.ToUpper(prefix) + "_" + } + + errs := []error{} + + envKeyToConfigKey := map[string]string{} + for _, k := range c.AllKeys() { + normalizedKey := prefix + strings.ToUpper(k) + normalizedKey = strings.ReplaceAll(normalizedKey, ".", "_") + envKeyToConfigKey[normalizedKey] = k + } + + for _, e := range env { + // Extract key and value from env + envKey, envValue, ok := strings.Cut(e, "=") + if !ok { + continue + } + + // Check if the configuration has a matching key + key, ok := envKeyToConfigKey[strings.ToUpper(envKey)] + if !ok { + continue + } + + // Update the configuration value + if err := c.SetFromCLIArgs(key, envValue); err != nil { + errs = append(errs, err) + } + } + return errs +} diff --git a/internal/go-configmap/configuration.go b/internal/go-configmap/configuration.go new file mode 100644 index 00000000000..e1a9dbb791e --- /dev/null +++ b/internal/go-configmap/configuration.go @@ -0,0 +1,219 @@ +// This file is part of arduino-cli. +// +// Copyright 2024 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 configmap + +import ( + "fmt" + "reflect" + "strings" +) + +type Map struct { + values map[string]any + schema map[string]reflect.Type +} + +func New() *Map { + return &Map{ + values: make(map[string]any), + schema: make(map[string]reflect.Type), + } +} + +func (c Map) Get(key string) any { + value, _ := c.GetOk(key) + return value +} + +func (c Map) GetOk(key string) (any, bool) { + keys := strings.Split(key, ".") + return c.get(keys) +} + +func (c Map) get(keys []string) (any, bool) { + if len(keys) == 0 { + return nil, false + } + value, ok := c.values[keys[0]] + if len(keys) == 1 { + return value, ok + } + + if subConf, ok := value.(*Map); ok { + return subConf.get(keys[1:]) + } + return nil, false +} + +func (c Map) Set(key string, value any) error { + if len(c.schema) > 0 { + t, ok := c.schema[key] + if !ok { + return fmt.Errorf("schema not defined for key '%s'", key) + } + newValue, err := tryConversion(value, t) + if err != nil { + return fmt.Errorf("invalid type for key '%s': %w", key, err) + } + value = newValue + } + keys := strings.Split(key, ".") + c.set(keys, value) + return nil +} + +func tryConversion(current any, desiredType reflect.Type) (any, error) { + currentType := reflect.TypeOf(current) + if currentType == desiredType { + return current, nil + } + + switch desiredType.Kind() { + case reflect.Uint: + // Exception for JSON decoder: json decoder will decode all numbers as float64 + if currentFloat, ok := current.(float64); ok { + return uint(currentFloat), nil + } + if currentInt, ok := current.(int); ok { + return uint(currentInt), nil + } + case reflect.Int: + // Exception for JSON decoder: json decoder will decode all numbers as float64 + if currentFloat, ok := current.(float64); ok { + return int(currentFloat), nil + } + case reflect.Array, reflect.Slice: + currentArray, ok := current.([]any) + if !ok && current != nil { + break + } + + resArray := reflect.MakeSlice(desiredType, len(currentArray), len(currentArray)) + for i, elem := range currentArray { + newElem, err := tryConversion(elem, desiredType.Elem()) + if err != nil { + return nil, err + } + resArray.Index(i).Set(reflect.ValueOf(newElem)) + } + return resArray.Interface(), nil + } + + currentTypeString := currentType.String() + if currentTypeString == "[]interface {}" { + currentTypeString = "array" + } + return nil, fmt.Errorf("invalid conversion, got %s but want %v", currentTypeString, desiredType) +} + +func (c Map) set(keys []string, value any) { + if len(keys) == 0 { + return + } + if len(keys) == 1 { + c.values[keys[0]] = value + return + } + + var subConf *Map + if subValue, ok := c.values[keys[0]]; !ok { + subConf = New() + c.values[keys[0]] = subConf + } else if conf, ok := subValue.(*Map); !ok { + subConf = New() + c.values[keys[0]] = subConf + } else { + subConf = conf + } + subConf.set(keys[1:], value) +} + +func (c Map) Delete(key string) { + keys := strings.Split(key, ".") + c.delete(keys) +} + +func (c Map) delete(keys []string) { + if len(keys) == 0 { + return + } + if len(keys) == 1 { + delete(c.values, keys[0]) + return + } + + if subValue, ok := c.values[keys[0]]; !ok { + return + } else if subConf, ok := subValue.(*Map); !ok { + return + } else { + subConf.delete(keys[1:]) + } +} + +func (c *Map) Merge(x *Map) error { + for xk, xv := range x.values { + if xSubConf, ok := xv.(*Map); ok { + if subConf, ok := c.values[xk].(*Map); ok { + if err := subConf.Merge(xSubConf); err != nil { + return err + } + continue + } + return fmt.Errorf("cannot merge sub-configuration into non sub-configuration: '%s'", xk) + } + + v, ok := c.values[xk] + if !ok { + return fmt.Errorf("target key do not exist: '%s'", xk) + } + if reflect.TypeOf(v) != reflect.TypeOf(xv) { + return fmt.Errorf("invalid types for key '%s': got %T but want %T", xk, v, xv) + } + c.values[xk] = xv + } + return nil +} + +func (c *Map) AllKeys() []string { + return c.allKeys("") +} + +func (c *Map) Schema() map[string]reflect.Type { + return c.schema +} + +func (c *Map) allKeys(prefix string) []string { + keys := []string{} + if len(c.schema) > 0 { + for k := range c.schema { + keys = append(keys, prefix+k) + } + } else { + for k, v := range c.values { + if subConf, ok := v.(*Map); ok { + keys = append(keys, subConf.allKeys(prefix+k+".")...) + } else { + keys = append(keys, prefix+k) + } + } + } + return keys +} + +func (c *Map) SetKeyTypeSchema(key string, t any) { + c.schema[key] = reflect.TypeOf(t) +} diff --git a/internal/go-configmap/configuration_test.go b/internal/go-configmap/configuration_test.go new file mode 100644 index 00000000000..c9c025cb3ce --- /dev/null +++ b/internal/go-configmap/configuration_test.go @@ -0,0 +1,218 @@ +// This file is part of arduino-cli. +// +// Copyright 2024 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 configmap_test + +import ( + "encoding/json" + "testing" + + "github.com/arduino/arduino-cli/internal/go-configmap" + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v3" +) + +func TestConfiguration(t *testing.T) { + c := configmap.New() + c.Set("foo", "bar") + c.Set("fooz.bar", "baz") + c.Set("answer", 42) + require.Equal(t, "bar", c.Get("foo")) + require.Equal(t, "baz", c.Get("fooz.bar")) + require.Equal(t, 42, c.Get("answer")) + + yml, err := yaml.Marshal(c) + require.NoError(t, err) + + d := configmap.New() + err = yaml.Unmarshal(yml, &d) + require.NoError(t, err) + + yml2, err := yaml.Marshal(d) + require.NoError(t, err) + require.Equal(t, string(yml), string(yml2)) + + d.Set("fooz.abc", "def") + d.Set("fooz.cde", "fgh") + require.Equal(t, "def", d.Get("fooz.abc")) + require.Equal(t, "fgh", d.Get("fooz.cde")) + d.Delete("fooz.abc") + require.Nil(t, d.Get("fooz.abc")) + require.Equal(t, "fgh", d.Get("fooz.cde")) + d.Delete("fooz") + require.Nil(t, d.Get("fooz.cde")) +} + +func TestYAMLCleanUpOfZeroValues(t *testing.T) { + inYml := []byte(` +foo: bar +directories: + builtins: {} +`) + c := configmap.New() + outYml1, err := yaml.Marshal(c) + require.NoError(t, err) + require.Equal(t, "{}\n", string(outYml1)) + + err = yaml.Unmarshal(inYml, &c) + require.NoError(t, err) + + outYml2, err := yaml.Marshal(c) + require.NoError(t, err) + require.Equal(t, "foo: bar\n", string(outYml2)) +} + +func TestApplyEnvVars(t *testing.T) { + c := configmap.New() + c.Set("foo", "bar") + c.Set("fooz.bar", "baz") + c.Set("answer", 42) + c.InjectEnvVars([]string{"APP_FOO=app-bar", "APP_FOOZ_BAR=app-baz"}, "APP") + require.Equal(t, "app-bar", c.Get("foo")) + require.Equal(t, "app-baz", c.Get("fooz.bar")) + require.Equal(t, 42, c.Get("answer")) +} + +func TestMerge(t *testing.T) { + c := configmap.New() + c.Set("foo", "bar") + c.Set("fooz.bar", "baz") + c.Set("answer", 42) + + d := configmap.New() + d.Set("answer", 24) + require.NoError(t, c.Merge(d)) + require.Equal(t, "bar", c.Get("foo")) + require.Equal(t, "baz", c.Get("fooz.bar")) + require.Equal(t, 24, c.Get("answer")) + + e := configmap.New() + e.Set("fooz.bar", "barz") + require.NoError(t, c.Merge(e)) + require.Equal(t, "bar", c.Get("foo")) + require.Equal(t, "barz", c.Get("fooz.bar")) + require.Equal(t, 24, c.Get("answer")) + + f := configmap.New() + f.Set("fooz.bar", 10) + require.EqualError(t, c.Merge(f), "invalid types for key 'bar': got string but want int") + + g := configmap.New() + g.Set("fooz.bart", "baz") + require.EqualError(t, c.Merge(g), "target key do not exist: 'bart'") +} + +func TestAllKeys(t *testing.T) { + { + c := configmap.New() + c.Set("foo", "bar") + c.Set("fooz.bar", "baz") + c.Set("answer", 42) + require.ElementsMatch(t, []string{"foo", "fooz.bar", "answer"}, c.AllKeys()) + } + { + inYml := []byte(` +foo: bar +dir: + a: yes + b: no + c: {} + d: + - 1 + - 2 +`) + c := configmap.New() + err := yaml.Unmarshal(inYml, &c) + require.NoError(t, err) + require.ElementsMatch(t, []string{"foo", "dir.a", "dir.b", "dir.d"}, c.AllKeys()) + } +} + +func TestSchema(t *testing.T) { + c := configmap.New() + c.SetKeyTypeSchema("string", "") + c.SetKeyTypeSchema("int", 15) + c.SetKeyTypeSchema("obj.string", "") + c.SetKeyTypeSchema("obj.int", 15) + c.SetKeyTypeSchema("uint", uint(15)) + c.SetKeyTypeSchema("obj.uint", uint(15)) + c.SetKeyTypeSchema("array", []string{}) + c.SetKeyTypeSchema("obj.array", []string{}) + + // Set array of string + require.NoError(t, c.Set("array", []string{"abc", "def"})) + require.NoError(t, c.Set("obj.array", []string{"abc", "def"})) + require.Equal(t, []string{"abc", "def"}, c.Get("array")) + require.Equal(t, []string{"abc", "def"}, c.Get("obj.array")) + // Set array of string with array of any + require.NoError(t, c.Set("array", []any{"abc", "def"})) + require.NoError(t, c.Set("obj.array", []any{"abc", "def"})) + require.Equal(t, []string{"abc", "def"}, c.Get("array")) + require.Equal(t, []string{"abc", "def"}, c.Get("obj.array")) + // Set array of string with array of int + require.EqualError(t, c.Set("array", []any{"abc", 123}), "invalid type for key 'array': invalid conversion, got int but want string") + require.EqualError(t, c.Set("obj.array", []any{"abc", 123}), "invalid type for key 'obj.array': invalid conversion, got int but want string") + + // Set string + require.NoError(t, c.Set("string", "abc")) + require.NoError(t, c.Set("obj.string", "abc")) + require.Equal(t, "abc", c.Get("string")) + require.Equal(t, "abc", c.Get("obj.string")) + // Set string with int + require.EqualError(t, c.Set("string", 123), "invalid type for key 'string': invalid conversion, got int but want string") + require.EqualError(t, c.Set("obj.string", 123), "invalid type for key 'obj.string': invalid conversion, got int but want string") + + // Set int + require.NoError(t, c.Set("int", 123)) + require.NoError(t, c.Set("obj.int", 123)) + require.Equal(t, 123, c.Get("int")) + require.Equal(t, 123, c.Get("obj.int")) + // Set int with string + require.EqualError(t, c.Set("int", "abc"), "invalid type for key 'int': invalid conversion, got string but want int") + require.EqualError(t, c.Set("obj.int", "abc"), "invalid type for key 'obj.int': invalid conversion, got string but want int") + + // Set uint + require.NoError(t, c.Set("uint", uint(234))) + require.NoError(t, c.Set("obj.uint", uint(234))) + require.Equal(t, uint(234), c.Get("uint")) + require.Equal(t, uint(234), c.Get("obj.uint")) + // Set uint using int + require.NoError(t, c.Set("uint", 345)) + require.NoError(t, c.Set("obj.uint", 345)) + require.Equal(t, uint(345), c.Get("uint")) + require.Equal(t, uint(345), c.Get("obj.uint")) + // Set uint using float + require.NoError(t, c.Set("uint", 456.0)) + require.NoError(t, c.Set("obj.uint", 456.0)) + require.Equal(t, uint(456), c.Get("uint")) + require.Equal(t, uint(456), c.Get("obj.uint")) + // Set uint using string + require.EqualError(t, c.Set("uint", "567"), "invalid type for key 'uint': invalid conversion, got string but want uint") + require.EqualError(t, c.Set("obj.uint", "567"), "invalid type for key 'obj.uint': invalid conversion, got string but want uint") + require.Equal(t, uint(456), c.Get("uint")) + require.Equal(t, uint(456), c.Get("obj.uint")) + + json1 := []byte(`{"string":"abcd","int":1234,"obj":{"string":"abcd","int":1234}}`) + require.NoError(t, json.Unmarshal(json1, &c)) + require.Equal(t, "abcd", c.Get("string")) + require.Equal(t, 1234, c.Get("int")) + require.Equal(t, "abcd", c.Get("obj.string")) + require.Equal(t, 1234, c.Get("obj.int")) + + json2 := []byte(`{"string":123,"int":123,"obj":{"string":"abc","int":123}}`) + require.EqualError(t, json.Unmarshal(json2, &c), "invalid type for key 'string': invalid conversion, got float64 but want string") + json3 := []byte(`{"string":"avc","int":123,"obj":{"string":123,"int":123}}`) + require.EqualError(t, json.Unmarshal(json3, &c), "invalid type for key 'obj.string': invalid conversion, got float64 but want string") +} diff --git a/internal/go-configmap/errors.go b/internal/go-configmap/errors.go new file mode 100644 index 00000000000..2ad42f3e129 --- /dev/null +++ b/internal/go-configmap/errors.go @@ -0,0 +1,54 @@ +// This file is part of arduino-cli. +// +// Copyright 2024 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 configmap + +import "strings" + +// UnmarshalErrors is a collection of errors that occurred during unmarshalling. +// Do not return this type directly, but use its result() method instead. +type UnmarshalErrors struct { + wrapped []error +} + +func (e *UnmarshalErrors) append(err error) { + e.wrapped = append(e.wrapped, err) +} + +func (e *UnmarshalErrors) result() error { + if len(e.wrapped) == 0 { + return nil + } + return e +} + +func (e *UnmarshalErrors) Error() string { + if len(e.wrapped) == 1 { + return e.wrapped[0].Error() + } + errs := []string{"multiple errors:"} + for _, err := range e.wrapped { + errs = append(errs, "- "+err.Error()) + } + return strings.Join(errs, "\n") +} + +// WrappedErrors returns the list of errors that occurred during unmarshalling. +func (e *UnmarshalErrors) WrappedErrors() []error { + if e == nil { + return nil + } + return e.wrapped +} diff --git a/internal/go-configmap/json.go b/internal/go-configmap/json.go new file mode 100644 index 00000000000..7ab0deaad4f --- /dev/null +++ b/internal/go-configmap/json.go @@ -0,0 +1,52 @@ +// This file is part of arduino-cli. +// +// Copyright 2024 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 configmap + +import "encoding/json" + +func (c Map) MarshalJSON() ([]byte, error) { + return json.Marshal(c.values) +} + +func (c *Map) UnmarshalJSON(data []byte) error { + in := map[string]any{} + if err := json.Unmarshal(data, &in); err != nil { + return err + } + + c.values = map[string]any{} + for k, v := range flattenMap(in) { + if err := c.Set(k, v); err != nil { + return err + } + } + return nil +} + +func flattenMap(in map[string]any) map[string]any { + out := map[string]any{} + for k, v := range in { + switch v := v.(type) { + case map[string]any: + for kk, vv := range flattenMap(v) { + out[k+"."+kk] = vv + } + default: + out[k] = v + } + } + return out +} diff --git a/internal/go-configmap/json_test.go b/internal/go-configmap/json_test.go new file mode 100644 index 00000000000..54566589baf --- /dev/null +++ b/internal/go-configmap/json_test.go @@ -0,0 +1,48 @@ +// This file is part of arduino-cli. +// +// Copyright 2024 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 configmap_test + +import ( + "encoding/json" + "fmt" + "testing" + + "github.com/arduino/arduino-cli/internal/go-configmap" + "github.com/stretchr/testify/require" +) + +func TestJson(t *testing.T) { + c := configmap.New() + c.Set("foo", "bar") + c.Set("fooz.bar", "baz") + c.Set("answer", 42) + require.Equal(t, "bar", c.Get("foo")) + require.Equal(t, "baz", c.Get("fooz.bar")) + require.Equal(t, 42, c.Get("answer")) + + j1, err := json.Marshal(c) + require.NoError(t, err) + fmt.Println(string(j1)) + + d := configmap.New() + err = json.Unmarshal(j1, d) + require.NoError(t, err) + require.Equal(t, "baz", d.Get("fooz.bar")) + + j2, err := json.Marshal(d) + require.NoError(t, err) + require.Equal(t, string(j1), string(j2)) +} diff --git a/internal/go-configmap/types.go b/internal/go-configmap/types.go new file mode 100644 index 00000000000..d6a032b8dd6 --- /dev/null +++ b/internal/go-configmap/types.go @@ -0,0 +1,215 @@ +// This file is part of arduino-cli. +// +// Copyright 2024 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 configmap + +import ( + "errors" + "fmt" + "time" +) + +func (c Map) GetStringOk(key string) (string, bool, error) { + v, ok := c.GetOk(key) + if !ok { + return "", false, nil + } + if s, ok := v.(string); ok { + return s, true, nil + } + return "", false, errors.New(key + " is not a string") +} + +func (c Map) GetString(key string) string { + v, ok, err := c.GetStringOk(key) + if err != nil { + panic(err.Error()) + } + if ok { + return v + } + return "" +} + +func (c Map) SetString(key string, value string) { + c.Set(key, value) +} + +func (c Map) GetBoolOk(key string) (bool, bool, error) { + v, ok := c.GetOk(key) + if !ok { + return false, false, nil + } + if b, ok := v.(bool); ok { + return b, true, nil + } + return false, false, errors.New(key + " is not a bool") +} + +func (c Map) GetBool(key string) bool { + v, ok, err := c.GetBoolOk(key) + if err != nil { + panic(err.Error()) + } + if ok { + return v + } + return false +} + +func (c Map) SetBool(key string, value bool) { + c.Set(key, value) +} + +func (c Map) GetUintOk(key string) (uint, bool, error) { + v, ok := c.GetOk(key) + if !ok { + return 0, false, nil + } + if i, ok := v.(uint); ok { + return i, true, nil + } + return 0, false, errors.New(key + " is not a uint") +} + +func (c Map) GetUint(key string) uint { + v, ok, err := c.GetUintOk(key) + if err != nil { + panic(err.Error()) + } + if ok { + return v + } + return 0 +} + +func (c Map) SetUint(key string, value uint) { + c.Set(key, value) +} + +func (c Map) GetIntOk(key string) (int, bool, error) { + v, ok := c.GetOk(key) + if !ok { + return 0, false, nil + } + if i, ok := v.(int); ok { + return i, true, nil + } + return 0, false, errors.New(key + " is not a uint") +} + +func (c Map) GetInt(key string) int { + v, ok, err := c.GetIntOk(key) + if err != nil { + panic(err.Error()) + } + if ok { + return v + } + return 0 +} + +func (c Map) SetInt(key string, value int) { + c.Set(key, value) +} + +func (c Map) GetUint32Ok(key string) (uint32, bool, error) { + v, ok := c.GetOk(key) + if !ok { + return 0, false, nil + } + if i, ok := v.(uint32); ok { + return i, true, nil + } + return 0, false, errors.New(key + " is not a uint32") +} + +func (c Map) GetUint32(key string) uint32 { + v, ok, err := c.GetUint32Ok(key) + if err != nil { + panic(err.Error()) + } + if ok { + return v + } + return 0 +} + +func (c Map) SetUint32(key string, value uint32) { + c.Set(key, value) +} + +func (c Map) GetStringSliceOk(key string) ([]string, bool, error) { + v, ok := c.GetOk(key) + if !ok { + return nil, false, nil + } + if genArray, ok := v.([]string); ok { + return genArray, true, nil + } + if genArray, ok := v.([]interface{}); ok { + // transform []interface{} to []string + var strArray []string + for i, gen := range genArray { + if str, ok := gen.(string); ok { + strArray = append(strArray, str) + } else { + return nil, false, fmt.Errorf("%s[%d] is not a string", key, i) + } + } + return strArray, true, nil + } + return nil, false, fmt.Errorf("%s is not an array of strings", key) +} + +func (c Map) GetStringSlice(key string) []string { + v, ok, err := c.GetStringSliceOk(key) + if err != nil { + panic(err.Error()) + } + if ok { + return v + } + return nil +} + +func (c Map) GetDurationOk(key string) (time.Duration, bool, error) { + v, ok := c.GetOk(key) + if !ok { + return 0, false, nil + } + if s, ok := v.(string); !ok { + return 0, false, errors.New(key + " is not a Duration") + } else if d, err := time.ParseDuration(s); err != nil { + return 0, false, fmt.Errorf("%s is not a valid Duration: %w", key, err) + } else { + return d, true, nil + } +} + +func (c Map) GetDuration(key string) time.Duration { + v, ok, err := c.GetDurationOk(key) + if err != nil { + panic(err.Error()) + } + if ok { + return v + } + return 0 +} + +func (c Map) SetDuration(key string, value time.Duration) { + c.SetString(key, value.String()) +} diff --git a/internal/go-configmap/yaml.go b/internal/go-configmap/yaml.go new file mode 100644 index 00000000000..e58f8f31ecf --- /dev/null +++ b/internal/go-configmap/yaml.go @@ -0,0 +1,39 @@ +// This file is part of arduino-cli. +// +// Copyright 2024 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 configmap + +import ( + "gopkg.in/yaml.v3" +) + +func (c Map) MarshalYAML() (interface{}, error) { + return c.values, nil +} + +func (c *Map) UnmarshalYAML(node *yaml.Node) error { + in := map[string]any{} + if err := node.Decode(&in); err != nil { + return err + } + + errs := &UnmarshalErrors{} + for k, v := range flattenMap(in) { + if err := c.Set(k, v); err != nil { + errs.append(err) + } + } + return errs.result() +} diff --git a/internal/go-configmap/yaml_test.go b/internal/go-configmap/yaml_test.go new file mode 100644 index 00000000000..9293f70662f --- /dev/null +++ b/internal/go-configmap/yaml_test.go @@ -0,0 +1,48 @@ +// This file is part of arduino-cli. +// +// Copyright 2024 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 configmap_test + +import ( + "fmt" + "testing" + + "github.com/arduino/arduino-cli/internal/go-configmap" + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v3" +) + +func TestYaml(t *testing.T) { + c := configmap.New() + c.Set("foo", "bar") + c.Set("fooz.bar", "baz") + c.Set("answer", 42) + require.Equal(t, "bar", c.Get("foo")) + require.Equal(t, "baz", c.Get("fooz.bar")) + require.Equal(t, 42, c.Get("answer")) + + y1, err := yaml.Marshal(c) + require.NoError(t, err) + fmt.Println(string(y1)) + + d := configmap.New() + err = yaml.Unmarshal(y1, d) + require.NoError(t, err) + require.Equal(t, "baz", d.Get("fooz.bar")) + + y2, err := yaml.Marshal(d) + require.NoError(t, err) + require.Equal(t, string(y1), string(y2)) +} diff --git a/internal/i18n/data/README.md b/internal/i18n/data/README.md index d52ec1782ae..5d8d773c26d 100644 --- a/internal/i18n/data/README.md +++ b/internal/i18n/data/README.md @@ -6,7 +6,7 @@ This folder contains the [localization](https://wikipedia.org/wiki/Language_loca at the source: - **en.po** - edit the string in the source code file indicated by the comment above it
e.g., a comment - `#: commands/upload/upload.go:615` indicates the source string is at line 615 of the file - [`commands/upload/upload.go`](../../../commands/upload/upload.go) + `#: internal/cli/lib/check_deps.go:102` indicates the source string is at line 102 of the file + [`internal/cli/lib/check_deps.go`](../../../internal/cli/lib/check_deps.go) - **All other files** - the localization is done on **Transifex**:
https://explore.transifex.com/arduino-1/arduino-cli/ diff --git a/internal/integrationtest/arduino-cli.go b/internal/integrationtest/arduino-cli.go index 3ec6f44f1ea..0e795f64d26 100644 --- a/internal/integrationtest/arduino-cli.go +++ b/internal/integrationtest/arduino-cli.go @@ -109,10 +109,10 @@ func NewArduinoCliWithinEnvironment(env *Environment, config *ArduinoCLIConfig) } cli.cliEnvVars = map[string]string{ - "LANG": "en", - "ARDUINO_DATA_DIR": cli.dataDir.String(), - "ARDUINO_DOWNLOADS_DIR": cli.stagingDir.String(), - "ARDUINO_SKETCHBOOK_DIR": cli.sketchbookDir.String(), + "LANG": "en", + "ARDUINO_DIRECTORIES_DATA": cli.dataDir.String(), + "ARDUINO_DIRECTORIES_DOWNLOADS": cli.stagingDir.String(), + "ARDUINO_DIRECTORIES_USER": cli.sketchbookDir.String(), "ARDUINO_BUILD_CACHE_COMPILATIONS_BEFORE_PURGE": "0", } env.RegisterCleanUpCallback(cli.CleanUp) @@ -326,7 +326,7 @@ func (cli *ArduinoCLI) run(stdoutBuff, stderrBuff io.Writer, stdinBuff io.Reader defer fmt.Fprintln(terminalOut, "::endgroup::") } - fmt.Fprintln(terminalOut, color.HiBlackString(">>> Running: ")+color.HiYellowString("%s %s", cli.path, strings.Join(args, " "))) + fmt.Fprintln(terminalOut, color.HiBlackString(">>> Running: ")+color.HiYellowString("%s %s %s", cli.path, strings.Join(args, " "), env)) cliProc, err := paths.NewProcessFromPath(cli.convertEnvForExecutils(env), cli.path, args...) cli.t.NoError(err) stdout, err := cliProc.StdoutPipe() @@ -444,8 +444,8 @@ func (cli *ArduinoCLI) Create() *ArduinoCLIInstance { // SetValue calls the "SetValue" gRPC method. func (cli *ArduinoCLI) SetValue(key, jsonData string) error { req := &commands.SettingsSetValueRequest{ - Key: key, - JsonData: jsonData, + Key: key, + EncodedValue: jsonData, } logCallf(">>> SetValue(%+v)\n", req) _, err := cli.daemonClient.SettingsSetValue(context.Background(), req) diff --git a/internal/integrationtest/board/board_test.go b/internal/integrationtest/board/board_test.go index 639a27d3a7b..0541f1a5c0f 100644 --- a/internal/integrationtest/board/board_test.go +++ b/internal/integrationtest/board/board_test.go @@ -651,7 +651,7 @@ func TestCLIStartupWithCorruptedInventory(t *testing.T) { require.NoError(t, f.Close()) // the CLI should not be able to load inventory and report it to the logs - _, stderr, err := cli.Run("core", "update-index", "-v") + stdout, _, err := cli.Run("core", "update-index", "-v") require.NoError(t, err) - require.Contains(t, string(stderr), "Error loading inventory store") + require.Contains(t, string(stdout), "Error loading inventory store") } diff --git a/internal/integrationtest/compile_1/compile_test.go b/internal/integrationtest/compile_1/compile_test.go index 6d6dd605602..27b31cb82c1 100644 --- a/internal/integrationtest/compile_1/compile_test.go +++ b/internal/integrationtest/compile_1/compile_test.go @@ -522,7 +522,7 @@ func compileWithExportBinariesConfig(t *testing.T, env *integrationtest.Environm { "config": { "sketch": { - "always_export_binaries": "true" + "always_export_binaries": true } } }`) diff --git a/internal/integrationtest/config/config_test.go b/internal/integrationtest/config/config_test.go index 107141ccfb4..5ed5145dd62 100644 --- a/internal/integrationtest/config/config_test.go +++ b/internal/integrationtest/config/config_test.go @@ -17,6 +17,7 @@ package config_test import ( "path/filepath" + "strings" "testing" "github.com/arduino/arduino-cli/internal/integrationtest" @@ -50,15 +51,6 @@ func TestInitWithExistingCustomConfig(t *testing.T) { err = yaml.Unmarshal(configFile, config) require.NoError(t, err) require.Equal(t, config["board_manager"]["additional_urls"].([]interface{})[0].(string), "https://example.com") - require.Equal(t, config["daemon"]["port"].(string), "50051") - require.Equal(t, config["directories"]["data"].(string), cli.DataDir().String()) - require.Equal(t, config["directories"]["downloads"].(string), cli.DownloadDir().String()) - require.Equal(t, config["directories"]["user"].(string), cli.SketchbookDir().String()) - require.Empty(t, config["logging"]["file"]) - require.Equal(t, config["logging"]["format"].(string), "text") - require.Equal(t, config["logging"]["level"].(string), "info") - require.Equal(t, config["metrics"]["addr"].(string), ":9090") - require.True(t, config["metrics"]["enabled"].(bool)) configFilePath := cli.WorkingDir().Join("config", "test", "config.yaml") require.NoFileExists(t, configFilePath.String()) @@ -71,15 +63,6 @@ func TestInitWithExistingCustomConfig(t *testing.T) { err = yaml.Unmarshal(configFile, config) require.NoError(t, err) require.Empty(t, config["board_manager"]["additional_urls"]) - require.Equal(t, config["daemon"]["port"].(string), "50051") - require.Equal(t, config["directories"]["data"].(string), cli.DataDir().String()) - require.Equal(t, config["directories"]["downloads"].(string), cli.DownloadDir().String()) - require.Equal(t, config["directories"]["user"].(string), cli.SketchbookDir().String()) - require.Empty(t, config["logging"]["file"]) - require.Equal(t, config["logging"]["format"].(string), "text") - require.Equal(t, config["logging"]["level"].(string), "info") - require.Equal(t, config["metrics"]["addr"].(string), ":9090") - require.True(t, config["metrics"]["enabled"].(bool)) } func TestInitOverwriteExistingCustomFile(t *testing.T) { @@ -96,15 +79,6 @@ func TestInitOverwriteExistingCustomFile(t *testing.T) { err = yaml.Unmarshal(configFile, config) require.NoError(t, err) require.Equal(t, config["board_manager"]["additional_urls"].([]interface{})[0].(string), "https://example.com") - require.Equal(t, config["daemon"]["port"].(string), "50051") - require.Equal(t, config["directories"]["data"].(string), cli.DataDir().String()) - require.Equal(t, config["directories"]["downloads"].(string), cli.DownloadDir().String()) - require.Equal(t, config["directories"]["user"].(string), cli.SketchbookDir().String()) - require.Empty(t, config["logging"]["file"]) - require.Equal(t, config["logging"]["format"].(string), "text") - require.Equal(t, config["logging"]["level"].(string), "info") - require.Equal(t, config["metrics"]["addr"].(string), ":9090") - require.True(t, config["metrics"]["enabled"].(bool)) stdout, _, err = cli.Run("config", "init", "--overwrite") require.NoError(t, err) @@ -115,15 +89,6 @@ func TestInitOverwriteExistingCustomFile(t *testing.T) { err = yaml.Unmarshal(configFile, config) require.NoError(t, err) require.Empty(t, config["board_manager"]["additional_urls"]) - require.Equal(t, config["daemon"]["port"].(string), "50051") - require.Equal(t, config["directories"]["data"].(string), cli.DataDir().String()) - require.Equal(t, config["directories"]["downloads"].(string), cli.DownloadDir().String()) - require.Equal(t, config["directories"]["user"].(string), cli.SketchbookDir().String()) - require.Empty(t, config["logging"]["file"]) - require.Equal(t, config["logging"]["format"].(string), "text") - require.Equal(t, config["logging"]["level"].(string), "info") - require.Equal(t, config["metrics"]["addr"].(string), ":9090") - require.True(t, config["metrics"]["enabled"].(bool)) } func TestInitDestAbsolutePath(t *testing.T) { @@ -289,21 +254,37 @@ func TestAddRemoveSetDeleteOnUnexistingKey(t *testing.T) { _, _, err := cli.Run("config", "init", "--dest-dir", ".") require.NoError(t, err) - _, stderr, err := cli.Run("config", "add", "some.key", "some_value", "--config-file", "arduino-cli.yaml") - require.Error(t, err) - require.Contains(t, string(stderr), "Settings key doesn't exist") + j, _, err := cli.Run("config", "dump", "--format", "json", "--config-file", "arduino-cli.yaml") + require.NoError(t, err) + requirejson.Contains(t, j, `{"config":{ "board_manager": {"additional_urls":[]} } }`) - _, stderr, err = cli.Run("config", "remove", "some.key", "some_value", "--config-file", "arduino-cli.yaml") - require.Error(t, err) - require.Contains(t, string(stderr), "Settings key doesn't exist") + // Delete array key + _, _, err = cli.Run("config", "delete", "board_manager.additional_urls", "--config-file", "arduino-cli.yaml") + require.NoError(t, err) + j, _, err = cli.Run("config", "dump", "--format", "json", "--config-file", "arduino-cli.yaml") + require.NoError(t, err) + requirejson.NotContains(t, j, `{"config":{ "board_manager": {"additional_urls":[]} } }`) - _, stderr, err = cli.Run("config", "set", "some.key", "some_value", "--config-file", "arduino-cli.yaml") - require.Error(t, err) - require.Contains(t, string(stderr), "Settings key doesn't exist") + // Add to non-existing array key + _, _, err = cli.Run("config", "add", "board_manager.additional_urls", "some_value", "--config-file", "arduino-cli.yaml") + require.NoError(t, err) + j, _, err = cli.Run("config", "dump", "--format", "json", "--config-file", "arduino-cli.yaml") + require.NoError(t, err) + requirejson.Contains(t, j, `{"config":{ "board_manager": {"additional_urls":["some_value"]} } }`) - _, stderr, err = cli.Run("config", "delete", "some.key", "--config-file", "arduino-cli.yaml") - require.Error(t, err) - require.Contains(t, string(stderr), "Cannot delete the key some.key: key not found in settings\n") + // Remove from non-existing array key + _, _, err = cli.Run("config", "remove", "board_manager.additional_urls", "some_value", "--config-file", "arduino-cli.yaml") + require.NoError(t, err) + j, _, err = cli.Run("config", "dump", "--format", "json", "--config-file", "arduino-cli.yaml") + require.NoError(t, err) + requirejson.NotContains(t, j, `{"config":{ "board_manager": {"additional_urls":["some_value"]} } }`) + + // Set on non-existing key + _, _, err = cli.Run("config", "set", "board_manager.additional_urls", "some_value", "other_value", "--config-file", "arduino-cli.yaml") + require.NoError(t, err) + j, _, err = cli.Run("config", "dump", "--format", "json", "--config-file", "arduino-cli.yaml") + require.NoError(t, err) + requirejson.Contains(t, j, `{"config":{ "board_manager": {"additional_urls":["some_value","other_value"]} } }`) } func TestAddSingleArgument(t *testing.T) { @@ -434,6 +415,8 @@ func TestAddOnUnsupportedKey(t *testing.T) { // Create a config file _, _, err := cli.Run("config", "init", "--dest-dir", ".") require.NoError(t, err) + _, _, err = cli.Run("config", "set", "daemon.port", "50051", "--config-file", "arduino-cli.yaml") + require.NoError(t, err) // Verifies default value stdout, _, err := cli.Run("config", "dump", "--json", "--config-file", "arduino-cli.yaml") @@ -542,6 +525,8 @@ func TestRemoveOnUnsupportedKey(t *testing.T) { // Create a config file _, _, err := cli.Run("config", "init", "--dest-dir", ".") require.NoError(t, err) + _, _, err = cli.Run("config", "set", "daemon.port", "50051", "--config-file", "arduino-cli.yaml") + require.NoError(t, err) // Verifies default value stdout, _, err := cli.Run("config", "dump", "--json", "--config-file", "arduino-cli.yaml") @@ -700,7 +685,7 @@ func TestSetStringWithSingleArgument(t *testing.T) { // Verifies default state stdout, _, err := cli.Run("config", "dump", "--json", "--config-file", "arduino-cli.yaml") require.NoError(t, err) - requirejson.Query(t, stdout, ".config | .logging | .level", "\"info\"") + requirejson.NotContains(t, stdout, `{"config":{"logging":{"level"}}}`) // Changes value _, _, err = cli.Run("config", "set", "logging.level", "trace", "--config-file", "arduino-cli.yaml") @@ -723,12 +708,12 @@ func TestSetStringWithMultipleArguments(t *testing.T) { // Verifies default state stdout, _, err := cli.Run("config", "dump", "--json", "--config-file", "arduino-cli.yaml") require.NoError(t, err) - requirejson.Query(t, stdout, ".config | .logging | .level", "\"info\"") + requirejson.NotContains(t, stdout, `{"config":{"logging":{"level"}}}`) // Tries to change value _, stderr, err := cli.Run("config", "set", "logging.level", "trace", "debug") require.Error(t, err) - require.Contains(t, string(stderr), "Can't set multiple values in key logging.level") + require.Contains(t, string(stderr), "Error setting value: invalid type for key 'logging.level': invalid conversion, got array but want string") } func TestSetBoolWithSingleArgument(t *testing.T) { @@ -742,7 +727,7 @@ func TestSetBoolWithSingleArgument(t *testing.T) { // Verifies default state stdout, _, err := cli.Run("config", "dump", "--json", "--config-file", "arduino-cli.yaml") require.NoError(t, err) - requirejson.Query(t, stdout, ".config | .library | .enable_unsafe_install", "false") + requirejson.NotContains(t, stdout, `{"config": {"library": {"enable_unsafe_install"}}}`) // Changes value _, _, err = cli.Run("config", "set", "library.enable_unsafe_install", "true", "--config-file", "arduino-cli.yaml") @@ -761,6 +746,8 @@ func TestSetBoolWithMultipleArguments(t *testing.T) { // Create a config file _, _, err := cli.Run("config", "init", "--dest-dir", ".") require.NoError(t, err) + _, _, err = cli.Run("config", "set", "library.enable_unsafe_install", "false", "--config-file", "arduino-cli.yaml") + require.NoError(t, err) // Verifies default state stdout, _, err := cli.Run("config", "dump", "--json", "--config-file", "arduino-cli.yaml") @@ -770,7 +757,7 @@ func TestSetBoolWithMultipleArguments(t *testing.T) { // Changes value _, stderr, err := cli.Run("config", "set", "library.enable_unsafe_install", "true", "foo", "--config-file", "arduino-cli.yaml") require.Error(t, err) - require.Contains(t, string(stderr), "Can't set multiple values in key library.enable_unsafe_install") + require.Contains(t, string(stderr), "Error setting value: invalid type for key 'library.enable_unsafe_install': invalid conversion, got array but want bool") } func TestDelete(t *testing.T) { @@ -780,6 +767,8 @@ func TestDelete(t *testing.T) { // Create a config file _, _, err := cli.Run("config", "init", "--dest-dir", ".") require.NoError(t, err) + _, _, err = cli.Run("config", "set", "library.enable_unsafe_install", "false", "--config-file", "arduino-cli.yaml") + require.NoError(t, err) // Verifies default state stdout, _, err := cli.Run("config", "dump", "--json", "--config-file", "arduino-cli.yaml") @@ -824,6 +813,8 @@ func TestGet(t *testing.T) { // Create a config file _, _, err := cli.Run("config", "init", "--dest-dir", ".") require.NoError(t, err) + _, _, err = cli.Run("config", "set", "daemon.port", "50051", "--config-file", "arduino-cli.yaml") + require.NoError(t, err) // Verifies default state stdout, _, err := cli.Run("config", "dump", "--json", "--config-file", "arduino-cli.yaml") @@ -843,7 +834,7 @@ func TestGet(t *testing.T) { // Get undefined key _, stderr, err := cli.Run("config", "get", "foo", "--json", "--config-file", "arduino-cli.yaml") require.Error(t, err) - requirejson.Contains(t, stderr, `{"error":"Cannot get the configuration key foo: key not found in settings"}`) + requirejson.Contains(t, stderr, `{"error":"Cannot get the configuration key foo: key foo not found"}`) } func TestInitializationOrderOfConfigThroughFlagAndEnv(t *testing.T) { @@ -851,9 +842,10 @@ func TestInitializationOrderOfConfigThroughFlagAndEnv(t *testing.T) { defer env.CleanUp() tmp := t.TempDir() - cliConfig, envConfig := paths.New(filepath.Join(tmp, "cli.yaml")), paths.New(filepath.Join(tmp, "env.yaml")) - cliConfig.WriteFile([]byte(`cli-test: "test"`)) - envConfig.WriteFile([]byte(`env-test: "test"`)) + cliConfig := paths.New(filepath.Join(tmp, "cli.yaml")) + cliConfig.WriteFile([]byte(`locale: "test"`)) + envConfig := paths.New(filepath.Join(tmp, "env.yaml")) + envConfig.WriteFile([]byte(`locale: "test2"`)) // No flag nor env specified. stdout, _, err := cli.Run("config", "dump", "--json") @@ -863,16 +855,50 @@ func TestInitializationOrderOfConfigThroughFlagAndEnv(t *testing.T) { // Flag specified stdout, _, err = cli.Run("config", "dump", "--config-file", cliConfig.String(), "--json") require.NoError(t, err) - requirejson.Contains(t, stdout, `{"config":{ "cli-test": "test" }}`) + requirejson.Contains(t, stdout, `{"config":{ "locale": "test" }}`) // Env specified customEnv := map[string]string{"ARDUINO_CONFIG_FILE": envConfig.String()} stdout, _, err = cli.RunWithCustomEnv(customEnv, "config", "dump", "--json") require.NoError(t, err) - requirejson.Contains(t, stdout, `{"config":{ "env-test": "test" }}`) + requirejson.Contains(t, stdout, `{"config":{ "locale": "test2" }}`) // Flag and env specified, flag takes precedence stdout, _, err = cli.RunWithCustomEnv(customEnv, "config", "dump", "--config-file", cliConfig.String(), "--json") require.NoError(t, err) - requirejson.Contains(t, stdout, `{"config":{ "cli-test": "test" }}`) + requirejson.Contains(t, stdout, `{"config":{ "locale": "test" }}`) +} + +func TestConfigLoadWithUnknownOrInvalidKeys(t *testing.T) { + env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t) + defer env.CleanUp() + + tmp := t.TempDir() + unkwnownConfig := paths.New(filepath.Join(tmp, "unknown.yaml")) + unkwnownConfig.WriteFile([]byte(` +unk: "test" +build.unk: 123 +`)) + t.Cleanup(func() { unkwnownConfig.Remove() }) + + // Run "config get" with a configuration containing an unknown key + out, _, err := cli.Run("config", "get", "locale", "--config-file", unkwnownConfig.String()) + require.NoError(t, err) + require.Equal(t, "en", strings.TrimSpace(string(out))) + + // Run "config get" with a configuration containing an invalid key + invalidConfig := paths.New(filepath.Join(tmp, "invalid.yaml")) + invalidConfig.WriteFile([]byte(`locale: 123`)) + t.Cleanup(func() { invalidConfig.Remove() }) + out, _, err = cli.Run("config", "get", "locale", "--config-file", invalidConfig.String()) + require.NoError(t, err) + require.Equal(t, "en", strings.TrimSpace(string(out))) + + // Run "config get" with a configuration containing a null array + nullArrayConfig := paths.New(filepath.Join(tmp, "null_array.yaml")) + nullArrayConfig.WriteFile([]byte(`board_manager.additional_urls:`)) + t.Cleanup(func() { nullArrayConfig.Remove() }) + out, _, err = cli.Run("config", "get", "locale", "--config-file", invalidConfig.String()) + require.NoError(t, err) + require.Equal(t, "en", strings.TrimSpace(string(out))) } diff --git a/internal/integrationtest/core/core_test.go b/internal/integrationtest/core/core_test.go index 864e45ab710..fbda8da1c0c 100644 --- a/internal/integrationtest/core/core_test.go +++ b/internal/integrationtest/core/core_test.go @@ -101,7 +101,7 @@ func TestCoreSearch(t *testing.T) { checkPlatformIsInJSONOutput := func(stdout []byte, id, version string) { jqquery := fmt.Sprintf(`{"platforms":[{id:"%s", releases:{"%s":{}}}]}`, id, version) - requirejson.Contains(t, out, jqquery, "platform %s@%s is missing from the output", id, version) + requirejson.Contains(t, stdout, jqquery, "platform %s@%s is missing from the output", id, version) } // Search all Retrokit platforms diff --git a/internal/integrationtest/daemon/daemon_test.go b/internal/integrationtest/daemon/daemon_test.go index 3bae601a01d..08e60cffccf 100644 --- a/internal/integrationtest/daemon/daemon_test.go +++ b/internal/integrationtest/daemon/daemon_test.go @@ -63,15 +63,15 @@ func TestArduinoCliDaemon(t *testing.T) { require.NoError(t, err) watcherCanceldCh := make(chan struct{}) go func() { + defer close(watcherCanceldCh) for { msg, err := watcher.Recv() if errors.Is(err, io.EOF) { - fmt.Println("Watcher EOF") + fmt.Println("Got EOF from watcher") return } if s, ok := status.FromError(err); ok && s.Code() == codes.Canceled { - fmt.Println("Watcher canceled") - watcherCanceldCh <- struct{}{} + fmt.Println("Got Canceled error from watcher") return } require.NoError(t, err, "BoardListWatch grpc call returned an error") @@ -357,7 +357,7 @@ func TestDaemonBundleLibInstall(t *testing.T) { } // Un-Set builtin libraries dir - err := cli.SetValue("directories.builtin.libraries", `""`) + err := cli.SetValue("directories.builtin.libraries", "") require.NoError(t, err) // Re-init @@ -503,7 +503,7 @@ func TestDaemonCoreUpgradePlatform(t *testing.T) { require.NoError(t, err) platform, upgradeError := analyzePlatformUpgradeClient(plUpgrade) - require.ErrorIs(t, upgradeError, (&cmderrors.PlatformAlreadyAtTheLatestVersionError{Platform: "esp8266:esp8266"}).ToRPCStatus().Err()) + require.ErrorIs(t, upgradeError, (&cmderrors.PlatformAlreadyAtTheLatestVersionError{Platform: "esp8266:esp8266"}).GRPCStatus().Err()) require.NotNil(t, platform) require.False(t, platform.GetMetadata().GetIndexed()) // the esp866 is not present in the additional-urls require.False(t, platform.GetRelease().GetMissingMetadata()) // install.json is present @@ -528,7 +528,7 @@ func TestDaemonCoreUpgradePlatform(t *testing.T) { require.NoError(t, err) platform, upgradeError := analyzePlatformUpgradeClient(plUpgrade) - require.ErrorIs(t, upgradeError, (&cmderrors.PlatformAlreadyAtTheLatestVersionError{Platform: "esp8266:esp8266"}).ToRPCStatus().Err()) + require.ErrorIs(t, upgradeError, (&cmderrors.PlatformAlreadyAtTheLatestVersionError{Platform: "esp8266:esp8266"}).GRPCStatus().Err()) require.NotNil(t, platform) require.False(t, platform.GetMetadata().GetIndexed()) // the esp866 is not present in the additional-urls require.True(t, platform.GetRelease().GetMissingMetadata()) // install.json is present diff --git a/main.go b/main.go index db1baad2bee..fa29df9c148 100644 --- a/main.go +++ b/main.go @@ -16,19 +16,73 @@ package main import ( + "context" + "fmt" + "io" "os" + "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/internal/cli" + "github.com/arduino/arduino-cli/internal/cli/config" "github.com/arduino/arduino-cli/internal/cli/configuration" "github.com/arduino/arduino-cli/internal/cli/feedback" "github.com/arduino/arduino-cli/internal/i18n" + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" + "github.com/arduino/go-paths-helper" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" ) func main() { - configuration.Settings = configuration.Init(configuration.FindConfigFileInArgsFallbackOnEnv(os.Args)) - i18n.Init(configuration.Settings.GetString("locale")) - arduinoCmd := cli.NewCommand() - if err := arduinoCmd.Execute(); err != nil { + // Disable logging until it is setup in the arduino-cli pre-run + logrus.SetOutput(io.Discard) + + // Create a new ArduinoCoreServer + srv := commands.NewArduinoCoreServer() + + // Search for the configuration file in the command line arguments and in the environment + configFile := configuration.FindConfigFileInArgsFallbackOnEnv(os.Args) + ctx := config.SetConfigFile(context.Background(), configFile) + + // Read the settings from the configuration file + openReq := &rpc.ConfigurationOpenRequest{SettingsFormat: "yaml"} + var configFileLoadingWarnings []string + if configData, err := paths.New(configFile).ReadFile(); err == nil { + openReq.EncodedSettings = string(configData) + } else if !os.IsNotExist(err) { + feedback.FatalError(fmt.Errorf("couldn't read configuration file: %w", err), feedback.ErrGeneric) + } + if resp, err := srv.ConfigurationOpen(ctx, openReq); err != nil { + feedback.FatalError(fmt.Errorf("couldn't load configuration: %w", err), feedback.ErrGeneric) + } else if warnings := resp.GetWarnings(); len(warnings) > 0 { + // Save the warnings to show them later when the feedback package is fully initialized + configFileLoadingWarnings = warnings + } + + // Get the current settings from the server + resp, err := srv.ConfigurationGet(ctx, &rpc.ConfigurationGetRequest{}) + if err != nil { + feedback.FatalError(err, feedback.ErrGeneric) + } + config := resp.GetConfiguration() + + // Setup i18n + i18n.Init(config.GetLocale()) + + // Setup command line parser with the server and settings + arduinoCmd := cli.NewCommand(srv) + parentPreRun := arduinoCmd.PersistentPreRun + arduinoCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) { + if parentPreRun != nil { + parentPreRun(cmd, args) + } + for _, warning := range configFileLoadingWarnings { + feedback.Warning(warning) + } + } + + // Execute the command line + if err := arduinoCmd.ExecuteContext(ctx); err != nil { feedback.FatalError(err, feedback.ErrGeneric) } } diff --git a/rpc/cc/arduino/cli/commands/v1/board.pb.go b/rpc/cc/arduino/cli/commands/v1/board.pb.go index 479446d50e6..65e778c1ba6 100644 --- a/rpc/cc/arduino/cli/commands/v1/board.pb.go +++ b/rpc/cc/arduino/cli/commands/v1/board.pb.go @@ -935,6 +935,8 @@ type BoardListResponse struct { // List of ports and the boards detected on those ports. Ports []*DetectedPort `protobuf:"bytes,1,rep,name=ports,proto3" json:"ports,omitempty"` + // Warning messages or errors coming from the discoveries. + Warnings []string `protobuf:"bytes,2,rep,name=warnings,proto3" json:"warnings,omitempty"` } func (x *BoardListResponse) Reset() { @@ -976,6 +978,13 @@ func (x *BoardListResponse) GetPorts() []*DetectedPort { return nil } +func (x *BoardListResponse) GetWarnings() []string { + if x != nil { + return x.Warnings + } + return nil +} + type DetectedPort struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1606,84 +1615,86 @@ var file_cc_arduino_cli_commands_v1_board_proto_rawDesc = []byte{ 0x63, 0x65, 0x52, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x71, 0x62, 0x6e, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x62, 0x6e, 0x22, 0x53, 0x0a, 0x11, 0x42, 0x6f, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x62, 0x6e, 0x22, 0x6f, 0x0a, 0x11, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x74, 0x65, - 0x63, 0x74, 0x65, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x22, - 0x98, 0x01, 0x0a, 0x0c, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x6f, 0x72, 0x74, - 0x12, 0x52, 0x0a, 0x0f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x5f, 0x62, 0x6f, 0x61, - 0x72, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x63, 0x63, 0x2e, 0x61, - 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, - 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x69, 0x73, 0x74, - 0x49, 0x74, 0x65, 0x6d, 0x52, 0x0e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x6f, - 0x61, 0x72, 0x64, 0x73, 0x12, 0x34, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, + 0x63, 0x74, 0x65, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x05, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x12, + 0x1a, 0x0a, 0x08, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x08, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x98, 0x01, 0x0a, 0x0c, + 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x52, 0x0a, 0x0f, + 0x6d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x5f, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, + 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, + 0x76, 0x31, 0x2e, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, + 0x52, 0x0e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x73, + 0x12, 0x34, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, + 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, + 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6f, 0x72, 0x74, + 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x22, 0xac, 0x01, 0x0a, 0x13, 0x42, 0x6f, 0x61, 0x72, 0x64, + 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, + 0x0a, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x24, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, + 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, + 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x41, 0x72, 0x67, + 0x73, 0x12, 0x32, 0x0a, 0x15, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x68, 0x69, 0x64, + 0x64, 0x65, 0x6e, 0x5f, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x13, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x48, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x42, + 0x6f, 0x61, 0x72, 0x64, 0x73, 0x22, 0x59, 0x0a, 0x14, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x69, + 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, + 0x06, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, + 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, + 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6f, 0x61, 0x72, 0x64, + 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x73, + 0x22, 0x59, 0x0a, 0x15, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x61, 0x74, + 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, 0x08, 0x69, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, 0x63, + 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, + 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, + 0x65, 0x52, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x8b, 0x01, 0x0a, 0x16, + 0x42, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x3c, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, + 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x52, 0x04, 0x70, + 0x6f, 0x72, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x96, 0x01, 0x0a, 0x0d, 0x42, 0x6f, + 0x61, 0x72, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x12, 0x0a, 0x04, 0x66, 0x71, 0x62, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, + 0x71, 0x62, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x73, 0x5f, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x69, 0x73, 0x48, 0x69, 0x64, 0x64, 0x65, 0x6e, + 0x12, 0x40, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, - 0x50, 0x6f, 0x72, 0x74, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x22, 0xac, 0x01, 0x0a, 0x13, 0x42, - 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x40, 0x0a, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, - 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, - 0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x08, 0x69, 0x6e, 0x73, 0x74, - 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x61, - 0x72, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x61, 0x72, 0x63, - 0x68, 0x41, 0x72, 0x67, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, - 0x5f, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x5f, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x73, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x48, 0x69, 0x64, - 0x64, 0x65, 0x6e, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x73, 0x22, 0x59, 0x0a, 0x14, 0x42, 0x6f, 0x61, - 0x72, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x41, 0x0a, 0x06, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x29, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, - 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x42, - 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x62, 0x6f, - 0x61, 0x72, 0x64, 0x73, 0x22, 0x59, 0x0a, 0x15, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x69, 0x73, - 0x74, 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, - 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x24, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, - 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x73, - 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x22, - 0x8b, 0x01, 0x0a, 0x16, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x61, 0x74, - 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x3c, 0x0a, 0x04, 0x70, 0x6f, 0x72, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, - 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, - 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x6f, 0x72, - 0x74, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x96, 0x01, - 0x0a, 0x0d, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x12, - 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x71, 0x62, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x66, 0x71, 0x62, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x73, 0x5f, 0x68, 0x69, - 0x64, 0x64, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x69, 0x73, 0x48, 0x69, - 0x64, 0x64, 0x65, 0x6e, 0x12, 0x40, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, - 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, - 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x52, 0x08, 0x70, 0x6c, - 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x22, 0xab, 0x01, 0x0a, 0x12, 0x42, 0x6f, 0x61, 0x72, 0x64, - 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, - 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x24, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, - 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x73, - 0x74, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, - 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x41, 0x72, 0x67, 0x73, - 0x12, 0x32, 0x0a, 0x15, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x68, 0x69, 0x64, 0x64, - 0x65, 0x6e, 0x5f, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x13, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x48, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x42, 0x6f, - 0x61, 0x72, 0x64, 0x73, 0x22, 0x58, 0x0a, 0x13, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x53, 0x65, 0x61, - 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x06, 0x62, - 0x6f, 0x61, 0x72, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x63, 0x63, + 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, + 0x72, 0x6d, 0x22, 0xab, 0x01, 0x0a, 0x12, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x53, 0x65, 0x61, 0x72, + 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, 0x08, 0x69, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, - 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x69, - 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x73, 0x42, 0x48, - 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x72, 0x64, - 0x75, 0x69, 0x6e, 0x6f, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2d, 0x63, 0x6c, 0x69, - 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x63, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2f, - 0x63, 0x6c, 0x69, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2f, 0x76, 0x31, 0x3b, - 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, + 0x65, 0x52, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, + 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x41, 0x72, 0x67, 0x73, 0x12, 0x32, 0x0a, 0x15, + 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x5f, 0x62, + 0x6f, 0x61, 0x72, 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x69, 0x6e, 0x63, + 0x6c, 0x75, 0x64, 0x65, 0x48, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x73, + 0x22, 0x58, 0x0a, 0x13, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x06, 0x62, 0x6f, 0x61, 0x72, 0x64, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, + 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, + 0x65, 0x6d, 0x52, 0x06, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x73, 0x42, 0x48, 0x5a, 0x46, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, + 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2d, 0x63, 0x6c, 0x69, 0x2f, 0x72, 0x70, 0x63, + 0x2f, 0x63, 0x63, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2f, 0x63, 0x6c, 0x69, 0x2f, + 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2f, 0x76, 0x31, 0x3b, 0x63, 0x6f, 0x6d, 0x6d, + 0x61, 0x6e, 0x64, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/rpc/cc/arduino/cli/commands/v1/board.proto b/rpc/cc/arduino/cli/commands/v1/board.proto index 8918473ec86..46a48c945f7 100644 --- a/rpc/cc/arduino/cli/commands/v1/board.proto +++ b/rpc/cc/arduino/cli/commands/v1/board.proto @@ -166,6 +166,8 @@ message BoardListRequest { message BoardListResponse { // List of ports and the boards detected on those ports. repeated DetectedPort ports = 1; + // Warning messages or errors coming from the discoveries. + repeated string warnings = 2; } message DetectedPort { diff --git a/rpc/cc/arduino/cli/commands/v1/commands.pb.go b/rpc/cc/arduino/cli/commands/v1/commands.pb.go index ca33b869446..859a2bf0900 100644 --- a/rpc/cc/arduino/cli/commands/v1/commands.pb.go +++ b/rpc/cc/arduino/cli/commands/v1/commands.pb.go @@ -2076,7 +2076,7 @@ var file_cc_arduino_cli_commands_v1_commands_proto_rawDesc = []byte{ 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x12, 0x34, 0x0a, 0x30, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x49, 0x4e, 0x53, 0x54, 0x41, 0x4e, 0x43, 0x45, 0x5f, 0x49, 0x4e, 0x49, 0x54, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x44, 0x45, 0x58, 0x5f, 0x44, 0x4f, 0x57, - 0x4e, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x04, 0x32, 0xd1, 0x2f, + 0x4e, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x04, 0x32, 0xfb, 0x2f, 0x0a, 0x12, 0x41, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x43, 0x6f, 0x72, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x61, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x29, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, @@ -2412,58 +2412,60 @@ var file_cc_arduino_cli_commands_v1_commands_proto_rawDesc = []byte{ 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x43, 0x61, 0x63, 0x68, 0x65, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x77, 0x0a, 0x0e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, - 0x73, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x12, 0x31, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, - 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, - 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x47, 0x65, 0x74, - 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x63, 0x63, 0x2e, - 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, - 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, - 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x74, - 0x0a, 0x0d, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x12, - 0x30, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, - 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, - 0x74, 0x69, 0x6e, 0x67, 0x73, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x31, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x80, 0x01, 0x0a, 0x11, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x61, 0x76, 0x65, 0x12, 0x34, 0x2e, 0x63, 0x63, + 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, + 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x61, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, + 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x61, 0x76, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x80, 0x01, 0x0a, 0x11, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x65, 0x6e, 0x12, 0x34, + 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, + 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x65, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, + 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4f, + 0x70, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7d, 0x0a, 0x10, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x47, 0x65, 0x74, 0x12, + 0x33, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, + 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, + 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x47, + 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x80, 0x01, 0x0a, 0x11, 0x53, + 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x45, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x61, 0x74, 0x65, + 0x12, 0x34, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, + 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, + 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x45, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, + 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x45, 0x6e, 0x75, 0x6d, + 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7d, 0x0a, + 0x10, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x47, 0x65, 0x74, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x12, 0x33, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, - 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7d, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, - 0x47, 0x65, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x33, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, - 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, - 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x47, 0x65, - 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, - 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, - 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x69, - 0x6e, 0x67, 0x73, 0x47, 0x65, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x7d, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x53, - 0x65, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x33, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, - 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, - 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x53, 0x65, 0x74, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x63, - 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, - 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, - 0x67, 0x73, 0x53, 0x65, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x74, 0x0a, 0x0d, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x57, 0x72, - 0x69, 0x74, 0x65, 0x12, 0x30, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, - 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, + 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x47, 0x65, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, + 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x47, 0x65, 0x74, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7d, 0x0a, 0x10, + 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x53, 0x65, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x12, 0x33, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, + 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, + 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x53, 0x65, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x57, 0x72, 0x69, 0x74, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x77, 0x0a, 0x0e, 0x53, 0x65, 0x74, 0x74, - 0x69, 0x6e, 0x67, 0x73, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x31, 0x2e, 0x63, 0x63, 0x2e, - 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, - 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, - 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, - 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x69, - 0x6e, 0x67, 0x73, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x42, 0x48, 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2d, - 0x63, 0x6c, 0x69, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x63, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, - 0x6e, 0x6f, 0x2f, 0x63, 0x6c, 0x69, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2f, - 0x76, 0x31, 0x3b, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x53, 0x65, 0x74, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x48, 0x5a, 0x46, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, + 0x6f, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2d, 0x63, 0x6c, 0x69, 0x2f, 0x72, 0x70, + 0x63, 0x2f, 0x63, 0x63, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2f, 0x63, 0x6c, 0x69, + 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2f, 0x76, 0x31, 0x3b, 0x63, 0x6f, 0x6d, + 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2549,12 +2551,12 @@ var file_cc_arduino_cli_commands_v1_commands_proto_goTypes = []interface{}{ (*DebugRequest)(nil), // 65: cc.arduino.cli.commands.v1.DebugRequest (*IsDebugSupportedRequest)(nil), // 66: cc.arduino.cli.commands.v1.IsDebugSupportedRequest (*GetDebugConfigRequest)(nil), // 67: cc.arduino.cli.commands.v1.GetDebugConfigRequest - (*SettingsGetAllRequest)(nil), // 68: cc.arduino.cli.commands.v1.SettingsGetAllRequest - (*SettingsMergeRequest)(nil), // 69: cc.arduino.cli.commands.v1.SettingsMergeRequest - (*SettingsGetValueRequest)(nil), // 70: cc.arduino.cli.commands.v1.SettingsGetValueRequest - (*SettingsSetValueRequest)(nil), // 71: cc.arduino.cli.commands.v1.SettingsSetValueRequest - (*SettingsWriteRequest)(nil), // 72: cc.arduino.cli.commands.v1.SettingsWriteRequest - (*SettingsDeleteRequest)(nil), // 73: cc.arduino.cli.commands.v1.SettingsDeleteRequest + (*ConfigurationSaveRequest)(nil), // 68: cc.arduino.cli.commands.v1.ConfigurationSaveRequest + (*ConfigurationOpenRequest)(nil), // 69: cc.arduino.cli.commands.v1.ConfigurationOpenRequest + (*ConfigurationGetRequest)(nil), // 70: cc.arduino.cli.commands.v1.ConfigurationGetRequest + (*SettingsEnumerateRequest)(nil), // 71: cc.arduino.cli.commands.v1.SettingsEnumerateRequest + (*SettingsGetValueRequest)(nil), // 72: cc.arduino.cli.commands.v1.SettingsGetValueRequest + (*SettingsSetValueRequest)(nil), // 73: cc.arduino.cli.commands.v1.SettingsSetValueRequest (*BoardDetailsResponse)(nil), // 74: cc.arduino.cli.commands.v1.BoardDetailsResponse (*BoardListResponse)(nil), // 75: cc.arduino.cli.commands.v1.BoardListResponse (*BoardListAllResponse)(nil), // 76: cc.arduino.cli.commands.v1.BoardListAllResponse @@ -2586,12 +2588,12 @@ var file_cc_arduino_cli_commands_v1_commands_proto_goTypes = []interface{}{ (*DebugResponse)(nil), // 102: cc.arduino.cli.commands.v1.DebugResponse (*IsDebugSupportedResponse)(nil), // 103: cc.arduino.cli.commands.v1.IsDebugSupportedResponse (*GetDebugConfigResponse)(nil), // 104: cc.arduino.cli.commands.v1.GetDebugConfigResponse - (*SettingsGetAllResponse)(nil), // 105: cc.arduino.cli.commands.v1.SettingsGetAllResponse - (*SettingsMergeResponse)(nil), // 106: cc.arduino.cli.commands.v1.SettingsMergeResponse - (*SettingsGetValueResponse)(nil), // 107: cc.arduino.cli.commands.v1.SettingsGetValueResponse - (*SettingsSetValueResponse)(nil), // 108: cc.arduino.cli.commands.v1.SettingsSetValueResponse - (*SettingsWriteResponse)(nil), // 109: cc.arduino.cli.commands.v1.SettingsWriteResponse - (*SettingsDeleteResponse)(nil), // 110: cc.arduino.cli.commands.v1.SettingsDeleteResponse + (*ConfigurationSaveResponse)(nil), // 105: cc.arduino.cli.commands.v1.ConfigurationSaveResponse + (*ConfigurationOpenResponse)(nil), // 106: cc.arduino.cli.commands.v1.ConfigurationOpenResponse + (*ConfigurationGetResponse)(nil), // 107: cc.arduino.cli.commands.v1.ConfigurationGetResponse + (*SettingsEnumerateResponse)(nil), // 108: cc.arduino.cli.commands.v1.SettingsEnumerateResponse + (*SettingsGetValueResponse)(nil), // 109: cc.arduino.cli.commands.v1.SettingsGetValueResponse + (*SettingsSetValueResponse)(nil), // 110: cc.arduino.cli.commands.v1.SettingsSetValueResponse } var file_cc_arduino_cli_commands_v1_commands_proto_depIdxs = []int32{ 31, // 0: cc.arduino.cli.commands.v1.CreateResponse.instance:type_name -> cc.arduino.cli.commands.v1.Instance @@ -2657,12 +2659,12 @@ var file_cc_arduino_cli_commands_v1_commands_proto_depIdxs = []int32{ 67, // 60: cc.arduino.cli.commands.v1.ArduinoCoreService.GetDebugConfig:input_type -> cc.arduino.cli.commands.v1.GetDebugConfigRequest 24, // 61: cc.arduino.cli.commands.v1.ArduinoCoreService.CheckForArduinoCLIUpdates:input_type -> cc.arduino.cli.commands.v1.CheckForArduinoCLIUpdatesRequest 26, // 62: cc.arduino.cli.commands.v1.ArduinoCoreService.CleanDownloadCacheDirectory:input_type -> cc.arduino.cli.commands.v1.CleanDownloadCacheDirectoryRequest - 68, // 63: cc.arduino.cli.commands.v1.ArduinoCoreService.SettingsGetAll:input_type -> cc.arduino.cli.commands.v1.SettingsGetAllRequest - 69, // 64: cc.arduino.cli.commands.v1.ArduinoCoreService.SettingsMerge:input_type -> cc.arduino.cli.commands.v1.SettingsMergeRequest - 70, // 65: cc.arduino.cli.commands.v1.ArduinoCoreService.SettingsGetValue:input_type -> cc.arduino.cli.commands.v1.SettingsGetValueRequest - 71, // 66: cc.arduino.cli.commands.v1.ArduinoCoreService.SettingsSetValue:input_type -> cc.arduino.cli.commands.v1.SettingsSetValueRequest - 72, // 67: cc.arduino.cli.commands.v1.ArduinoCoreService.SettingsWrite:input_type -> cc.arduino.cli.commands.v1.SettingsWriteRequest - 73, // 68: cc.arduino.cli.commands.v1.ArduinoCoreService.SettingsDelete:input_type -> cc.arduino.cli.commands.v1.SettingsDeleteRequest + 68, // 63: cc.arduino.cli.commands.v1.ArduinoCoreService.ConfigurationSave:input_type -> cc.arduino.cli.commands.v1.ConfigurationSaveRequest + 69, // 64: cc.arduino.cli.commands.v1.ArduinoCoreService.ConfigurationOpen:input_type -> cc.arduino.cli.commands.v1.ConfigurationOpenRequest + 70, // 65: cc.arduino.cli.commands.v1.ArduinoCoreService.ConfigurationGet:input_type -> cc.arduino.cli.commands.v1.ConfigurationGetRequest + 71, // 66: cc.arduino.cli.commands.v1.ArduinoCoreService.SettingsEnumerate:input_type -> cc.arduino.cli.commands.v1.SettingsEnumerateRequest + 72, // 67: cc.arduino.cli.commands.v1.ArduinoCoreService.SettingsGetValue:input_type -> cc.arduino.cli.commands.v1.SettingsGetValueRequest + 73, // 68: cc.arduino.cli.commands.v1.ArduinoCoreService.SettingsSetValue:input_type -> cc.arduino.cli.commands.v1.SettingsSetValueRequest 3, // 69: cc.arduino.cli.commands.v1.ArduinoCoreService.Create:output_type -> cc.arduino.cli.commands.v1.CreateResponse 5, // 70: cc.arduino.cli.commands.v1.ArduinoCoreService.Init:output_type -> cc.arduino.cli.commands.v1.InitResponse 8, // 71: cc.arduino.cli.commands.v1.ArduinoCoreService.Destroy:output_type -> cc.arduino.cli.commands.v1.DestroyResponse @@ -2706,12 +2708,12 @@ var file_cc_arduino_cli_commands_v1_commands_proto_depIdxs = []int32{ 104, // 109: cc.arduino.cli.commands.v1.ArduinoCoreService.GetDebugConfig:output_type -> cc.arduino.cli.commands.v1.GetDebugConfigResponse 25, // 110: cc.arduino.cli.commands.v1.ArduinoCoreService.CheckForArduinoCLIUpdates:output_type -> cc.arduino.cli.commands.v1.CheckForArduinoCLIUpdatesResponse 27, // 111: cc.arduino.cli.commands.v1.ArduinoCoreService.CleanDownloadCacheDirectory:output_type -> cc.arduino.cli.commands.v1.CleanDownloadCacheDirectoryResponse - 105, // 112: cc.arduino.cli.commands.v1.ArduinoCoreService.SettingsGetAll:output_type -> cc.arduino.cli.commands.v1.SettingsGetAllResponse - 106, // 113: cc.arduino.cli.commands.v1.ArduinoCoreService.SettingsMerge:output_type -> cc.arduino.cli.commands.v1.SettingsMergeResponse - 107, // 114: cc.arduino.cli.commands.v1.ArduinoCoreService.SettingsGetValue:output_type -> cc.arduino.cli.commands.v1.SettingsGetValueResponse - 108, // 115: cc.arduino.cli.commands.v1.ArduinoCoreService.SettingsSetValue:output_type -> cc.arduino.cli.commands.v1.SettingsSetValueResponse - 109, // 116: cc.arduino.cli.commands.v1.ArduinoCoreService.SettingsWrite:output_type -> cc.arduino.cli.commands.v1.SettingsWriteResponse - 110, // 117: cc.arduino.cli.commands.v1.ArduinoCoreService.SettingsDelete:output_type -> cc.arduino.cli.commands.v1.SettingsDeleteResponse + 105, // 112: cc.arduino.cli.commands.v1.ArduinoCoreService.ConfigurationSave:output_type -> cc.arduino.cli.commands.v1.ConfigurationSaveResponse + 106, // 113: cc.arduino.cli.commands.v1.ArduinoCoreService.ConfigurationOpen:output_type -> cc.arduino.cli.commands.v1.ConfigurationOpenResponse + 107, // 114: cc.arduino.cli.commands.v1.ArduinoCoreService.ConfigurationGet:output_type -> cc.arduino.cli.commands.v1.ConfigurationGetResponse + 108, // 115: cc.arduino.cli.commands.v1.ArduinoCoreService.SettingsEnumerate:output_type -> cc.arduino.cli.commands.v1.SettingsEnumerateResponse + 109, // 116: cc.arduino.cli.commands.v1.ArduinoCoreService.SettingsGetValue:output_type -> cc.arduino.cli.commands.v1.SettingsGetValueResponse + 110, // 117: cc.arduino.cli.commands.v1.ArduinoCoreService.SettingsSetValue:output_type -> cc.arduino.cli.commands.v1.SettingsSetValueResponse 69, // [69:118] is the sub-list for method output_type 20, // [20:69] is the sub-list for method input_type 20, // [20:20] is the sub-list for extension type_name diff --git a/rpc/cc/arduino/cli/commands/v1/commands.proto b/rpc/cc/arduino/cli/commands/v1/commands.proto index 78660e28d5c..ce5aba9ed91 100644 --- a/rpc/cc/arduino/cli/commands/v1/commands.proto +++ b/rpc/cc/arduino/cli/commands/v1/commands.proto @@ -197,25 +197,28 @@ service ArduinoCoreService { rpc CleanDownloadCacheDirectory(CleanDownloadCacheDirectoryRequest) returns (CleanDownloadCacheDirectoryResponse); - // List all the settings. - rpc SettingsGetAll(SettingsGetAllRequest) returns (SettingsGetAllResponse); + // Writes the settings currently stored in memory in a YAML file + rpc ConfigurationSave(ConfigurationSaveRequest) + returns (ConfigurationSaveResponse); - // Set multiple settings values at once. - rpc SettingsMerge(SettingsMergeRequest) returns (SettingsMergeResponse); + // Read the settings from a YAML file + rpc ConfigurationOpen(ConfigurationOpenRequest) + returns (ConfigurationOpenResponse); - // Get the value of a specific setting. + rpc ConfigurationGet(ConfigurationGetRequest) + returns (ConfigurationGetResponse); + + // Enumerate all the keys/values pairs available in the configuration + rpc SettingsEnumerate(SettingsEnumerateRequest) + returns (SettingsEnumerateResponse); + + // Get a single configuration value rpc SettingsGetValue(SettingsGetValueRequest) returns (SettingsGetValueResponse); - // Set the value of a specific setting. + // Set a single configuration value rpc SettingsSetValue(SettingsSetValueRequest) returns (SettingsSetValueResponse); - - // Writes to file settings currently stored in memory - rpc SettingsWrite(SettingsWriteRequest) returns (SettingsWriteResponse); - - // Deletes an entry and rewrites the file settings - rpc SettingsDelete(SettingsDeleteRequest) returns (SettingsDeleteResponse); } message CreateRequest {} diff --git a/rpc/cc/arduino/cli/commands/v1/commands_grpc.pb.go b/rpc/cc/arduino/cli/commands/v1/commands_grpc.pb.go index 5021e89d488..4ebd93a59f0 100644 --- a/rpc/cc/arduino/cli/commands/v1/commands_grpc.pb.go +++ b/rpc/cc/arduino/cli/commands/v1/commands_grpc.pb.go @@ -77,12 +77,12 @@ const ( ArduinoCoreService_GetDebugConfig_FullMethodName = "/cc.arduino.cli.commands.v1.ArduinoCoreService/GetDebugConfig" ArduinoCoreService_CheckForArduinoCLIUpdates_FullMethodName = "/cc.arduino.cli.commands.v1.ArduinoCoreService/CheckForArduinoCLIUpdates" ArduinoCoreService_CleanDownloadCacheDirectory_FullMethodName = "/cc.arduino.cli.commands.v1.ArduinoCoreService/CleanDownloadCacheDirectory" - ArduinoCoreService_SettingsGetAll_FullMethodName = "/cc.arduino.cli.commands.v1.ArduinoCoreService/SettingsGetAll" - ArduinoCoreService_SettingsMerge_FullMethodName = "/cc.arduino.cli.commands.v1.ArduinoCoreService/SettingsMerge" + ArduinoCoreService_ConfigurationSave_FullMethodName = "/cc.arduino.cli.commands.v1.ArduinoCoreService/ConfigurationSave" + ArduinoCoreService_ConfigurationOpen_FullMethodName = "/cc.arduino.cli.commands.v1.ArduinoCoreService/ConfigurationOpen" + ArduinoCoreService_ConfigurationGet_FullMethodName = "/cc.arduino.cli.commands.v1.ArduinoCoreService/ConfigurationGet" + ArduinoCoreService_SettingsEnumerate_FullMethodName = "/cc.arduino.cli.commands.v1.ArduinoCoreService/SettingsEnumerate" ArduinoCoreService_SettingsGetValue_FullMethodName = "/cc.arduino.cli.commands.v1.ArduinoCoreService/SettingsGetValue" ArduinoCoreService_SettingsSetValue_FullMethodName = "/cc.arduino.cli.commands.v1.ArduinoCoreService/SettingsSetValue" - ArduinoCoreService_SettingsWrite_FullMethodName = "/cc.arduino.cli.commands.v1.ArduinoCoreService/SettingsWrite" - ArduinoCoreService_SettingsDelete_FullMethodName = "/cc.arduino.cli.commands.v1.ArduinoCoreService/SettingsDelete" ) // ArduinoCoreServiceClient is the client API for ArduinoCoreService service. @@ -183,18 +183,17 @@ type ArduinoCoreServiceClient interface { CheckForArduinoCLIUpdates(ctx context.Context, in *CheckForArduinoCLIUpdatesRequest, opts ...grpc.CallOption) (*CheckForArduinoCLIUpdatesResponse, error) // Clean the download cache directory (where archives are downloaded). CleanDownloadCacheDirectory(ctx context.Context, in *CleanDownloadCacheDirectoryRequest, opts ...grpc.CallOption) (*CleanDownloadCacheDirectoryResponse, error) - // List all the settings. - SettingsGetAll(ctx context.Context, in *SettingsGetAllRequest, opts ...grpc.CallOption) (*SettingsGetAllResponse, error) - // Set multiple settings values at once. - SettingsMerge(ctx context.Context, in *SettingsMergeRequest, opts ...grpc.CallOption) (*SettingsMergeResponse, error) - // Get the value of a specific setting. + // Writes the settings currently stored in memory in a YAML file + ConfigurationSave(ctx context.Context, in *ConfigurationSaveRequest, opts ...grpc.CallOption) (*ConfigurationSaveResponse, error) + // Read the settings from a YAML file + ConfigurationOpen(ctx context.Context, in *ConfigurationOpenRequest, opts ...grpc.CallOption) (*ConfigurationOpenResponse, error) + ConfigurationGet(ctx context.Context, in *ConfigurationGetRequest, opts ...grpc.CallOption) (*ConfigurationGetResponse, error) + // Enumerate all the keys/values pairs available in the configuration + SettingsEnumerate(ctx context.Context, in *SettingsEnumerateRequest, opts ...grpc.CallOption) (*SettingsEnumerateResponse, error) + // Get a single configuration value SettingsGetValue(ctx context.Context, in *SettingsGetValueRequest, opts ...grpc.CallOption) (*SettingsGetValueResponse, error) - // Set the value of a specific setting. + // Set a single configuration value SettingsSetValue(ctx context.Context, in *SettingsSetValueRequest, opts ...grpc.CallOption) (*SettingsSetValueResponse, error) - // Writes to file settings currently stored in memory - SettingsWrite(ctx context.Context, in *SettingsWriteRequest, opts ...grpc.CallOption) (*SettingsWriteResponse, error) - // Deletes an entry and rewrites the file settings - SettingsDelete(ctx context.Context, in *SettingsDeleteRequest, opts ...grpc.CallOption) (*SettingsDeleteResponse, error) } type arduinoCoreServiceClient struct { @@ -1073,54 +1072,54 @@ func (c *arduinoCoreServiceClient) CleanDownloadCacheDirectory(ctx context.Conte return out, nil } -func (c *arduinoCoreServiceClient) SettingsGetAll(ctx context.Context, in *SettingsGetAllRequest, opts ...grpc.CallOption) (*SettingsGetAllResponse, error) { - out := new(SettingsGetAllResponse) - err := c.cc.Invoke(ctx, ArduinoCoreService_SettingsGetAll_FullMethodName, in, out, opts...) +func (c *arduinoCoreServiceClient) ConfigurationSave(ctx context.Context, in *ConfigurationSaveRequest, opts ...grpc.CallOption) (*ConfigurationSaveResponse, error) { + out := new(ConfigurationSaveResponse) + err := c.cc.Invoke(ctx, ArduinoCoreService_ConfigurationSave_FullMethodName, in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *arduinoCoreServiceClient) SettingsMerge(ctx context.Context, in *SettingsMergeRequest, opts ...grpc.CallOption) (*SettingsMergeResponse, error) { - out := new(SettingsMergeResponse) - err := c.cc.Invoke(ctx, ArduinoCoreService_SettingsMerge_FullMethodName, in, out, opts...) +func (c *arduinoCoreServiceClient) ConfigurationOpen(ctx context.Context, in *ConfigurationOpenRequest, opts ...grpc.CallOption) (*ConfigurationOpenResponse, error) { + out := new(ConfigurationOpenResponse) + err := c.cc.Invoke(ctx, ArduinoCoreService_ConfigurationOpen_FullMethodName, in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *arduinoCoreServiceClient) SettingsGetValue(ctx context.Context, in *SettingsGetValueRequest, opts ...grpc.CallOption) (*SettingsGetValueResponse, error) { - out := new(SettingsGetValueResponse) - err := c.cc.Invoke(ctx, ArduinoCoreService_SettingsGetValue_FullMethodName, in, out, opts...) +func (c *arduinoCoreServiceClient) ConfigurationGet(ctx context.Context, in *ConfigurationGetRequest, opts ...grpc.CallOption) (*ConfigurationGetResponse, error) { + out := new(ConfigurationGetResponse) + err := c.cc.Invoke(ctx, ArduinoCoreService_ConfigurationGet_FullMethodName, in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *arduinoCoreServiceClient) SettingsSetValue(ctx context.Context, in *SettingsSetValueRequest, opts ...grpc.CallOption) (*SettingsSetValueResponse, error) { - out := new(SettingsSetValueResponse) - err := c.cc.Invoke(ctx, ArduinoCoreService_SettingsSetValue_FullMethodName, in, out, opts...) +func (c *arduinoCoreServiceClient) SettingsEnumerate(ctx context.Context, in *SettingsEnumerateRequest, opts ...grpc.CallOption) (*SettingsEnumerateResponse, error) { + out := new(SettingsEnumerateResponse) + err := c.cc.Invoke(ctx, ArduinoCoreService_SettingsEnumerate_FullMethodName, in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *arduinoCoreServiceClient) SettingsWrite(ctx context.Context, in *SettingsWriteRequest, opts ...grpc.CallOption) (*SettingsWriteResponse, error) { - out := new(SettingsWriteResponse) - err := c.cc.Invoke(ctx, ArduinoCoreService_SettingsWrite_FullMethodName, in, out, opts...) +func (c *arduinoCoreServiceClient) SettingsGetValue(ctx context.Context, in *SettingsGetValueRequest, opts ...grpc.CallOption) (*SettingsGetValueResponse, error) { + out := new(SettingsGetValueResponse) + err := c.cc.Invoke(ctx, ArduinoCoreService_SettingsGetValue_FullMethodName, in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *arduinoCoreServiceClient) SettingsDelete(ctx context.Context, in *SettingsDeleteRequest, opts ...grpc.CallOption) (*SettingsDeleteResponse, error) { - out := new(SettingsDeleteResponse) - err := c.cc.Invoke(ctx, ArduinoCoreService_SettingsDelete_FullMethodName, in, out, opts...) +func (c *arduinoCoreServiceClient) SettingsSetValue(ctx context.Context, in *SettingsSetValueRequest, opts ...grpc.CallOption) (*SettingsSetValueResponse, error) { + out := new(SettingsSetValueResponse) + err := c.cc.Invoke(ctx, ArduinoCoreService_SettingsSetValue_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -1225,18 +1224,17 @@ type ArduinoCoreServiceServer interface { CheckForArduinoCLIUpdates(context.Context, *CheckForArduinoCLIUpdatesRequest) (*CheckForArduinoCLIUpdatesResponse, error) // Clean the download cache directory (where archives are downloaded). CleanDownloadCacheDirectory(context.Context, *CleanDownloadCacheDirectoryRequest) (*CleanDownloadCacheDirectoryResponse, error) - // List all the settings. - SettingsGetAll(context.Context, *SettingsGetAllRequest) (*SettingsGetAllResponse, error) - // Set multiple settings values at once. - SettingsMerge(context.Context, *SettingsMergeRequest) (*SettingsMergeResponse, error) - // Get the value of a specific setting. + // Writes the settings currently stored in memory in a YAML file + ConfigurationSave(context.Context, *ConfigurationSaveRequest) (*ConfigurationSaveResponse, error) + // Read the settings from a YAML file + ConfigurationOpen(context.Context, *ConfigurationOpenRequest) (*ConfigurationOpenResponse, error) + ConfigurationGet(context.Context, *ConfigurationGetRequest) (*ConfigurationGetResponse, error) + // Enumerate all the keys/values pairs available in the configuration + SettingsEnumerate(context.Context, *SettingsEnumerateRequest) (*SettingsEnumerateResponse, error) + // Get a single configuration value SettingsGetValue(context.Context, *SettingsGetValueRequest) (*SettingsGetValueResponse, error) - // Set the value of a specific setting. + // Set a single configuration value SettingsSetValue(context.Context, *SettingsSetValueRequest) (*SettingsSetValueResponse, error) - // Writes to file settings currently stored in memory - SettingsWrite(context.Context, *SettingsWriteRequest) (*SettingsWriteResponse, error) - // Deletes an entry and rewrites the file settings - SettingsDelete(context.Context, *SettingsDeleteRequest) (*SettingsDeleteResponse, error) mustEmbedUnimplementedArduinoCoreServiceServer() } @@ -1373,11 +1371,17 @@ func (UnimplementedArduinoCoreServiceServer) CheckForArduinoCLIUpdates(context.C func (UnimplementedArduinoCoreServiceServer) CleanDownloadCacheDirectory(context.Context, *CleanDownloadCacheDirectoryRequest) (*CleanDownloadCacheDirectoryResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method CleanDownloadCacheDirectory not implemented") } -func (UnimplementedArduinoCoreServiceServer) SettingsGetAll(context.Context, *SettingsGetAllRequest) (*SettingsGetAllResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method SettingsGetAll not implemented") +func (UnimplementedArduinoCoreServiceServer) ConfigurationSave(context.Context, *ConfigurationSaveRequest) (*ConfigurationSaveResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ConfigurationSave not implemented") +} +func (UnimplementedArduinoCoreServiceServer) ConfigurationOpen(context.Context, *ConfigurationOpenRequest) (*ConfigurationOpenResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ConfigurationOpen not implemented") +} +func (UnimplementedArduinoCoreServiceServer) ConfigurationGet(context.Context, *ConfigurationGetRequest) (*ConfigurationGetResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ConfigurationGet not implemented") } -func (UnimplementedArduinoCoreServiceServer) SettingsMerge(context.Context, *SettingsMergeRequest) (*SettingsMergeResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method SettingsMerge not implemented") +func (UnimplementedArduinoCoreServiceServer) SettingsEnumerate(context.Context, *SettingsEnumerateRequest) (*SettingsEnumerateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SettingsEnumerate not implemented") } func (UnimplementedArduinoCoreServiceServer) SettingsGetValue(context.Context, *SettingsGetValueRequest) (*SettingsGetValueResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SettingsGetValue not implemented") @@ -1385,12 +1389,6 @@ func (UnimplementedArduinoCoreServiceServer) SettingsGetValue(context.Context, * func (UnimplementedArduinoCoreServiceServer) SettingsSetValue(context.Context, *SettingsSetValueRequest) (*SettingsSetValueResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SettingsSetValue not implemented") } -func (UnimplementedArduinoCoreServiceServer) SettingsWrite(context.Context, *SettingsWriteRequest) (*SettingsWriteResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method SettingsWrite not implemented") -} -func (UnimplementedArduinoCoreServiceServer) SettingsDelete(context.Context, *SettingsDeleteRequest) (*SettingsDeleteResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method SettingsDelete not implemented") -} func (UnimplementedArduinoCoreServiceServer) mustEmbedUnimplementedArduinoCoreServiceServer() {} // UnsafeArduinoCoreServiceServer may be embedded to opt out of forward compatibility for this service. @@ -2251,110 +2249,110 @@ func _ArduinoCoreService_CleanDownloadCacheDirectory_Handler(srv interface{}, ct return interceptor(ctx, in, info, handler) } -func _ArduinoCoreService_SettingsGetAll_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SettingsGetAllRequest) +func _ArduinoCoreService_ConfigurationSave_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ConfigurationSaveRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(ArduinoCoreServiceServer).SettingsGetAll(ctx, in) + return srv.(ArduinoCoreServiceServer).ConfigurationSave(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: ArduinoCoreService_SettingsGetAll_FullMethodName, + FullMethod: ArduinoCoreService_ConfigurationSave_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ArduinoCoreServiceServer).SettingsGetAll(ctx, req.(*SettingsGetAllRequest)) + return srv.(ArduinoCoreServiceServer).ConfigurationSave(ctx, req.(*ConfigurationSaveRequest)) } return interceptor(ctx, in, info, handler) } -func _ArduinoCoreService_SettingsMerge_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SettingsMergeRequest) +func _ArduinoCoreService_ConfigurationOpen_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ConfigurationOpenRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(ArduinoCoreServiceServer).SettingsMerge(ctx, in) + return srv.(ArduinoCoreServiceServer).ConfigurationOpen(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: ArduinoCoreService_SettingsMerge_FullMethodName, + FullMethod: ArduinoCoreService_ConfigurationOpen_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ArduinoCoreServiceServer).SettingsMerge(ctx, req.(*SettingsMergeRequest)) + return srv.(ArduinoCoreServiceServer).ConfigurationOpen(ctx, req.(*ConfigurationOpenRequest)) } return interceptor(ctx, in, info, handler) } -func _ArduinoCoreService_SettingsGetValue_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SettingsGetValueRequest) +func _ArduinoCoreService_ConfigurationGet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ConfigurationGetRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(ArduinoCoreServiceServer).SettingsGetValue(ctx, in) + return srv.(ArduinoCoreServiceServer).ConfigurationGet(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: ArduinoCoreService_SettingsGetValue_FullMethodName, + FullMethod: ArduinoCoreService_ConfigurationGet_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ArduinoCoreServiceServer).SettingsGetValue(ctx, req.(*SettingsGetValueRequest)) + return srv.(ArduinoCoreServiceServer).ConfigurationGet(ctx, req.(*ConfigurationGetRequest)) } return interceptor(ctx, in, info, handler) } -func _ArduinoCoreService_SettingsSetValue_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SettingsSetValueRequest) +func _ArduinoCoreService_SettingsEnumerate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SettingsEnumerateRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(ArduinoCoreServiceServer).SettingsSetValue(ctx, in) + return srv.(ArduinoCoreServiceServer).SettingsEnumerate(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: ArduinoCoreService_SettingsSetValue_FullMethodName, + FullMethod: ArduinoCoreService_SettingsEnumerate_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ArduinoCoreServiceServer).SettingsSetValue(ctx, req.(*SettingsSetValueRequest)) + return srv.(ArduinoCoreServiceServer).SettingsEnumerate(ctx, req.(*SettingsEnumerateRequest)) } return interceptor(ctx, in, info, handler) } -func _ArduinoCoreService_SettingsWrite_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SettingsWriteRequest) +func _ArduinoCoreService_SettingsGetValue_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SettingsGetValueRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(ArduinoCoreServiceServer).SettingsWrite(ctx, in) + return srv.(ArduinoCoreServiceServer).SettingsGetValue(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: ArduinoCoreService_SettingsWrite_FullMethodName, + FullMethod: ArduinoCoreService_SettingsGetValue_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ArduinoCoreServiceServer).SettingsWrite(ctx, req.(*SettingsWriteRequest)) + return srv.(ArduinoCoreServiceServer).SettingsGetValue(ctx, req.(*SettingsGetValueRequest)) } return interceptor(ctx, in, info, handler) } -func _ArduinoCoreService_SettingsDelete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SettingsDeleteRequest) +func _ArduinoCoreService_SettingsSetValue_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SettingsSetValueRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(ArduinoCoreServiceServer).SettingsDelete(ctx, in) + return srv.(ArduinoCoreServiceServer).SettingsSetValue(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: ArduinoCoreService_SettingsDelete_FullMethodName, + FullMethod: ArduinoCoreService_SettingsSetValue_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ArduinoCoreServiceServer).SettingsDelete(ctx, req.(*SettingsDeleteRequest)) + return srv.(ArduinoCoreServiceServer).SettingsSetValue(ctx, req.(*SettingsSetValueRequest)) } return interceptor(ctx, in, info, handler) } @@ -2455,28 +2453,28 @@ var ArduinoCoreService_ServiceDesc = grpc.ServiceDesc{ Handler: _ArduinoCoreService_CleanDownloadCacheDirectory_Handler, }, { - MethodName: "SettingsGetAll", - Handler: _ArduinoCoreService_SettingsGetAll_Handler, + MethodName: "ConfigurationSave", + Handler: _ArduinoCoreService_ConfigurationSave_Handler, }, { - MethodName: "SettingsMerge", - Handler: _ArduinoCoreService_SettingsMerge_Handler, + MethodName: "ConfigurationOpen", + Handler: _ArduinoCoreService_ConfigurationOpen_Handler, }, { - MethodName: "SettingsGetValue", - Handler: _ArduinoCoreService_SettingsGetValue_Handler, + MethodName: "ConfigurationGet", + Handler: _ArduinoCoreService_ConfigurationGet_Handler, }, { - MethodName: "SettingsSetValue", - Handler: _ArduinoCoreService_SettingsSetValue_Handler, + MethodName: "SettingsEnumerate", + Handler: _ArduinoCoreService_SettingsEnumerate_Handler, }, { - MethodName: "SettingsWrite", - Handler: _ArduinoCoreService_SettingsWrite_Handler, + MethodName: "SettingsGetValue", + Handler: _ArduinoCoreService_SettingsGetValue_Handler, }, { - MethodName: "SettingsDelete", - Handler: _ArduinoCoreService_SettingsDelete_Handler, + MethodName: "SettingsSetValue", + Handler: _ArduinoCoreService_SettingsSetValue_Handler, }, }, Streams: []grpc.StreamDesc{ diff --git a/rpc/cc/arduino/cli/commands/v1/settings.pb.go b/rpc/cc/arduino/cli/commands/v1/settings.pb.go index 8a594bbd49f..e367998c9ab 100644 --- a/rpc/cc/arduino/cli/commands/v1/settings.pb.go +++ b/rpc/cc/arduino/cli/commands/v1/settings.pb.go @@ -35,17 +35,28 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -type SettingsGetAllResponse struct { +// Configuration to apply to the given instance. +// Any missing field will be kept at the default value. +type Configuration struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // The settings, in JSON format. - JsonData string `protobuf:"bytes,1,opt,name=json_data,json=jsonData,proto3" json:"json_data,omitempty"` -} - -func (x *SettingsGetAllResponse) Reset() { - *x = SettingsGetAllResponse{} + Directories *Configuration_Directories `protobuf:"bytes,1,opt,name=directories,proto3" json:"directories,omitempty"` + Network *Configuration_Network `protobuf:"bytes,2,opt,name=network,proto3" json:"network,omitempty"` + Sketch *Configuration_Sketch `protobuf:"bytes,3,opt,name=sketch,proto3" json:"sketch,omitempty"` + BuildCache *Configuration_BuildCache `protobuf:"bytes,4,opt,name=build_cache,json=buildCache,proto3" json:"build_cache,omitempty"` + BoardManager *Configuration_BoardManager `protobuf:"bytes,5,opt,name=board_manager,json=boardManager,proto3" json:"board_manager,omitempty"` + Daemon *Configuration_Daemon `protobuf:"bytes,6,opt,name=daemon,proto3" json:"daemon,omitempty"` + Output *Configuration_Output `protobuf:"bytes,7,opt,name=output,proto3" json:"output,omitempty"` + Logging *Configuration_Logging `protobuf:"bytes,8,opt,name=logging,proto3" json:"logging,omitempty"` + Library *Configuration_Library `protobuf:"bytes,9,opt,name=library,proto3" json:"library,omitempty"` + Updater *Configuration_Updater `protobuf:"bytes,10,opt,name=updater,proto3" json:"updater,omitempty"` + Locale *string `protobuf:"bytes,100,opt,name=locale,proto3,oneof" json:"locale,omitempty"` +} + +func (x *Configuration) Reset() { + *x = Configuration{} if protoimpl.UnsafeEnabled { mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -53,13 +64,13 @@ func (x *SettingsGetAllResponse) Reset() { } } -func (x *SettingsGetAllResponse) String() string { +func (x *Configuration) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SettingsGetAllResponse) ProtoMessage() {} +func (*Configuration) ProtoMessage() {} -func (x *SettingsGetAllResponse) ProtoReflect() protoreflect.Message { +func (x *Configuration) ProtoReflect() protoreflect.Message { mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -71,29 +82,96 @@ func (x *SettingsGetAllResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SettingsGetAllResponse.ProtoReflect.Descriptor instead. -func (*SettingsGetAllResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use Configuration.ProtoReflect.Descriptor instead. +func (*Configuration) Descriptor() ([]byte, []int) { return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{0} } -func (x *SettingsGetAllResponse) GetJsonData() string { +func (x *Configuration) GetDirectories() *Configuration_Directories { + if x != nil { + return x.Directories + } + return nil +} + +func (x *Configuration) GetNetwork() *Configuration_Network { + if x != nil { + return x.Network + } + return nil +} + +func (x *Configuration) GetSketch() *Configuration_Sketch { + if x != nil { + return x.Sketch + } + return nil +} + +func (x *Configuration) GetBuildCache() *Configuration_BuildCache { + if x != nil { + return x.BuildCache + } + return nil +} + +func (x *Configuration) GetBoardManager() *Configuration_BoardManager { + if x != nil { + return x.BoardManager + } + return nil +} + +func (x *Configuration) GetDaemon() *Configuration_Daemon { + if x != nil { + return x.Daemon + } + return nil +} + +func (x *Configuration) GetOutput() *Configuration_Output { + if x != nil { + return x.Output + } + return nil +} + +func (x *Configuration) GetLogging() *Configuration_Logging { + if x != nil { + return x.Logging + } + return nil +} + +func (x *Configuration) GetLibrary() *Configuration_Library { + if x != nil { + return x.Library + } + return nil +} + +func (x *Configuration) GetUpdater() *Configuration_Updater { if x != nil { - return x.JsonData + return x.Updater + } + return nil +} + +func (x *Configuration) GetLocale() string { + if x != nil && x.Locale != nil { + return *x.Locale } return "" } -type SettingsMergeRequest struct { +type ConfigurationGetRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - - // The settings, in JSON format. - JsonData string `protobuf:"bytes,1,opt,name=json_data,json=jsonData,proto3" json:"json_data,omitempty"` } -func (x *SettingsMergeRequest) Reset() { - *x = SettingsMergeRequest{} +func (x *ConfigurationGetRequest) Reset() { + *x = ConfigurationGetRequest{} if protoimpl.UnsafeEnabled { mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -101,13 +179,13 @@ func (x *SettingsMergeRequest) Reset() { } } -func (x *SettingsMergeRequest) String() string { +func (x *ConfigurationGetRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SettingsMergeRequest) ProtoMessage() {} +func (*ConfigurationGetRequest) ProtoMessage() {} -func (x *SettingsMergeRequest) ProtoReflect() protoreflect.Message { +func (x *ConfigurationGetRequest) ProtoReflect() protoreflect.Message { mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -119,46 +197,717 @@ func (x *SettingsMergeRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SettingsMergeRequest.ProtoReflect.Descriptor instead. -func (*SettingsMergeRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use ConfigurationGetRequest.ProtoReflect.Descriptor instead. +func (*ConfigurationGetRequest) Descriptor() ([]byte, []int) { return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{1} } -func (x *SettingsMergeRequest) GetJsonData() string { +type ConfigurationGetResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The current configuration + Configuration *Configuration `protobuf:"bytes,1,opt,name=configuration,proto3" json:"configuration,omitempty"` +} + +func (x *ConfigurationGetResponse) Reset() { + *x = ConfigurationGetResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ConfigurationGetResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConfigurationGetResponse) ProtoMessage() {} + +func (x *ConfigurationGetResponse) ProtoReflect() protoreflect.Message { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConfigurationGetResponse.ProtoReflect.Descriptor instead. +func (*ConfigurationGetResponse) Descriptor() ([]byte, []int) { + return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{2} +} + +func (x *ConfigurationGetResponse) GetConfiguration() *Configuration { + if x != nil { + return x.Configuration + } + return nil +} + +type ConfigurationSaveRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The format of the encoded settings, allowed values are "json" and "yaml" + SettingsFormat string `protobuf:"bytes,1,opt,name=settings_format,json=settingsFormat,proto3" json:"settings_format,omitempty"` +} + +func (x *ConfigurationSaveRequest) Reset() { + *x = ConfigurationSaveRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ConfigurationSaveRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConfigurationSaveRequest) ProtoMessage() {} + +func (x *ConfigurationSaveRequest) ProtoReflect() protoreflect.Message { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConfigurationSaveRequest.ProtoReflect.Descriptor instead. +func (*ConfigurationSaveRequest) Descriptor() ([]byte, []int) { + return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{3} +} + +func (x *ConfigurationSaveRequest) GetSettingsFormat() string { if x != nil { - return x.JsonData + return x.SettingsFormat } return "" } -type SettingsGetValueResponse struct { +type ConfigurationSaveResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The encoded settings + EncodedSettings string `protobuf:"bytes,1,opt,name=encoded_settings,json=encodedSettings,proto3" json:"encoded_settings,omitempty"` +} + +func (x *ConfigurationSaveResponse) Reset() { + *x = ConfigurationSaveResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ConfigurationSaveResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConfigurationSaveResponse) ProtoMessage() {} + +func (x *ConfigurationSaveResponse) ProtoReflect() protoreflect.Message { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConfigurationSaveResponse.ProtoReflect.Descriptor instead. +func (*ConfigurationSaveResponse) Descriptor() ([]byte, []int) { + return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{4} +} + +func (x *ConfigurationSaveResponse) GetEncodedSettings() string { + if x != nil { + return x.EncodedSettings + } + return "" +} + +type ConfigurationOpenRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The encoded settings + EncodedSettings string `protobuf:"bytes,1,opt,name=encoded_settings,json=encodedSettings,proto3" json:"encoded_settings,omitempty"` + // The format of the encoded settings, allowed values are "json" and "yaml" + SettingsFormat string `protobuf:"bytes,2,opt,name=settings_format,json=settingsFormat,proto3" json:"settings_format,omitempty"` +} + +func (x *ConfigurationOpenRequest) Reset() { + *x = ConfigurationOpenRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ConfigurationOpenRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConfigurationOpenRequest) ProtoMessage() {} + +func (x *ConfigurationOpenRequest) ProtoReflect() protoreflect.Message { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConfigurationOpenRequest.ProtoReflect.Descriptor instead. +func (*ConfigurationOpenRequest) Descriptor() ([]byte, []int) { + return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{5} +} + +func (x *ConfigurationOpenRequest) GetEncodedSettings() string { + if x != nil { + return x.EncodedSettings + } + return "" +} + +func (x *ConfigurationOpenRequest) GetSettingsFormat() string { + if x != nil { + return x.SettingsFormat + } + return "" +} + +type ConfigurationOpenResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Warnings that occurred while opening the configuration (e.g. unknown keys, + // or invalid values) + Warnings []string `protobuf:"bytes,1,rep,name=warnings,proto3" json:"warnings,omitempty"` +} + +func (x *ConfigurationOpenResponse) Reset() { + *x = ConfigurationOpenResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ConfigurationOpenResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConfigurationOpenResponse) ProtoMessage() {} + +func (x *ConfigurationOpenResponse) ProtoReflect() protoreflect.Message { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConfigurationOpenResponse.ProtoReflect.Descriptor instead. +func (*ConfigurationOpenResponse) Descriptor() ([]byte, []int) { + return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{6} +} + +func (x *ConfigurationOpenResponse) GetWarnings() []string { + if x != nil { + return x.Warnings + } + return nil +} + +type SettingsGetValueRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // The key of the setting. + // The key to get Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` - // The setting, in JSON format. - JsonData string `protobuf:"bytes,2,opt,name=json_data,json=jsonData,proto3" json:"json_data,omitempty"` + // The format of the encoded_value (default is "json", allowed values are + // "json" and "yaml) + ValueFormat string `protobuf:"bytes,2,opt,name=value_format,json=valueFormat,proto3" json:"value_format,omitempty"` } -func (x *SettingsGetValueResponse) Reset() { - *x = SettingsGetValueResponse{} +func (x *SettingsGetValueRequest) Reset() { + *x = SettingsGetValueRequest{} if protoimpl.UnsafeEnabled { - mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[2] + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SettingsGetValueResponse) String() string { +func (x *SettingsGetValueRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SettingsGetValueResponse) ProtoMessage() {} +func (*SettingsGetValueRequest) ProtoMessage() {} + +func (x *SettingsGetValueRequest) ProtoReflect() protoreflect.Message { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SettingsGetValueRequest.ProtoReflect.Descriptor instead. +func (*SettingsGetValueRequest) Descriptor() ([]byte, []int) { + return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{7} +} + +func (x *SettingsGetValueRequest) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *SettingsGetValueRequest) GetValueFormat() string { + if x != nil { + return x.ValueFormat + } + return "" +} + +type SettingsGetValueResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The value of the key (encoded) + EncodedValue string `protobuf:"bytes,1,opt,name=encoded_value,json=encodedValue,proto3" json:"encoded_value,omitempty"` +} + +func (x *SettingsGetValueResponse) Reset() { + *x = SettingsGetValueResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SettingsGetValueResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SettingsGetValueResponse) ProtoMessage() {} + +func (x *SettingsGetValueResponse) ProtoReflect() protoreflect.Message { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SettingsGetValueResponse.ProtoReflect.Descriptor instead. +func (*SettingsGetValueResponse) Descriptor() ([]byte, []int) { + return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{8} +} + +func (x *SettingsGetValueResponse) GetEncodedValue() string { + if x != nil { + return x.EncodedValue + } + return "" +} + +type SettingsSetValueRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The key to change + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + // The new value (encoded), no objects, only scalar or array of scalars are + // allowed. + EncodedValue string `protobuf:"bytes,2,opt,name=encoded_value,json=encodedValue,proto3" json:"encoded_value,omitempty"` + // The format of the encoded_value (default is "json", allowed values are + // "json", "yaml" and "cli") + ValueFormat string `protobuf:"bytes,3,opt,name=value_format,json=valueFormat,proto3" json:"value_format,omitempty"` +} + +func (x *SettingsSetValueRequest) Reset() { + *x = SettingsSetValueRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SettingsSetValueRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SettingsSetValueRequest) ProtoMessage() {} + +func (x *SettingsSetValueRequest) ProtoReflect() protoreflect.Message { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SettingsSetValueRequest.ProtoReflect.Descriptor instead. +func (*SettingsSetValueRequest) Descriptor() ([]byte, []int) { + return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{9} +} + +func (x *SettingsSetValueRequest) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *SettingsSetValueRequest) GetEncodedValue() string { + if x != nil { + return x.EncodedValue + } + return "" +} + +func (x *SettingsSetValueRequest) GetValueFormat() string { + if x != nil { + return x.ValueFormat + } + return "" +} + +type SettingsSetValueResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *SettingsSetValueResponse) Reset() { + *x = SettingsSetValueResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SettingsSetValueResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SettingsSetValueResponse) ProtoMessage() {} + +func (x *SettingsSetValueResponse) ProtoReflect() protoreflect.Message { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SettingsSetValueResponse.ProtoReflect.Descriptor instead. +func (*SettingsSetValueResponse) Descriptor() ([]byte, []int) { + return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{10} +} + +type SettingsEnumerateRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *SettingsEnumerateRequest) Reset() { + *x = SettingsEnumerateRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SettingsEnumerateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SettingsEnumerateRequest) ProtoMessage() {} + +func (x *SettingsEnumerateRequest) ProtoReflect() protoreflect.Message { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SettingsEnumerateRequest.ProtoReflect.Descriptor instead. +func (*SettingsEnumerateRequest) Descriptor() ([]byte, []int) { + return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{11} +} + +type SettingsEnumerateResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The list of key/value pairs + Entries []*SettingsEnumerateResponse_Entry `protobuf:"bytes,1,rep,name=entries,proto3" json:"entries,omitempty"` +} + +func (x *SettingsEnumerateResponse) Reset() { + *x = SettingsEnumerateResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SettingsEnumerateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SettingsEnumerateResponse) ProtoMessage() {} + +func (x *SettingsEnumerateResponse) ProtoReflect() protoreflect.Message { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SettingsEnumerateResponse.ProtoReflect.Descriptor instead. +func (*SettingsEnumerateResponse) Descriptor() ([]byte, []int) { + return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{12} +} + +func (x *SettingsEnumerateResponse) GetEntries() []*SettingsEnumerateResponse_Entry { + if x != nil { + return x.Entries + } + return nil +} + +type Configuration_Directories struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Data directory + Data string `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` + // User directory + User string `protobuf:"bytes,2,opt,name=user,proto3" json:"user,omitempty"` + // Downloads directory + Downloads string `protobuf:"bytes,3,opt,name=downloads,proto3" json:"downloads,omitempty"` + // The directory where the built-in resources are installed + Builtin *Configuration_Directories_Builtin `protobuf:"bytes,4,opt,name=builtin,proto3,oneof" json:"builtin,omitempty"` +} + +func (x *Configuration_Directories) Reset() { + *x = Configuration_Directories{} + if protoimpl.UnsafeEnabled { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Configuration_Directories) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Configuration_Directories) ProtoMessage() {} + +func (x *Configuration_Directories) ProtoReflect() protoreflect.Message { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Configuration_Directories.ProtoReflect.Descriptor instead. +func (*Configuration_Directories) Descriptor() ([]byte, []int) { + return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{0, 0} +} + +func (x *Configuration_Directories) GetData() string { + if x != nil { + return x.Data + } + return "" +} + +func (x *Configuration_Directories) GetUser() string { + if x != nil { + return x.User + } + return "" +} + +func (x *Configuration_Directories) GetDownloads() string { + if x != nil { + return x.Downloads + } + return "" +} + +func (x *Configuration_Directories) GetBuiltin() *Configuration_Directories_Builtin { + if x != nil { + return x.Builtin + } + return nil +} + +type Configuration_Network struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Extra user-agent information to be appended in network requests + ExtraUserAgent *string `protobuf:"bytes,1,opt,name=extra_user_agent,json=extraUserAgent,proto3,oneof" json:"extra_user_agent,omitempty"` + // The proxy to use for network requests + Proxy *string `protobuf:"bytes,2,opt,name=proxy,proto3,oneof" json:"proxy,omitempty"` +} + +func (x *Configuration_Network) Reset() { + *x = Configuration_Network{} + if protoimpl.UnsafeEnabled { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Configuration_Network) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Configuration_Network) ProtoMessage() {} + +func (x *Configuration_Network) ProtoReflect() protoreflect.Message { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Configuration_Network.ProtoReflect.Descriptor instead. +func (*Configuration_Network) Descriptor() ([]byte, []int) { + return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{0, 1} +} + +func (x *Configuration_Network) GetExtraUserAgent() string { + if x != nil && x.ExtraUserAgent != nil { + return *x.ExtraUserAgent + } + return "" +} + +func (x *Configuration_Network) GetProxy() string { + if x != nil && x.Proxy != nil { + return *x.Proxy + } + return "" +} + +type Configuration_Sketch struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Set to true to always export binaries to the sketch directory + AlwaysExportBinaries bool `protobuf:"varint,1,opt,name=always_export_binaries,json=alwaysExportBinaries,proto3" json:"always_export_binaries,omitempty"` +} + +func (x *Configuration_Sketch) Reset() { + *x = Configuration_Sketch{} + if protoimpl.UnsafeEnabled { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Configuration_Sketch) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Configuration_Sketch) ProtoMessage() {} -func (x *SettingsGetValueResponse) ProtoReflect() protoreflect.Message { - mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[2] +func (x *Configuration_Sketch) ProtoReflect() protoreflect.Message { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -169,53 +918,46 @@ func (x *SettingsGetValueResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SettingsGetValueResponse.ProtoReflect.Descriptor instead. -func (*SettingsGetValueResponse) Descriptor() ([]byte, []int) { - return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{2} -} - -func (x *SettingsGetValueResponse) GetKey() string { - if x != nil { - return x.Key - } - return "" +// Deprecated: Use Configuration_Sketch.ProtoReflect.Descriptor instead. +func (*Configuration_Sketch) Descriptor() ([]byte, []int) { + return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{0, 2} } -func (x *SettingsGetValueResponse) GetJsonData() string { +func (x *Configuration_Sketch) GetAlwaysExportBinaries() bool { if x != nil { - return x.JsonData + return x.AlwaysExportBinaries } - return "" + return false } -type SettingsSetValueRequest struct { +type Configuration_BuildCache struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // The key of the setting. - Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` - // The setting, in JSON format. - JsonData string `protobuf:"bytes,2,opt,name=json_data,json=jsonData,proto3" json:"json_data,omitempty"` + // The minimum number of compilations before the cache is purged + CompilationsBeforePurge uint64 `protobuf:"varint,1,opt,name=compilations_before_purge,json=compilationsBeforePurge,proto3" json:"compilations_before_purge,omitempty"` + // Time to live of the cache in seconds + TtlSecs uint64 `protobuf:"varint,2,opt,name=ttl_secs,json=ttlSecs,proto3" json:"ttl_secs,omitempty"` } -func (x *SettingsSetValueRequest) Reset() { - *x = SettingsSetValueRequest{} +func (x *Configuration_BuildCache) Reset() { + *x = Configuration_BuildCache{} if protoimpl.UnsafeEnabled { - mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[3] + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SettingsSetValueRequest) String() string { +func (x *Configuration_BuildCache) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SettingsSetValueRequest) ProtoMessage() {} +func (*Configuration_BuildCache) ProtoMessage() {} -func (x *SettingsSetValueRequest) ProtoReflect() protoreflect.Message { - mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[3] +func (x *Configuration_BuildCache) ProtoReflect() protoreflect.Message { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -226,48 +968,51 @@ func (x *SettingsSetValueRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SettingsSetValueRequest.ProtoReflect.Descriptor instead. -func (*SettingsSetValueRequest) Descriptor() ([]byte, []int) { - return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{3} +// Deprecated: Use Configuration_BuildCache.ProtoReflect.Descriptor instead. +func (*Configuration_BuildCache) Descriptor() ([]byte, []int) { + return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{0, 3} } -func (x *SettingsSetValueRequest) GetKey() string { +func (x *Configuration_BuildCache) GetCompilationsBeforePurge() uint64 { if x != nil { - return x.Key + return x.CompilationsBeforePurge } - return "" + return 0 } -func (x *SettingsSetValueRequest) GetJsonData() string { +func (x *Configuration_BuildCache) GetTtlSecs() uint64 { if x != nil { - return x.JsonData + return x.TtlSecs } - return "" + return 0 } -type SettingsGetAllRequest struct { +type Configuration_BoardManager struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + + // Additional URLs to be used for the board manager + AdditionalUrls []string `protobuf:"bytes,1,rep,name=additional_urls,json=additionalUrls,proto3" json:"additional_urls,omitempty"` } -func (x *SettingsGetAllRequest) Reset() { - *x = SettingsGetAllRequest{} +func (x *Configuration_BoardManager) Reset() { + *x = Configuration_BoardManager{} if protoimpl.UnsafeEnabled { - mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[4] + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SettingsGetAllRequest) String() string { +func (x *Configuration_BoardManager) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SettingsGetAllRequest) ProtoMessage() {} +func (*Configuration_BoardManager) ProtoMessage() {} -func (x *SettingsGetAllRequest) ProtoReflect() protoreflect.Message { - mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[4] +func (x *Configuration_BoardManager) ProtoReflect() protoreflect.Message { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -278,37 +1023,44 @@ func (x *SettingsGetAllRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SettingsGetAllRequest.ProtoReflect.Descriptor instead. -func (*SettingsGetAllRequest) Descriptor() ([]byte, []int) { - return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{4} +// Deprecated: Use Configuration_BoardManager.ProtoReflect.Descriptor instead. +func (*Configuration_BoardManager) Descriptor() ([]byte, []int) { + return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{0, 4} } -type SettingsGetValueRequest struct { +func (x *Configuration_BoardManager) GetAdditionalUrls() []string { + if x != nil { + return x.AdditionalUrls + } + return nil +} + +type Configuration_Daemon struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // The key of the setting. - Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + // The TCP port of the daemon + Port string `protobuf:"bytes,1,opt,name=port,proto3" json:"port,omitempty"` } -func (x *SettingsGetValueRequest) Reset() { - *x = SettingsGetValueRequest{} +func (x *Configuration_Daemon) Reset() { + *x = Configuration_Daemon{} if protoimpl.UnsafeEnabled { - mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[5] + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SettingsGetValueRequest) String() string { +func (x *Configuration_Daemon) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SettingsGetValueRequest) ProtoMessage() {} +func (*Configuration_Daemon) ProtoMessage() {} -func (x *SettingsGetValueRequest) ProtoReflect() protoreflect.Message { - mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[5] +func (x *Configuration_Daemon) ProtoReflect() protoreflect.Message { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -319,41 +1071,44 @@ func (x *SettingsGetValueRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SettingsGetValueRequest.ProtoReflect.Descriptor instead. -func (*SettingsGetValueRequest) Descriptor() ([]byte, []int) { - return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{5} +// Deprecated: Use Configuration_Daemon.ProtoReflect.Descriptor instead. +func (*Configuration_Daemon) Descriptor() ([]byte, []int) { + return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{0, 5} } -func (x *SettingsGetValueRequest) GetKey() string { +func (x *Configuration_Daemon) GetPort() string { if x != nil { - return x.Key + return x.Port } return "" } -type SettingsMergeResponse struct { +type Configuration_Output struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + + // Set to true to disable coloring of the output + NoColor bool `protobuf:"varint,1,opt,name=no_color,json=noColor,proto3" json:"no_color,omitempty"` } -func (x *SettingsMergeResponse) Reset() { - *x = SettingsMergeResponse{} +func (x *Configuration_Output) Reset() { + *x = Configuration_Output{} if protoimpl.UnsafeEnabled { - mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[6] + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SettingsMergeResponse) String() string { +func (x *Configuration_Output) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SettingsMergeResponse) ProtoMessage() {} +func (*Configuration_Output) ProtoMessage() {} -func (x *SettingsMergeResponse) ProtoReflect() protoreflect.Message { - mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[6] +func (x *Configuration_Output) ProtoReflect() protoreflect.Message { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -364,34 +1119,48 @@ func (x *SettingsMergeResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SettingsMergeResponse.ProtoReflect.Descriptor instead. -func (*SettingsMergeResponse) Descriptor() ([]byte, []int) { - return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{6} +// Deprecated: Use Configuration_Output.ProtoReflect.Descriptor instead. +func (*Configuration_Output) Descriptor() ([]byte, []int) { + return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{0, 6} } -type SettingsSetValueResponse struct { +func (x *Configuration_Output) GetNoColor() bool { + if x != nil { + return x.NoColor + } + return false +} + +type Configuration_Logging struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + + // The logging level + Level string `protobuf:"bytes,1,opt,name=level,proto3" json:"level,omitempty"` + // The logging format + Format string `protobuf:"bytes,2,opt,name=format,proto3" json:"format,omitempty"` + // The logging file + File *string `protobuf:"bytes,3,opt,name=file,proto3,oneof" json:"file,omitempty"` } -func (x *SettingsSetValueResponse) Reset() { - *x = SettingsSetValueResponse{} +func (x *Configuration_Logging) Reset() { + *x = Configuration_Logging{} if protoimpl.UnsafeEnabled { - mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[7] + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SettingsSetValueResponse) String() string { +func (x *Configuration_Logging) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SettingsSetValueResponse) ProtoMessage() {} +func (*Configuration_Logging) ProtoMessage() {} -func (x *SettingsSetValueResponse) ProtoReflect() protoreflect.Message { - mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[7] +func (x *Configuration_Logging) ProtoReflect() protoreflect.Message { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -402,37 +1171,59 @@ func (x *SettingsSetValueResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SettingsSetValueResponse.ProtoReflect.Descriptor instead. -func (*SettingsSetValueResponse) Descriptor() ([]byte, []int) { - return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{7} +// Deprecated: Use Configuration_Logging.ProtoReflect.Descriptor instead. +func (*Configuration_Logging) Descriptor() ([]byte, []int) { + return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{0, 7} +} + +func (x *Configuration_Logging) GetLevel() string { + if x != nil { + return x.Level + } + return "" +} + +func (x *Configuration_Logging) GetFormat() string { + if x != nil { + return x.Format + } + return "" } -type SettingsWriteRequest struct { +func (x *Configuration_Logging) GetFile() string { + if x != nil && x.File != nil { + return *x.File + } + return "" +} + +type Configuration_Library struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Path to settings file (e.g. /path/to/arduino-cli.yaml) - FilePath string `protobuf:"bytes,1,opt,name=file_path,json=filePath,proto3" json:"file_path,omitempty"` + // Set to true to enable library installation from zip archives or git + // repositories + EnableUnsafeInstall bool `protobuf:"varint,1,opt,name=enable_unsafe_install,json=enableUnsafeInstall,proto3" json:"enable_unsafe_install,omitempty"` } -func (x *SettingsWriteRequest) Reset() { - *x = SettingsWriteRequest{} +func (x *Configuration_Library) Reset() { + *x = Configuration_Library{} if protoimpl.UnsafeEnabled { - mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[8] + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SettingsWriteRequest) String() string { +func (x *Configuration_Library) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SettingsWriteRequest) ProtoMessage() {} +func (*Configuration_Library) ProtoMessage() {} -func (x *SettingsWriteRequest) ProtoReflect() protoreflect.Message { - mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[8] +func (x *Configuration_Library) ProtoReflect() protoreflect.Message { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -443,41 +1234,44 @@ func (x *SettingsWriteRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SettingsWriteRequest.ProtoReflect.Descriptor instead. -func (*SettingsWriteRequest) Descriptor() ([]byte, []int) { - return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{8} +// Deprecated: Use Configuration_Library.ProtoReflect.Descriptor instead. +func (*Configuration_Library) Descriptor() ([]byte, []int) { + return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{0, 8} } -func (x *SettingsWriteRequest) GetFilePath() string { +func (x *Configuration_Library) GetEnableUnsafeInstall() bool { if x != nil { - return x.FilePath + return x.EnableUnsafeInstall } - return "" + return false } -type SettingsWriteResponse struct { +type Configuration_Updater struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + + // Set to true to enable notifications for updates + EnableNotification bool `protobuf:"varint,1,opt,name=enable_notification,json=enableNotification,proto3" json:"enable_notification,omitempty"` } -func (x *SettingsWriteResponse) Reset() { - *x = SettingsWriteResponse{} +func (x *Configuration_Updater) Reset() { + *x = Configuration_Updater{} if protoimpl.UnsafeEnabled { - mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[9] + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SettingsWriteResponse) String() string { +func (x *Configuration_Updater) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SettingsWriteResponse) ProtoMessage() {} +func (*Configuration_Updater) ProtoMessage() {} -func (x *SettingsWriteResponse) ProtoReflect() protoreflect.Message { - mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[9] +func (x *Configuration_Updater) ProtoReflect() protoreflect.Message { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -488,37 +1282,44 @@ func (x *SettingsWriteResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SettingsWriteResponse.ProtoReflect.Descriptor instead. -func (*SettingsWriteResponse) Descriptor() ([]byte, []int) { - return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{9} +// Deprecated: Use Configuration_Updater.ProtoReflect.Descriptor instead. +func (*Configuration_Updater) Descriptor() ([]byte, []int) { + return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{0, 9} +} + +func (x *Configuration_Updater) GetEnableNotification() bool { + if x != nil { + return x.EnableNotification + } + return false } -type SettingsDeleteRequest struct { +type Configuration_Directories_Builtin struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // The key of the setting to delete. - Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + // The directory where the built-in libraries are installed + Libraries *string `protobuf:"bytes,1,opt,name=libraries,proto3,oneof" json:"libraries,omitempty"` } -func (x *SettingsDeleteRequest) Reset() { - *x = SettingsDeleteRequest{} +func (x *Configuration_Directories_Builtin) Reset() { + *x = Configuration_Directories_Builtin{} if protoimpl.UnsafeEnabled { - mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[10] + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SettingsDeleteRequest) String() string { +func (x *Configuration_Directories_Builtin) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SettingsDeleteRequest) ProtoMessage() {} +func (*Configuration_Directories_Builtin) ProtoMessage() {} -func (x *SettingsDeleteRequest) ProtoReflect() protoreflect.Message { - mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[10] +func (x *Configuration_Directories_Builtin) ProtoReflect() protoreflect.Message { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -529,41 +1330,46 @@ func (x *SettingsDeleteRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SettingsDeleteRequest.ProtoReflect.Descriptor instead. -func (*SettingsDeleteRequest) Descriptor() ([]byte, []int) { - return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{10} +// Deprecated: Use Configuration_Directories_Builtin.ProtoReflect.Descriptor instead. +func (*Configuration_Directories_Builtin) Descriptor() ([]byte, []int) { + return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{0, 0, 0} } -func (x *SettingsDeleteRequest) GetKey() string { - if x != nil { - return x.Key +func (x *Configuration_Directories_Builtin) GetLibraries() string { + if x != nil && x.Libraries != nil { + return *x.Libraries } return "" } -type SettingsDeleteResponse struct { +type SettingsEnumerateResponse_Entry struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + + // The key + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + // The key type + Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` } -func (x *SettingsDeleteResponse) Reset() { - *x = SettingsDeleteResponse{} +func (x *SettingsEnumerateResponse_Entry) Reset() { + *x = SettingsEnumerateResponse_Entry{} if protoimpl.UnsafeEnabled { - mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[11] + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *SettingsDeleteResponse) String() string { +func (x *SettingsEnumerateResponse_Entry) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SettingsDeleteResponse) ProtoMessage() {} +func (*SettingsEnumerateResponse_Entry) ProtoMessage() {} -func (x *SettingsDeleteResponse) ProtoReflect() protoreflect.Message { - mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[11] +func (x *SettingsEnumerateResponse_Entry) ProtoReflect() protoreflect.Message { + mi := &file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -574,9 +1380,23 @@ func (x *SettingsDeleteResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SettingsDeleteResponse.ProtoReflect.Descriptor instead. -func (*SettingsDeleteResponse) Descriptor() ([]byte, []int) { - return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{11} +// Deprecated: Use SettingsEnumerateResponse_Entry.ProtoReflect.Descriptor instead. +func (*SettingsEnumerateResponse_Entry) Descriptor() ([]byte, []int) { + return file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP(), []int{12, 0} +} + +func (x *SettingsEnumerateResponse_Entry) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *SettingsEnumerateResponse_Entry) GetType() string { + if x != nil { + return x.Type + } + return "" } var File_cc_arduino_cli_commands_v1_settings_proto protoreflect.FileDescriptor @@ -586,45 +1406,178 @@ var file_cc_arduino_cli_commands_v1_settings_proto_rawDesc = []byte{ 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1a, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, - 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x22, 0x35, 0x0a, 0x16, 0x53, 0x65, 0x74, 0x74, 0x69, - 0x6e, 0x67, 0x73, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6a, 0x73, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x22, 0x33, - 0x0a, 0x14, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x64, - 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6a, 0x73, 0x6f, 0x6e, 0x44, - 0x61, 0x74, 0x61, 0x22, 0x49, 0x0a, 0x18, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x47, - 0x65, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6a, 0x73, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x22, 0x48, - 0x0a, 0x17, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x53, 0x65, 0x74, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x6a, - 0x73, 0x6f, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x6a, 0x73, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x22, 0x17, 0x0a, 0x15, 0x53, 0x65, 0x74, 0x74, - 0x69, 0x6e, 0x67, 0x73, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x22, 0x2b, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x47, 0x65, 0x74, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x17, - 0x0a, 0x15, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x52, + 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x22, 0xbd, 0x0d, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x57, 0x0a, 0x0b, 0x64, 0x69, 0x72, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, + 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, + 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x69, 0x65, 0x73, 0x52, 0x0b, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x69, + 0x65, 0x73, 0x12, 0x4b, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, + 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4e, + 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, + 0x48, 0x0a, 0x06, 0x73, 0x6b, 0x65, 0x74, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x30, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, + 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x6b, 0x65, 0x74, 0x63, + 0x68, 0x52, 0x06, 0x73, 0x6b, 0x65, 0x74, 0x63, 0x68, 0x12, 0x55, 0x0a, 0x0b, 0x62, 0x75, 0x69, + 0x6c, 0x64, 0x5f, 0x63, 0x61, 0x63, 0x68, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, + 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, + 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, + 0x61, 0x63, 0x68, 0x65, 0x52, 0x0a, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x61, 0x63, 0x68, 0x65, + 0x12, 0x5b, 0x0a, 0x0d, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, + 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x52, + 0x0c, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x12, 0x48, 0x0a, + 0x06, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, + 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, + 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x52, + 0x06, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x12, 0x48, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, + 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, + 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, + 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, + 0x74, 0x12, 0x4b, 0x0a, 0x07, 0x6c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, + 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, + 0x67, 0x67, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x12, 0x4b, + 0x0a, 0x07, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x31, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, + 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x62, 0x72, 0x61, + 0x72, 0x79, 0x52, 0x07, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x12, 0x4b, 0x0a, 0x07, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x63, + 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, + 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x72, 0x52, + 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x06, 0x6c, 0x6f, 0x63, 0x61, + 0x6c, 0x65, 0x18, 0x64, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x6c, 0x6f, 0x63, 0x61, + 0x6c, 0x65, 0x88, 0x01, 0x01, 0x1a, 0xf9, 0x01, 0x0a, 0x0b, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x69, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, + 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x1c, 0x0a, + 0x09, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x12, 0x5c, 0x0a, 0x07, 0x62, + 0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x63, + 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, + 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x69, 0x65, 0x73, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e, 0x48, 0x00, 0x52, 0x07, 0x62, + 0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e, 0x88, 0x01, 0x01, 0x1a, 0x3a, 0x0a, 0x07, 0x42, 0x75, 0x69, + 0x6c, 0x74, 0x69, 0x6e, 0x12, 0x21, 0x0a, 0x09, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x09, 0x6c, 0x69, 0x62, 0x72, 0x61, + 0x72, 0x69, 0x65, 0x73, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6c, 0x69, 0x62, 0x72, + 0x61, 0x72, 0x69, 0x65, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x74, 0x69, + 0x6e, 0x1a, 0x72, 0x0a, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x2d, 0x0a, 0x10, + 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0e, 0x65, 0x78, 0x74, 0x72, 0x61, 0x55, + 0x73, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x70, + 0x72, 0x6f, 0x78, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x05, 0x70, 0x72, + 0x6f, 0x78, 0x79, 0x88, 0x01, 0x01, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x65, 0x78, 0x74, 0x72, 0x61, + 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x42, 0x08, 0x0a, 0x06, 0x5f, + 0x70, 0x72, 0x6f, 0x78, 0x79, 0x1a, 0x3e, 0x0a, 0x06, 0x53, 0x6b, 0x65, 0x74, 0x63, 0x68, 0x12, + 0x34, 0x0a, 0x16, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, + 0x5f, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x14, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x42, 0x69, 0x6e, + 0x61, 0x72, 0x69, 0x65, 0x73, 0x1a, 0x63, 0x0a, 0x0a, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x61, + 0x63, 0x68, 0x65, 0x12, 0x3a, 0x0a, 0x19, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x5f, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x5f, 0x70, 0x75, 0x72, 0x67, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x17, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x50, 0x75, 0x72, 0x67, 0x65, 0x12, + 0x19, 0x0a, 0x08, 0x74, 0x74, 0x6c, 0x5f, 0x73, 0x65, 0x63, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x07, 0x74, 0x74, 0x6c, 0x53, 0x65, 0x63, 0x73, 0x1a, 0x37, 0x0a, 0x0c, 0x42, 0x6f, + 0x61, 0x72, 0x64, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x64, + 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x75, 0x72, 0x6c, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x55, + 0x72, 0x6c, 0x73, 0x1a, 0x1c, 0x0a, 0x06, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x12, 0x12, 0x0a, + 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x6f, 0x72, + 0x74, 0x1a, 0x23, 0x0a, 0x06, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x6e, + 0x6f, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x6e, + 0x6f, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x1a, 0x59, 0x0a, 0x07, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, + 0x67, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, + 0x17, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, + 0x04, 0x66, 0x69, 0x6c, 0x65, 0x88, 0x01, 0x01, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x66, 0x69, 0x6c, + 0x65, 0x1a, 0x3d, 0x0a, 0x07, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x12, 0x32, 0x0a, 0x15, + 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x75, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x5f, 0x69, 0x6e, + 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x65, 0x6e, 0x61, + 0x62, 0x6c, 0x65, 0x55, 0x6e, 0x73, 0x61, 0x66, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, + 0x1a, 0x3a, 0x0a, 0x07, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x72, 0x12, 0x2f, 0x0a, 0x13, 0x65, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x09, 0x0a, 0x07, + 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x22, 0x19, 0x0a, 0x17, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x22, 0x6b, 0x0a, 0x18, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, + 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, + 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, + 0x43, 0x0a, 0x18, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x53, 0x61, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x73, + 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x46, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x22, 0x46, 0x0a, 0x19, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x61, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x29, 0x0a, 0x10, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x74, + 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x65, 0x6e, 0x63, + 0x6f, 0x64, 0x65, 0x64, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x6e, 0x0a, 0x18, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x65, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x65, 0x6e, 0x63, 0x6f, + 0x64, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0f, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x53, 0x65, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x5f, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x65, + 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x22, 0x37, 0x0a, 0x19, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x65, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x61, 0x72, + 0x6e, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x77, 0x61, 0x72, + 0x6e, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x4e, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, + 0x73, 0x47, 0x65, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x46, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x22, 0x3f, 0x0a, 0x18, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, + 0x73, 0x47, 0x65, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x5f, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, + 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x73, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x53, 0x65, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x5f, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x6e, 0x63, 0x6f, + 0x64, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x22, 0x1a, 0x0a, 0x18, 0x53, + 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x53, 0x65, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x0a, 0x18, 0x53, 0x65, 0x74, 0x74, 0x69, - 0x6e, 0x67, 0x73, 0x53, 0x65, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x33, 0x0a, 0x14, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x57, - 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x66, - 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x22, 0x17, 0x0a, 0x15, 0x53, 0x65, 0x74, 0x74, - 0x69, 0x6e, 0x67, 0x73, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x29, 0x0a, 0x15, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x18, 0x0a, 0x16, - 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x48, 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2f, 0x61, 0x72, 0x64, - 0x75, 0x69, 0x6e, 0x6f, 0x2d, 0x63, 0x6c, 0x69, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x63, 0x2f, - 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2f, 0x63, 0x6c, 0x69, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, - 0x61, 0x6e, 0x64, 0x73, 0x2f, 0x76, 0x31, 0x3b, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6e, 0x67, 0x73, 0x45, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x22, 0xa1, 0x01, 0x0a, 0x19, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, + 0x45, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x55, 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, + 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x45, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x61, 0x74, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x1a, 0x2d, 0x0a, 0x05, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x42, 0x48, 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2f, 0x61, 0x72, + 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2d, 0x63, 0x6c, 0x69, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x63, + 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2f, 0x63, 0x6c, 0x69, 0x2f, 0x63, 0x6f, 0x6d, + 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2f, 0x76, 0x31, 0x3b, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, + 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -639,27 +1592,53 @@ func file_cc_arduino_cli_commands_v1_settings_proto_rawDescGZIP() []byte { return file_cc_arduino_cli_commands_v1_settings_proto_rawDescData } -var file_cc_arduino_cli_commands_v1_settings_proto_msgTypes = make([]protoimpl.MessageInfo, 12) +var file_cc_arduino_cli_commands_v1_settings_proto_msgTypes = make([]protoimpl.MessageInfo, 25) var file_cc_arduino_cli_commands_v1_settings_proto_goTypes = []interface{}{ - (*SettingsGetAllResponse)(nil), // 0: cc.arduino.cli.commands.v1.SettingsGetAllResponse - (*SettingsMergeRequest)(nil), // 1: cc.arduino.cli.commands.v1.SettingsMergeRequest - (*SettingsGetValueResponse)(nil), // 2: cc.arduino.cli.commands.v1.SettingsGetValueResponse - (*SettingsSetValueRequest)(nil), // 3: cc.arduino.cli.commands.v1.SettingsSetValueRequest - (*SettingsGetAllRequest)(nil), // 4: cc.arduino.cli.commands.v1.SettingsGetAllRequest - (*SettingsGetValueRequest)(nil), // 5: cc.arduino.cli.commands.v1.SettingsGetValueRequest - (*SettingsMergeResponse)(nil), // 6: cc.arduino.cli.commands.v1.SettingsMergeResponse - (*SettingsSetValueResponse)(nil), // 7: cc.arduino.cli.commands.v1.SettingsSetValueResponse - (*SettingsWriteRequest)(nil), // 8: cc.arduino.cli.commands.v1.SettingsWriteRequest - (*SettingsWriteResponse)(nil), // 9: cc.arduino.cli.commands.v1.SettingsWriteResponse - (*SettingsDeleteRequest)(nil), // 10: cc.arduino.cli.commands.v1.SettingsDeleteRequest - (*SettingsDeleteResponse)(nil), // 11: cc.arduino.cli.commands.v1.SettingsDeleteResponse + (*Configuration)(nil), // 0: cc.arduino.cli.commands.v1.Configuration + (*ConfigurationGetRequest)(nil), // 1: cc.arduino.cli.commands.v1.ConfigurationGetRequest + (*ConfigurationGetResponse)(nil), // 2: cc.arduino.cli.commands.v1.ConfigurationGetResponse + (*ConfigurationSaveRequest)(nil), // 3: cc.arduino.cli.commands.v1.ConfigurationSaveRequest + (*ConfigurationSaveResponse)(nil), // 4: cc.arduino.cli.commands.v1.ConfigurationSaveResponse + (*ConfigurationOpenRequest)(nil), // 5: cc.arduino.cli.commands.v1.ConfigurationOpenRequest + (*ConfigurationOpenResponse)(nil), // 6: cc.arduino.cli.commands.v1.ConfigurationOpenResponse + (*SettingsGetValueRequest)(nil), // 7: cc.arduino.cli.commands.v1.SettingsGetValueRequest + (*SettingsGetValueResponse)(nil), // 8: cc.arduino.cli.commands.v1.SettingsGetValueResponse + (*SettingsSetValueRequest)(nil), // 9: cc.arduino.cli.commands.v1.SettingsSetValueRequest + (*SettingsSetValueResponse)(nil), // 10: cc.arduino.cli.commands.v1.SettingsSetValueResponse + (*SettingsEnumerateRequest)(nil), // 11: cc.arduino.cli.commands.v1.SettingsEnumerateRequest + (*SettingsEnumerateResponse)(nil), // 12: cc.arduino.cli.commands.v1.SettingsEnumerateResponse + (*Configuration_Directories)(nil), // 13: cc.arduino.cli.commands.v1.Configuration.Directories + (*Configuration_Network)(nil), // 14: cc.arduino.cli.commands.v1.Configuration.Network + (*Configuration_Sketch)(nil), // 15: cc.arduino.cli.commands.v1.Configuration.Sketch + (*Configuration_BuildCache)(nil), // 16: cc.arduino.cli.commands.v1.Configuration.BuildCache + (*Configuration_BoardManager)(nil), // 17: cc.arduino.cli.commands.v1.Configuration.BoardManager + (*Configuration_Daemon)(nil), // 18: cc.arduino.cli.commands.v1.Configuration.Daemon + (*Configuration_Output)(nil), // 19: cc.arduino.cli.commands.v1.Configuration.Output + (*Configuration_Logging)(nil), // 20: cc.arduino.cli.commands.v1.Configuration.Logging + (*Configuration_Library)(nil), // 21: cc.arduino.cli.commands.v1.Configuration.Library + (*Configuration_Updater)(nil), // 22: cc.arduino.cli.commands.v1.Configuration.Updater + (*Configuration_Directories_Builtin)(nil), // 23: cc.arduino.cli.commands.v1.Configuration.Directories.Builtin + (*SettingsEnumerateResponse_Entry)(nil), // 24: cc.arduino.cli.commands.v1.SettingsEnumerateResponse.Entry } var file_cc_arduino_cli_commands_v1_settings_proto_depIdxs = []int32{ - 0, // [0:0] is the sub-list for method output_type - 0, // [0:0] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name + 13, // 0: cc.arduino.cli.commands.v1.Configuration.directories:type_name -> cc.arduino.cli.commands.v1.Configuration.Directories + 14, // 1: cc.arduino.cli.commands.v1.Configuration.network:type_name -> cc.arduino.cli.commands.v1.Configuration.Network + 15, // 2: cc.arduino.cli.commands.v1.Configuration.sketch:type_name -> cc.arduino.cli.commands.v1.Configuration.Sketch + 16, // 3: cc.arduino.cli.commands.v1.Configuration.build_cache:type_name -> cc.arduino.cli.commands.v1.Configuration.BuildCache + 17, // 4: cc.arduino.cli.commands.v1.Configuration.board_manager:type_name -> cc.arduino.cli.commands.v1.Configuration.BoardManager + 18, // 5: cc.arduino.cli.commands.v1.Configuration.daemon:type_name -> cc.arduino.cli.commands.v1.Configuration.Daemon + 19, // 6: cc.arduino.cli.commands.v1.Configuration.output:type_name -> cc.arduino.cli.commands.v1.Configuration.Output + 20, // 7: cc.arduino.cli.commands.v1.Configuration.logging:type_name -> cc.arduino.cli.commands.v1.Configuration.Logging + 21, // 8: cc.arduino.cli.commands.v1.Configuration.library:type_name -> cc.arduino.cli.commands.v1.Configuration.Library + 22, // 9: cc.arduino.cli.commands.v1.Configuration.updater:type_name -> cc.arduino.cli.commands.v1.Configuration.Updater + 0, // 10: cc.arduino.cli.commands.v1.ConfigurationGetResponse.configuration:type_name -> cc.arduino.cli.commands.v1.Configuration + 24, // 11: cc.arduino.cli.commands.v1.SettingsEnumerateResponse.entries:type_name -> cc.arduino.cli.commands.v1.SettingsEnumerateResponse.Entry + 23, // 12: cc.arduino.cli.commands.v1.Configuration.Directories.builtin:type_name -> cc.arduino.cli.commands.v1.Configuration.Directories.Builtin + 13, // [13:13] is the sub-list for method output_type + 13, // [13:13] is the sub-list for method input_type + 13, // [13:13] is the sub-list for extension type_name + 13, // [13:13] is the sub-list for extension extendee + 0, // [0:13] is the sub-list for field type_name } func init() { file_cc_arduino_cli_commands_v1_settings_proto_init() } @@ -669,7 +1648,7 @@ func file_cc_arduino_cli_commands_v1_settings_proto_init() { } if !protoimpl.UnsafeEnabled { file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SettingsGetAllResponse); i { + switch v := v.(*Configuration); i { case 0: return &v.state case 1: @@ -681,7 +1660,7 @@ func file_cc_arduino_cli_commands_v1_settings_proto_init() { } } file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SettingsMergeRequest); i { + switch v := v.(*ConfigurationGetRequest); i { case 0: return &v.state case 1: @@ -693,7 +1672,7 @@ func file_cc_arduino_cli_commands_v1_settings_proto_init() { } } file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SettingsGetValueResponse); i { + switch v := v.(*ConfigurationGetResponse); i { case 0: return &v.state case 1: @@ -705,7 +1684,7 @@ func file_cc_arduino_cli_commands_v1_settings_proto_init() { } } file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SettingsSetValueRequest); i { + switch v := v.(*ConfigurationSaveRequest); i { case 0: return &v.state case 1: @@ -717,7 +1696,7 @@ func file_cc_arduino_cli_commands_v1_settings_proto_init() { } } file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SettingsGetAllRequest); i { + switch v := v.(*ConfigurationSaveResponse); i { case 0: return &v.state case 1: @@ -729,7 +1708,7 @@ func file_cc_arduino_cli_commands_v1_settings_proto_init() { } } file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SettingsGetValueRequest); i { + switch v := v.(*ConfigurationOpenRequest); i { case 0: return &v.state case 1: @@ -741,7 +1720,7 @@ func file_cc_arduino_cli_commands_v1_settings_proto_init() { } } file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SettingsMergeResponse); i { + switch v := v.(*ConfigurationOpenResponse); i { case 0: return &v.state case 1: @@ -753,7 +1732,7 @@ func file_cc_arduino_cli_commands_v1_settings_proto_init() { } } file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SettingsSetValueResponse); i { + switch v := v.(*SettingsGetValueRequest); i { case 0: return &v.state case 1: @@ -765,7 +1744,7 @@ func file_cc_arduino_cli_commands_v1_settings_proto_init() { } } file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SettingsWriteRequest); i { + switch v := v.(*SettingsGetValueResponse); i { case 0: return &v.state case 1: @@ -777,7 +1756,7 @@ func file_cc_arduino_cli_commands_v1_settings_proto_init() { } } file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SettingsWriteResponse); i { + switch v := v.(*SettingsSetValueRequest); i { case 0: return &v.state case 1: @@ -789,7 +1768,7 @@ func file_cc_arduino_cli_commands_v1_settings_proto_init() { } } file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SettingsDeleteRequest); i { + switch v := v.(*SettingsSetValueResponse); i { case 0: return &v.state case 1: @@ -801,7 +1780,163 @@ func file_cc_arduino_cli_commands_v1_settings_proto_init() { } } file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SettingsDeleteResponse); i { + switch v := v.(*SettingsEnumerateRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SettingsEnumerateResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Configuration_Directories); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Configuration_Network); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Configuration_Sketch); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Configuration_BuildCache); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Configuration_BoardManager); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Configuration_Daemon); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Configuration_Output); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Configuration_Logging); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Configuration_Library); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Configuration_Updater); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Configuration_Directories_Builtin); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SettingsEnumerateResponse_Entry); i { case 0: return &v.state case 1: @@ -813,13 +1948,18 @@ func file_cc_arduino_cli_commands_v1_settings_proto_init() { } } } + file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[0].OneofWrappers = []interface{}{} + file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[13].OneofWrappers = []interface{}{} + file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[14].OneofWrappers = []interface{}{} + file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[20].OneofWrappers = []interface{}{} + file_cc_arduino_cli_commands_v1_settings_proto_msgTypes[23].OneofWrappers = []interface{}{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_cc_arduino_cli_commands_v1_settings_proto_rawDesc, NumEnums: 0, - NumMessages: 12, + NumMessages: 25, NumExtensions: 0, NumServices: 0, }, diff --git a/rpc/cc/arduino/cli/commands/v1/settings.proto b/rpc/cc/arduino/cli/commands/v1/settings.proto index 05a6ea16a22..c55a2c122f3 100644 --- a/rpc/cc/arduino/cli/commands/v1/settings.proto +++ b/rpc/cc/arduino/cli/commands/v1/settings.proto @@ -19,51 +19,149 @@ package cc.arduino.cli.commands.v1; option go_package = "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1;commands"; -message SettingsGetAllResponse { - // The settings, in JSON format. - string json_data = 1; +// Configuration to apply to the given instance. +// Any missing field will be kept at the default value. +message Configuration { + message Directories { + message Builtin { + // The directory where the built-in libraries are installed + optional string libraries = 1; + } + // Data directory + string data = 1; + // User directory + string user = 2; + // Downloads directory + string downloads = 3; + // The directory where the built-in resources are installed + optional Builtin builtin = 4; + }; + message Network { + // Extra user-agent information to be appended in network requests + optional string extra_user_agent = 1; + // The proxy to use for network requests + optional string proxy = 2; + }; + message Sketch { + // Set to true to always export binaries to the sketch directory + bool always_export_binaries = 1; + } + message BuildCache { + // The minimum number of compilations before the cache is purged + uint64 compilations_before_purge = 1; + // Time to live of the cache in seconds + uint64 ttl_secs = 2; + } + message BoardManager { + // Additional URLs to be used for the board manager + repeated string additional_urls = 1; + } + message Daemon { + // The TCP port of the daemon + string port = 1; + } + message Output { + // Set to true to disable coloring of the output + bool no_color = 1; + } + message Logging { + // The logging level + string level = 1; + // The logging format + string format = 2; + // The logging file + optional string file = 3; + } + message Library { + // Set to true to enable library installation from zip archives or git + // repositories + bool enable_unsafe_install = 1; + } + message Updater { + // Set to true to enable notifications for updates + bool enable_notification = 1; + } + + Directories directories = 1; + Network network = 2; + Sketch sketch = 3; + BuildCache build_cache = 4; + BoardManager board_manager = 5; + Daemon daemon = 6; + Output output = 7; + Logging logging = 8; + Library library = 9; + Updater updater = 10; + + optional string locale = 100; } -message SettingsMergeRequest { - // The settings, in JSON format. - string json_data = 1; +message ConfigurationGetRequest {} + +message ConfigurationGetResponse { + // The current configuration + Configuration configuration = 1; } -message SettingsGetValueResponse { - // The key of the setting. - string key = 1; - // The setting, in JSON format. - string json_data = 2; +message ConfigurationSaveRequest { + // The format of the encoded settings, allowed values are "json" and "yaml" + string settings_format = 1; } -message SettingsSetValueRequest { - // The key of the setting. - string key = 1; - // The setting, in JSON format. - string json_data = 2; +message ConfigurationSaveResponse { + // The encoded settings + string encoded_settings = 1; } -message SettingsGetAllRequest {} +message ConfigurationOpenRequest { + // The encoded settings + string encoded_settings = 1; + // The format of the encoded settings, allowed values are "json" and "yaml" + string settings_format = 2; +} + +message ConfigurationOpenResponse { + // Warnings that occurred while opening the configuration (e.g. unknown keys, + // or invalid values) + repeated string warnings = 1; +} message SettingsGetValueRequest { - // The key of the setting. + // The key to get string key = 1; + // The format of the encoded_value (default is "json", allowed values are + // "json" and "yaml) + string value_format = 2; } -message SettingsMergeResponse {} +message SettingsGetValueResponse { + // The value of the key (encoded) + string encoded_value = 1; +} + +message SettingsSetValueRequest { + // The key to change + string key = 1; + // The new value (encoded), no objects, only scalar or array of scalars are + // allowed. + string encoded_value = 2; + // The format of the encoded_value (default is "json", allowed values are + // "json", "yaml" and "cli") + string value_format = 3; +} message SettingsSetValueResponse {} -message SettingsWriteRequest { - // Path to settings file (e.g. /path/to/arduino-cli.yaml) - string file_path = 1; -} +message SettingsEnumerateRequest {} -message SettingsWriteResponse {} +message SettingsEnumerateResponse { + message Entry { + // The key + string key = 1; + // The key type + string type = 2; + } -message SettingsDeleteRequest { - // The key of the setting to delete. - string key = 1; + // The list of key/value pairs + repeated Entry entries = 1; } - -message SettingsDeleteResponse {} diff --git a/rpc/internal/client_example/main.go b/rpc/internal/client_example/main.go index dd23b0ec8d5..9fe750f36ea 100644 --- a/rpc/internal/client_example/main.go +++ b/rpc/internal/client_example/main.go @@ -72,40 +72,24 @@ func main() { callLoadSketch(client) // Use SetValue to configure the arduino-cli directories. - log.Println("calling SetValue") - callSetValue(client) + callSetValue(client, "directories.data", `"`+dataDir+`"`) + callSetValue(client, "directories.downloads", `"`+path.Join(dataDir, "staging")+`"`) + callSetValue(client, "directories.user", `"`+path.Join(dataDir, "sketchbook")+`"`) // List all settings - log.Println("calling SettingsGetAll()") - callGetAll(client) + callConfigurationSave(client) // Merge applies multiple settings values at once. - log.Println("calling SettingsMerge()") - callMerge(client, `{"foo": {"value": "bar"}, "daemon":{"port":"422"}, "board_manager": {"additional_urls":["https://example.com"]}}`) + callSetValue(client, "daemon.port", `"422"`) + callSetValue(client, "board_manager.additional_urls", `[ "https://example.com" ]`) - log.Println("calling SettingsGetAll()") - callGetAll(client) + callConfigurationSave(client) - log.Println("calling SettingsMerge()") - callMerge(client, `{"foo": {} }`) + callSetValue(client, "daemon.port", "") + callGetValue(client, "daemon.port") + callGetValue(client, "directories.data") - log.Println("calling SettingsGetAll()") - callGetAll(client) - - log.Println("calling SettingsMerge()") - callMerge(client, `{"foo": "bar" }`) - - // Get the value of the foo key. - log.Println("calling SettingsGetValue(foo)") - callGetValue(client) - - // List all settings - log.Println("calling SettingsGetAll()") - callGetAll(client) - - // Write settings to file. - log.Println("calling Write()") - callWrite(client) + callConfigurationSave(client) // Before we can do anything with the CLI, an "instance" must be created. // We keep a reference to the created instance because we will need it to @@ -244,11 +228,12 @@ func callVersion(client rpc.ArduinoCoreServiceClient) { log.Printf("arduino-cli version: %v", versionResp.GetVersion()) } -func callSetValue(client rpc.ArduinoCoreServiceClient) { +func callSetValue(client rpc.ArduinoCoreServiceClient, key, jsonValue string) { + log.Printf("Calling SetValue: %s = %s", key, jsonValue) _, err := client.SettingsSetValue(context.Background(), &rpc.SettingsSetValueRequest{ - Key: "directories", - JsonData: `{"data": "` + dataDir + `", "downloads": "` + path.Join(dataDir, "staging") + `", "user": "` + path.Join(dataDir, "sketchbook") + `"}`, + Key: key, + EncodedValue: jsonValue, }) if err != nil { @@ -259,8 +244,8 @@ func callSetValue(client rpc.ArduinoCoreServiceClient) { func callSetProxy(client rpc.ArduinoCoreServiceClient) { _, err := client.SettingsSetValue(context.Background(), &rpc.SettingsSetValueRequest{ - Key: "network.proxy", - JsonData: `"http://localhost:3128"`, + Key: "network.proxy", + EncodedValue: `"http://localhost:3128"`, }) if err != nil { @@ -271,8 +256,7 @@ func callSetProxy(client rpc.ArduinoCoreServiceClient) { func callUnsetProxy(client rpc.ArduinoCoreServiceClient) { _, err := client.SettingsSetValue(context.Background(), &rpc.SettingsSetValueRequest{ - Key: "network.proxy", - JsonData: `""`, + Key: "network.proxy", }) if err != nil { @@ -280,49 +264,30 @@ func callUnsetProxy(client rpc.ArduinoCoreServiceClient) { } } -func callMerge(client rpc.ArduinoCoreServiceClient, jsonData string) { - _, err := client.SettingsMerge(context.Background(), - &rpc.SettingsMergeRequest{ - JsonData: jsonData, - }) - - if err != nil { - log.Fatalf("Error merging settings: %s", err) - } -} - -func callGetValue(client rpc.ArduinoCoreServiceClient) { +func callGetValue(client rpc.ArduinoCoreServiceClient, key string) { getValueResp, err := client.SettingsGetValue(context.Background(), &rpc.SettingsGetValueRequest{ - Key: "foo", + Key: key, }) if err != nil { log.Fatalf("Error getting settings value: %s", err) } - log.Printf("Value: %s: %s", getValueResp.GetKey(), getValueResp.GetJsonData()) + log.Printf("%s = %s", key, getValueResp.GetEncodedValue()) } -func callGetAll(client rpc.ArduinoCoreServiceClient) { - getAllResp, err := client.SettingsGetAll(context.Background(), &rpc.SettingsGetAllRequest{}) +func callConfigurationSave(client rpc.ArduinoCoreServiceClient) { + log.Println("calling ConfigurationSave() >>") + getAllResp, err := client.ConfigurationSave(context.Background(), &rpc.ConfigurationSaveRequest{ + SettingsFormat: "json", + }) if err != nil { log.Fatalf("Error getting settings: %s", err) } - log.Printf("Settings: %s", getAllResp.GetJsonData()) -} - -func callWrite(client rpc.ArduinoCoreServiceClient) { - _, err := client.SettingsWrite(context.Background(), - &rpc.SettingsWriteRequest{ - FilePath: path.Join(dataDir, "written-rpc.Settingsyml"), - }) - - if err != nil { - log.Fatalf("Error writing settings: %s", err) - } + log.Printf("Settings follow:\n\n%s", getAllResp.GetEncodedSettings()) } func createInstance(client rpc.ArduinoCoreServiceClient) *rpc.Instance { diff --git a/version/version.go b/version/version.go index 576b48d1b66..abb0f75cc20 100644 --- a/version/version.go +++ b/version/version.go @@ -16,9 +16,6 @@ package version import ( - "os" - "path/filepath" - "github.com/arduino/arduino-cli/internal/i18n" ) @@ -70,5 +67,5 @@ func init() { versionString = defaultVersionString } - VersionInfo = NewInfo(filepath.Base(os.Args[0])) + VersionInfo = NewInfo("arduino-cli") }