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") } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy