From dff2df4aab73a26fb90401869bfd58035dc652a9 Mon Sep 17 00:00:00 2001 From: Lorenzo Alberto Maria Ambrosi Date: Tue, 7 Sep 2021 15:20:56 +0200 Subject: [PATCH 1/4] Add LED settings for potentially different hardware Change-type: patch Signed-off-by: Lorenzo Alberto Maria Ambrosi --- lib/gui/app/models/leds.ts | 88 ++++++++++++++++++++++++++------------ 1 file changed, 61 insertions(+), 27 deletions(-) diff --git a/lib/gui/app/models/leds.ts b/lib/gui/app/models/leds.ts index 85940055..61013bb2 100644 --- a/lib/gui/app/models/leds.ts +++ b/lib/gui/app/models/leds.ts @@ -29,13 +29,6 @@ import { observe, store } from './store'; const leds: Map = new Map(); const animator = new Animator([], 10); -const red: Color = [0.78, 0, 0]; -const green: Color = [0, 0.58, 0]; -const blue: Color = [0, 0, 0.1]; -const purple: Color = [0.7, 0, 0.78]; -const white: Color = [0.7, 0.7, 0.7]; -const black: Color = [0, 0, 0]; - function createAnimationFunction( intensityFunction: (t: number) => number, color: Color, @@ -54,13 +47,27 @@ function one(_t: number) { return 1; } -const blinkGreen = createAnimationFunction(blink, green); -const blinkPurple = createAnimationFunction(blink, purple); -const staticRed = createAnimationFunction(one, red); -const staticGreen = createAnimationFunction(one, green); -const staticBlue = createAnimationFunction(one, blue); -const staticWhite = createAnimationFunction(one, white); -const staticBlack = createAnimationFunction(one, black); +type LEDColors = { + green: Color; + purple: Color; + red: Color; + blue: Color; + white: Color; + black: Color; +}; + +type LEDAnimationFunctions = { + blinkGreen: AnimationFunction; + blinkPurple: AnimationFunction; + staticRed: AnimationFunction; + staticGreen: AnimationFunction; + staticBlue: AnimationFunction; + staticWhite: AnimationFunction; + staticBlack: AnimationFunction; +}; + +let ledColors: LEDColors; +let ledAnimationFunctions: LEDAnimationFunctions; interface LedsState { step: 'main' | 'flashing' | 'verifying' | 'finish'; @@ -132,31 +139,48 @@ export function updateLeds({ if (sourceDrive !== undefined) { if (plugged.has(sourceDrive)) { plugged.delete(sourceDrive); - mapping.push(setLeds(staticBlue, new Set([sourceDrive]))); + mapping.push( + setLeds(ledAnimationFunctions.staticBlue, new Set([sourceDrive])), + ); } } if (step === 'main') { mapping.push( - setLeds(staticBlack, new Set([...unplugged, ...plugged])), - setLeds(staticWhite, new Set([...selectedOk, ...selectedFailed])), + setLeds( + ledAnimationFunctions.staticBlack, + new Set([...unplugged, ...plugged]), + ), + setLeds( + ledAnimationFunctions.staticWhite, + new Set([...selectedOk, ...selectedFailed]), + ), ); } else if (step === 'flashing') { mapping.push( - setLeds(staticBlack, new Set([...unplugged, ...plugged])), - setLeds(blinkPurple, selectedOk), - setLeds(staticRed, selectedFailed), + setLeds( + ledAnimationFunctions.staticBlack, + new Set([...unplugged, ...plugged]), + ), + setLeds(ledAnimationFunctions.blinkPurple, selectedOk), + setLeds(ledAnimationFunctions.staticRed, selectedFailed), ); } else if (step === 'verifying') { mapping.push( - setLeds(staticBlack, new Set([...unplugged, ...plugged])), - setLeds(blinkGreen, selectedOk), - setLeds(staticRed, selectedFailed), + setLeds( + ledAnimationFunctions.staticBlack, + new Set([...unplugged, ...plugged]), + ), + setLeds(ledAnimationFunctions.blinkGreen, selectedOk), + setLeds(ledAnimationFunctions.staticRed, selectedFailed), ); } else if (step === 'finish') { mapping.push( - setLeds(staticBlack, new Set([...unplugged, ...plugged])), - setLeds(staticGreen, selectedOk), - setLeds(staticRed, selectedFailed), + setLeds( + ledAnimationFunctions.staticBlack, + new Set([...unplugged, ...plugged]), + ), + setLeds(ledAnimationFunctions.staticGreen, selectedOk), + setLeds(ledAnimationFunctions.staticRed, selectedFailed), ); } animator.mapping = mapping; @@ -221,6 +245,16 @@ export async function init(): Promise { for (const [drivePath, ledsNames] of Object.entries(ledsMapping)) { leds.set('/dev/disk/by-path/' + drivePath, new RGBLed(ledsNames)); } - observe(_.debounce(stateObserver, 1000, { maxWait: 1000 })); } + ledColors = (await settings.get('ledColors')) || {}; + ledAnimationFunctions = { + blinkGreen: createAnimationFunction(blink, ledColors['green']), + blinkPurple: createAnimationFunction(blink, ledColors['purple']), + staticRed: createAnimationFunction(one, ledColors['red']), + staticGreen: createAnimationFunction(one, ledColors['green']), + staticBlue: createAnimationFunction(one, ledColors['blue']), + staticWhite: createAnimationFunction(one, ledColors['white']), + staticBlack: createAnimationFunction(one, ledColors['black']), + }; + observe(_.debounce(stateObserver, 1000, { maxWait: 1000 })); } From d0114aece7df213e27a84cb0081ba6cedd541bcb Mon Sep 17 00:00:00 2001 From: David Gaspar <37712275+davidgaspardev@users.noreply.github.com> Date: Sat, 21 Aug 2021 14:04:28 -0300 Subject: [PATCH 2/4] Update Makefile to Apple M1 info Expanding host architecture detection. Change-type: patch --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index ece6d2ec..d7d7b645 100644 --- a/Makefile +++ b/Makefile @@ -66,6 +66,9 @@ else ifeq ($(shell uname -m),x86_64) HOST_ARCH = x64 endif + ifeq ($(shell uname -m),arm64) + HOST_ARCH = aarch64 + endif endif endif From 14d91400a425617ee87e0d64f55980bd378fbfc2 Mon Sep 17 00:00:00 2001 From: Lorenzo Alberto Maria Ambrosi Date: Wed, 15 Sep 2021 13:58:07 +0200 Subject: [PATCH 3/4] patch: Update etcher-sdk from v6.2.1 to v6.2.5 Signed-off-by: Lorenzo Alberto Maria Ambrosi --- npm-shrinkwrap.json | 85 +++++++++++++++++++++++++-------------------- package.json | 2 +- 2 files changed, 49 insertions(+), 38 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index c0eb0090..27552074 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1149,6 +1149,12 @@ "tar": "^4.4.2" } }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -1156,18 +1162,18 @@ "dev": true }, "tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", + "version": "4.4.19", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", + "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==", "dev": true, "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" + "chownr": "^1.1.4", + "fs-minipass": "^1.2.7", + "minipass": "^2.9.0", + "minizlib": "^1.3.3", + "mkdirp": "^0.5.5", + "safe-buffer": "^5.2.1", + "yallist": "^3.1.1" } }, "yallist": { @@ -1233,9 +1239,9 @@ } }, "@balena/node-crc-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@balena/node-crc-utils/-/node-crc-utils-2.0.0.tgz", - "integrity": "sha512-u86QDMtkpHLlvehs3Z+yHklXRhDPL5XGCO3BCSuaD61gKzrNDUIj03cz8T/PBPPUJqn7DfWkf9sKP9VwlvxKuw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@balena/node-crc-utils/-/node-crc-utils-2.0.1.tgz", + "integrity": "sha512-l+PZFPnO0vdx1HNaYq2p89mXIW8XcLoL7XjhwXAAbJ2FOmTg+8fgUEpohX+SJMxTUAE52FBTS8GzIKErCmBNTw==", "dev": true }, "@balena/udif": { @@ -3108,12 +3114,20 @@ } }, "axios": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", - "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", "dev": true, "requires": { - "follow-redirects": "^1.10.0" + "follow-redirects": "^1.14.0" + }, + "dependencies": { + "follow-redirects": { + "version": "1.14.4", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz", + "integrity": "sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==", + "dev": true + } } }, "babel-plugin-dynamic-import-node": { @@ -3535,11 +3549,11 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, "requires": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" - }, - "dev": true + } }, "buffer-alloc": { "version": "1.2.0", @@ -6791,9 +6805,9 @@ "dev": true }, "etcher-sdk": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/etcher-sdk/-/etcher-sdk-6.2.1.tgz", - "integrity": "sha512-d7B/6/b1+NdrvpybQrb1f315LRdAIPAkMAX8Gq63dJh5f4448svBadllzwZ2D4aqVfc++8SpSX0iPi1laXh6SA==", + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/etcher-sdk/-/etcher-sdk-6.2.5.tgz", + "integrity": "sha512-kPDhrJw9AVLnhCA9GOUnLNOmLNH7WO/paWRH43xF99svWcQu9IBprm7fSeuQthZGHojchWv/u4eyUI9wDck0/A==", "dev": true, "requires": { "@balena/node-beaglebone-usbboot": "^1.0.3", @@ -6819,7 +6833,7 @@ "tslib": "^2.0.0", "unbzip2-stream": "github:balena-io-modules/unbzip2-stream#4a54f56a25b58950f9e4277c56db2912d62242e7", "unzip-stream": "^0.3.0", - "xxhash": "^0.3.0", + "xxhash-addon": "^1.4.0", "yauzl": "^2.9.2", "zip-part-stream": "^1.0.3" }, @@ -10523,9 +10537,9 @@ } }, "nan": { - "version": "2.14.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", - "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", + "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", "dev": true }, "nanoid": { @@ -13668,9 +13682,9 @@ } }, "smart-buffer": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz", - "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "dev": true }, "snapdragon": { @@ -16719,14 +16733,11 @@ "integrity": "sha512-p4BESuV/g2L6pZzFHpeNLLnep9mp/DkF3qrPglMiucSFtD8iJxtMufEoEJbN8LZwB4i+8PFpFvVuFrGOSpW05w==", "dev": true }, - "xxhash": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/xxhash/-/xxhash-0.3.0.tgz", - "integrity": "sha512-1ud2yyPiR1DJhgyF1ZVMt+Ijrn0VNS/wzej1Z8eSFfkNfRPp8abVZNV2u9tYy9574II0ZayZYZgJm8KJoyGLCw==", - "dev": true, - "requires": { - "nan": "^2.13.2" - } + "xxhash-addon": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/xxhash-addon/-/xxhash-addon-1.4.0.tgz", + "integrity": "sha512-n3Ml0Vgvy7jMYJBlQIoFLjYxXNZQ5CbzW8E2Ynq2QCUpWMqCouooW7j02+7Oud5FijBuSrjQNuN/fCiz1SHN+w==", + "dev": true }, "y18n": { "version": "5.0.5", diff --git a/package.json b/package.json index 868151ab..3b91779c 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,7 @@ "electron-notarize": "^1.0.0", "electron-rebuild": "^2.3.2", "electron-updater": "^4.3.5", - "etcher-sdk": "^6.2.1", + "etcher-sdk": "^6.2.5", "file-loader": "^6.2.0", "husky": "^4.2.5", "immutable": "^3.8.1", From b2d0c1c9ddbbfe87d5a905d420d615821610e825 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20F=C3=BCllemann?= Date: Fri, 20 Aug 2021 14:39:23 +0200 Subject: [PATCH 4/4] add support for basic auth when downloading images from URL When selecting "Flash from URL" the user can optionally provide a username and password for basic authentication. The authentication input fields are collapsed by default. When the authentication input fields are collapsed after entering values the values are cleared to ensure that the user sees all parameter passed to the server. Change-Type: minor Changelog-Entry: Add support for basic auth when downloading images from URL. --- .../source-selector/source-selector.tsx | 75 +++++++++++++++++-- 1 file changed, 68 insertions(+), 7 deletions(-) diff --git a/lib/gui/app/components/source-selector/source-selector.tsx b/lib/gui/app/components/source-selector/source-selector.tsx index 50902ed6..d3e62fdb 100644 --- a/lib/gui/app/components/source-selector/source-selector.tsx +++ b/lib/gui/app/components/source-selector/source-selector.tsx @@ -18,6 +18,8 @@ import CopySvg from '@fortawesome/fontawesome-free/svgs/solid/copy.svg'; import FileSvg from '@fortawesome/fontawesome-free/svgs/solid/file.svg'; import LinkSvg from '@fortawesome/fontawesome-free/svgs/solid/link.svg'; import ExclamationTriangleSvg from '@fortawesome/fontawesome-free/svgs/solid/exclamation-triangle.svg'; +import ChevronDownSvg from '@fortawesome/fontawesome-free/svgs/solid/chevron-down.svg'; +import ChevronRightSvg from '@fortawesome/fontawesome-free/svgs/solid/chevron-right.svg'; import { sourceDestination } from 'etcher-sdk'; import { ipcRenderer, IpcRendererEvent } from 'electron'; import * as _ from 'lodash'; @@ -33,6 +35,7 @@ import { Card as BaseCard, Input, Spinner, + Link, } from 'rendition'; import styled from 'styled-components'; @@ -134,12 +137,15 @@ const URLSelector = ({ done, cancel, }: { - done: (imageURL: string) => void; + done: (imageURL: string, auth?: Authentication) => void; cancel: () => void; }) => { const [imageURL, setImageURL] = React.useState(''); const [recentImages, setRecentImages] = React.useState([]); const [loading, setLoading] = React.useState(false); + const [showBasicAuth, setShowBasicAuth] = React.useState(false); + const [username, setUsername] = React.useState(''); + const [password, setPassword] = React.useState(''); React.useEffect(() => { const fetchRecentUrlImages = async () => { const recentUrlImages: URL[] = await getRecentUrlImages(); @@ -162,11 +168,12 @@ const URLSelector = ({ imageURL, ]); setRecentUrlImages(normalizedRecentUrls); - await done(imageURL); + const auth = username ? { username, password } : undefined; + await done(imageURL, auth); }} > - + Use Image URL @@ -178,6 +185,49 @@ const URLSelector = ({ setImageURL(evt.target.value) } /> + { + if (showBasicAuth) { + setUsername(''); + setPassword(''); + } + setShowBasicAuth(!showBasicAuth); + }} + > + + {showBasicAuth && ( + + )} + {!showBasicAuth && ( + + )} + Authentication + + + {showBasicAuth && ( + + ) => + setUsername(evt.target.value) + } + /> + ) => + setPassword(evt.target.value) + } + /> + + )} {recentImages.length > 0 && ( @@ -283,6 +333,11 @@ interface SourceSelectorState { imageLoading: boolean; } +interface Authentication { + username: string; + password: string; +} + export class SourceSelector extends React.Component< SourceSelectorProps, SourceSelectorState @@ -328,7 +383,11 @@ export class SourceSelector extends React.Component< this.setState({ imageLoading: false }); } - private async createSource(selected: string, SourceType: Source) { + private async createSource( + selected: string, + SourceType: Source, + auth?: Authentication, + ) { try { selected = await replaceWindowsNetworkDriveLetter(selected); } catch (error) { @@ -351,7 +410,7 @@ export class SourceSelector extends React.Component< }); } - return new sourceDestination.Http({ url: selected }); + return new sourceDestination.Http({ url: selected, auth }); } public isJson(jsonString: string) { @@ -374,6 +433,7 @@ export class SourceSelector extends React.Component< private selectSource( selected: string | DrivelistDrive, SourceType: Source, + auth?: Authentication, ): { promise: Promise; cancel: () => void } { let cancelled = false; return { @@ -403,7 +463,7 @@ export class SourceSelector extends React.Component< }, }); } - source = await this.createSource(selected, SourceType); + source = await this.createSource(selected, SourceType, auth); if (cancelled) { return; @@ -750,7 +810,7 @@ export class SourceSelector extends React.Component< showURLSelector: false, }); }} - done={async (imageURL: string) => { + done={async (imageURL: string, auth?: Authentication) => { // Avoid analytics and selection state changes // if no file was resolved from the dialog. if (!imageURL) { @@ -760,6 +820,7 @@ export class SourceSelector extends React.Component< ({ promise, cancel: cancelURLSelection } = this.selectSource( imageURL, sourceDestination.Http, + auth, )); await promise; }