mirror of
https://github.com/balena-io/etcher.git
synced 2025-10-02 16:08:31 +00:00
Compare commits
34 Commits
build-inst
...
fix-leds-i
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b5e9701048 | ||
![]() |
292f86d6f5 | ||
![]() |
76ca9934c8 | ||
![]() |
37b826ee4e | ||
![]() |
1e1bd3c508 | ||
![]() |
00e8f11913 | ||
![]() |
a3c24a26a0 | ||
![]() |
4232928ad8 | ||
![]() |
b165fb78da | ||
![]() |
e9f6c5ead9 | ||
![]() |
b2d0c1c9dd | ||
![]() |
14d91400a4 | ||
![]() |
d0114aece7 | ||
![]() |
dff2df4aab | ||
![]() |
13159f93ee | ||
![]() |
3ece1fd841 | ||
![]() |
f46963b6b3 | ||
![]() |
b97f4e0031 | ||
![]() |
e2d233d74b | ||
![]() |
a7ca2e527b | ||
![]() |
396a053c0a | ||
![]() |
d1a3f1cb88 | ||
![]() |
9f96558cdd | ||
![]() |
b3bc589d70 | ||
![]() |
18d2c28110 | ||
![]() |
b272ef296d | ||
![]() |
32ca28a3a9 | ||
![]() |
4d5e5a3b0b | ||
![]() |
8b3f37102d | ||
![]() |
4b74253631 | ||
![]() |
a81b552b95 | ||
![]() |
53f53c0f75 | ||
![]() |
fdaf5c69d6 | ||
![]() |
061afca5d3 |
@@ -7,7 +7,6 @@ indent_size = 2
|
|||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
insert_final_newline = true
|
|
||||||
|
|
||||||
[*.md]
|
[*.md]
|
||||||
trim_trailing_whitespace = false
|
trim_trailing_whitespace = false
|
||||||
|
@@ -24,7 +24,6 @@
|
|||||||
"generated",
|
"generated",
|
||||||
"lib/shared/catalina-sudo/sudo-askpass.osascript.js"
|
"lib/shared/catalina-sudo/sudo-askpass.osascript.js"
|
||||||
],
|
],
|
||||||
"beforeBuild": "./beforeBuild.js",
|
|
||||||
"afterSign": "./afterSignHook.js",
|
"afterSign": "./afterSignHook.js",
|
||||||
"mac": {
|
"mac": {
|
||||||
"category": "public.app-category.developer-tools",
|
"category": "public.app-category.developer-tools",
|
||||||
|
@@ -1,3 +1,204 @@
|
|||||||
|
- commits:
|
||||||
|
- subject: Add support for basic auth when downloading images from URL.
|
||||||
|
hash: b2d0c1c9ddbbfe87d5a905d420d615821610e825
|
||||||
|
body: >
|
||||||
|
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.
|
||||||
|
footer:
|
||||||
|
Change-Type: minor
|
||||||
|
change-type: minor
|
||||||
|
Changelog-Entry: Add support for basic auth when downloading images from URL.
|
||||||
|
changelog-entry: Add support for basic auth when downloading images from URL.
|
||||||
|
author: Marco Füllemann
|
||||||
|
nested: []
|
||||||
|
- subject: 'patch: Update etcher-sdk from v6.2.1 to v6.2.5'
|
||||||
|
hash: 14d91400a425617ee87e0d64f55980bd378fbfc2
|
||||||
|
body: ''
|
||||||
|
footer:
|
||||||
|
Signed-off-by: Lorenzo Alberto Maria Ambrosi <lorenzothunder.ambrosi@gmail.com>
|
||||||
|
signed-off-by: Lorenzo Alberto Maria Ambrosi <lorenzothunder.ambrosi@gmail.com>
|
||||||
|
author: Lorenzo Alberto Maria Ambrosi
|
||||||
|
nested: []
|
||||||
|
- subject: Update Makefile to Apple M1 info
|
||||||
|
hash: d0114aece7df213e27a84cb0081ba6cedd541bcb
|
||||||
|
body: |
|
||||||
|
Expanding host architecture detection.
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
author: David Gaspar
|
||||||
|
nested: []
|
||||||
|
- subject: Add LED settings for potentially different hardware
|
||||||
|
hash: dff2df4aab73a26fb90401869bfd58035dc652a9
|
||||||
|
body: ''
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
Signed-off-by: Lorenzo Alberto Maria Ambrosi <lorenzothunder.ambrosi@gmail.com>
|
||||||
|
signed-off-by: Lorenzo Alberto Maria Ambrosi <lorenzothunder.ambrosi@gmail.com>
|
||||||
|
author: Lorenzo Alberto Maria Ambrosi
|
||||||
|
nested: []
|
||||||
|
version: 1.6.0
|
||||||
|
date: 2021-09-20T10:42:04.677Z
|
||||||
|
- commits:
|
||||||
|
- subject: Restore image file selection LED-drive pathing
|
||||||
|
hash: f46963b6b3176395acc07863c9936a7c7f31d31a
|
||||||
|
body: ''
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
Signed-off-by: Lorenzo Alberto Maria Ambrosi <lorenzothunder.ambrosi@gmail.com>
|
||||||
|
signed-off-by: Lorenzo Alberto Maria Ambrosi <lorenzothunder.ambrosi@gmail.com>
|
||||||
|
author: Lorenzo Alberto Maria Ambrosi
|
||||||
|
nested: []
|
||||||
|
- subject: Update scripts submodule
|
||||||
|
hash: b97f4e0031d7c4d0f33be9fdb8c999631f9eef1d
|
||||||
|
body: ''
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
Signed-off-by: Lorenzo Alberto Maria Ambrosi <lorenzothunder.ambrosi@gmail.com>
|
||||||
|
signed-off-by: Lorenzo Alberto Maria Ambrosi <lorenzothunder.ambrosi@gmail.com>
|
||||||
|
author: Lorenzo Alberto Maria Ambrosi
|
||||||
|
nested: []
|
||||||
|
- subject: Change LEDs colours
|
||||||
|
hash: e2d233d74b6335fd53a9271a9c00c3f93828c5b5
|
||||||
|
body: ''
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
Signed-off-by: Lorenzo Alberto Maria Ambrosi <lorenzothunder.ambrosi@gmail.com>
|
||||||
|
signed-off-by: Lorenzo Alberto Maria Ambrosi <lorenzothunder.ambrosi@gmail.com>
|
||||||
|
author: Lorenzo Alberto Maria Ambrosi
|
||||||
|
nested: []
|
||||||
|
- subject: Windows images now show the proper warning again
|
||||||
|
hash: a7ca2e527bc0cc040711ee4d60f93eda35f17558
|
||||||
|
body: ''
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
Changelog-entry: Windows images now show the proper warning again
|
||||||
|
changelog-entry: Windows images now show the proper warning again
|
||||||
|
Signed-off-by: Lorenzo Alberto Maria Ambrosi <lorenzothunder.ambrosi@gmail.com>
|
||||||
|
signed-off-by: Lorenzo Alberto Maria Ambrosi <lorenzothunder.ambrosi@gmail.com>
|
||||||
|
author: Lorenzo Alberto Maria Ambrosi
|
||||||
|
nested: []
|
||||||
|
- subject: Fix Update and install with DNF instructions
|
||||||
|
hash: 396a053c0a0ec8def4b3672509cbb4ecc0b0c784
|
||||||
|
body: ''
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
author: Mohamed Salah
|
||||||
|
nested: []
|
||||||
|
- subject: Add possibile authorization as a query param
|
||||||
|
hash: d1a3f1cb88ff38f804caa9289d3205b09666c1e6
|
||||||
|
body: ''
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
Signed-off-by: Andrea Rosci <andrear@balena.io>
|
||||||
|
signed-off-by: Andrea Rosci <andrear@balena.io>
|
||||||
|
author: JSReds
|
||||||
|
nested: []
|
||||||
|
- subject: update the windows part
|
||||||
|
hash: 9f96558cdd11ce83dcc08289c31da425063eab24
|
||||||
|
body: |
|
||||||
|
I choose to add this part because, after the clean the usb stick
|
||||||
|
could stay in a raw state without creating the new partions,
|
||||||
|
activating and formatting.
|
||||||
|
Thanks
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
author: Xtraim
|
||||||
|
nested: []
|
||||||
|
- subject: Update SUPPORT.md
|
||||||
|
hash: b3bc589d70cc4498a13f86f7d9aa36d9908275e3
|
||||||
|
body: ''
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
author: thambu1710
|
||||||
|
nested: []
|
||||||
|
- subject: replace make webpack with npm run webpack
|
||||||
|
hash: 18d2c28110c8b4b4c327a58f6f6a712c33dfd4cc
|
||||||
|
body: ''
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
author: Seth Falco
|
||||||
|
nested: []
|
||||||
|
- subject: Add loader on image select
|
||||||
|
hash: b272ef296dec9b4242028202e1d759f1e2d1aa2b
|
||||||
|
body: ''
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
Signed-off-by: Andrea Rosci <andrear@balena.io>
|
||||||
|
signed-off-by: Andrea Rosci <andrear@balena.io>
|
||||||
|
author: JSReds
|
||||||
|
nested: []
|
||||||
|
- subject: add pnp-webpack-plugin
|
||||||
|
hash: 32ca28a3a95d2ffd3eb2b32cfc54113515ae3097
|
||||||
|
body: ''
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
author: Zane Hitchcox
|
||||||
|
nested: []
|
||||||
|
- subject: Remove redundant codespell dependency/tests
|
||||||
|
hash: 4d5e5a3b0b81cbdd3341abbcca0c816bc905a8ed
|
||||||
|
body: ''
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
Signed-off-by: Lorenzo Alberto Maria Ambrosi <lorenzothunder.ambrosi@gmail.com>
|
||||||
|
signed-off-by: Lorenzo Alberto Maria Ambrosi <lorenzothunder.ambrosi@gmail.com>
|
||||||
|
author: Lorenzo Alberto Maria Ambrosi
|
||||||
|
nested: []
|
||||||
|
version: 1.5.122
|
||||||
|
date: 2021-09-02T12:20:22.871Z
|
||||||
|
- commits:
|
||||||
|
- subject: 'patch: Delete Codeowners'
|
||||||
|
hash: a81b552b95f93a8989a6fff4774a14e21abe9a0e
|
||||||
|
body: ''
|
||||||
|
footer: {}
|
||||||
|
author: Vipul Gupta
|
||||||
|
nested: []
|
||||||
|
- subject: Add source maps for devtools
|
||||||
|
hash: 53f53c0f75779e814834e2fd0375b705664190c5
|
||||||
|
body: ''
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
Signed-off-by: Lorenzo Alberto Maria Ambrosi <lorenzothunder.ambrosi@gmail.com>
|
||||||
|
signed-off-by: Lorenzo Alberto Maria Ambrosi <lorenzothunder.ambrosi@gmail.com>
|
||||||
|
author: Lorenzo Alberto Maria Ambrosi
|
||||||
|
nested: []
|
||||||
|
- subject: Clone submodules when initializing modules
|
||||||
|
hash: fdaf5c69d6bd20b64b1c1749b62dec9c22f12fb4
|
||||||
|
body: ''
|
||||||
|
footer:
|
||||||
|
Change-type: patch
|
||||||
|
change-type: patch
|
||||||
|
Signed-off-by: Lorenzo Alberto Maria Ambrosi <lorenzothunder.ambrosi@gmail.com>
|
||||||
|
signed-off-by: Lorenzo Alberto Maria Ambrosi <lorenzothunder.ambrosi@gmail.com>
|
||||||
|
author: Lorenzo Alberto Maria Ambrosi
|
||||||
|
nested: []
|
||||||
|
- subject: 'patch: Select drive on list interaction rather than modal closing'
|
||||||
|
hash: 061afca5d3ce7dbf67d66706e6c2c65ecd61cf7b
|
||||||
|
body: ''
|
||||||
|
footer:
|
||||||
|
Signed-off-by: Lorenzo Alberto Maria Ambrosi <lorenzothunder.ambrosi@gmail.com>
|
||||||
|
signed-off-by: Lorenzo Alberto Maria Ambrosi <lorenzothunder.ambrosi@gmail.com>
|
||||||
|
author: Lorenzo Alberto Maria Ambrosi
|
||||||
|
nested: []
|
||||||
|
version: 1.5.121
|
||||||
|
date: 2021-07-05T18:20:04.735Z
|
||||||
- commits:
|
- commits:
|
||||||
- subject: Update README to reference Cloudsmith
|
- subject: Update README to reference Cloudsmith
|
||||||
hash: 7e333caaf9d94ff90583fe897ccabb6fdf860f74
|
hash: 7e333caaf9d94ff90583fe897ccabb6fdf860f74
|
||||||
|
93
BUILD.md
93
BUILD.md
@@ -1,93 +0,0 @@
|
|||||||
# Building etcher on Windows, Linux, and Mac
|
|
||||||
|
|
||||||
> How do you build etcher? On Windows, Linux, Mac
|
|
||||||
|
|
||||||
It’s the same on all systems:
|
|
||||||
|
|
||||||
First clone the repository recursively:
|
|
||||||
|
|
||||||
`git clone --recursive https://github.com:balena-io/etcher`
|
|
||||||
|
|
||||||
```
|
|
||||||
rm -rf node_modules
|
|
||||||
make electron-develop
|
|
||||||
npm start
|
|
||||||
```
|
|
||||||
|
|
||||||
To build packages for distribution, run `make electron-build` after the steps above.
|
|
||||||
|
|
||||||
### Windows
|
|
||||||
|
|
||||||
> What version of mingw and visual studio are needed to build? Any other dependencies for windows? Instructions for how to install?
|
|
||||||
|
|
||||||
Visual studio 2019 with:
|
|
||||||
* MSVC v142 x86-64 build tools
|
|
||||||
* Windows 10 SDK
|
|
||||||
|
|
||||||
Also install Windows Driver Kit from https://docs.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk
|
|
||||||
- you might have to look under "previous versions" for the windows wdk to match your windows sdk
|
|
||||||
- all you need from this page is the WDK, you can ignore everything else, just download it
|
|
||||||
|
|
||||||
pip install -r requirements.txt
|
|
||||||
|
|
||||||
Mingw64, install it from https://www.msys2.org/
|
|
||||||
|
|
||||||
install choco: https://chocolatey.org/install
|
|
||||||
install jq: choco install jq -y
|
|
||||||
|
|
||||||
Add /c/msys64/usr/bin/ to path
|
|
||||||
|
|
||||||
|
|
||||||
> It appears you’re supposed to use `npm i` and then `npm watch` and then `npm start` in order to build/run the electron app, but I’m getting the error:
|
|
||||||
|
|
||||||
NODE_MODULE_VERSION 88. This version of Node.js requires
|
|
||||||
NODE_MODULE_VERSION 80. Please try re-compiling or re-installing
|
|
||||||
|
|
||||||
From the module `xxhash`
|
|
||||||
|
|
||||||
This error means that a module wasn’t built for the correct runtime (node or electron) and version. In general it means that either you ran `npm i` instead of `make electron-develop` or you just updated the electron version.
|
|
||||||
Remove the node_modules folder and run `make electron-develop`, that should fix it.
|
|
||||||
|
|
||||||
If you want to use only npm, set up some env vars to target the correct runtime as in https://github.com/balena-io/EtcherProApplication/blob/v1.0.0/Dockerfile#L11-L13
|
|
||||||
|
|
||||||
|
|
||||||
Each time you change a js file, you need to run `npm webpack` again. This is quite slow. To make it faster leave `npm run watch` running. It actually just runs `webpack --watch`
|
|
||||||
|
|
||||||
https://github.com/electron/electron/blob/master/docs/tutorial/using-native-node-modules.md
|
|
||||||
|
|
||||||
### Mac OS X
|
|
||||||
|
|
||||||
Install XCode via the App Store
|
|
||||||
|
|
||||||
> I get the error: `gyp: No Xcode or CLT version detected!`
|
|
||||||
|
|
||||||
Fix with:
|
|
||||||
|
|
||||||
```
|
|
||||||
sudo xcodebuild -license accept
|
|
||||||
```
|
|
||||||
|
|
||||||
> I get the error: `xcode-select: error: tool 'xcodebuild' requires Xcode, but active developer directory '/Library/Developer/CommandLineTools' is a command line tools instance`
|
|
||||||
|
|
||||||
Fix with:
|
|
||||||
|
|
||||||
```
|
|
||||||
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
|
|
||||||
```
|
|
||||||
|
|
||||||
> I get the error: `[webpack-cli] Error: Could not find lzma_native binding`
|
|
||||||
|
|
||||||
Try removing node_modules and running `make electron-develop` again.
|
|
||||||
|
|
||||||
### Linux
|
|
||||||
|
|
||||||
Should work OOTB, please create issue if not.
|
|
||||||
|
|
||||||
* Note: Might have to downgrade to Node 14
|
|
||||||
|
|
||||||
|
|
||||||
### FreeBSD
|
|
||||||
|
|
||||||
Should work OOTB, please create issue if not.
|
|
||||||
|
|
||||||
* Note: Might have to downgrade to Node 14
|
|
32
CHANGELOG.md
32
CHANGELOG.md
@@ -3,6 +3,38 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
|
# v1.6.0
|
||||||
|
## (2021-09-20)
|
||||||
|
|
||||||
|
* Add support for basic auth when downloading images from URL. [Marco Füllemann]
|
||||||
|
* patch: Update etcher-sdk from v6.2.1 to v6.2.5 [Lorenzo Alberto Maria Ambrosi]
|
||||||
|
* Update Makefile to Apple M1 info [David Gaspar]
|
||||||
|
* Add LED settings for potentially different hardware [Lorenzo Alberto Maria Ambrosi]
|
||||||
|
|
||||||
|
# v1.5.122
|
||||||
|
## (2021-09-02)
|
||||||
|
|
||||||
|
* Restore image file selection LED-drive pathing [Lorenzo Alberto Maria Ambrosi]
|
||||||
|
* Update scripts submodule [Lorenzo Alberto Maria Ambrosi]
|
||||||
|
* Change LEDs colours [Lorenzo Alberto Maria Ambrosi]
|
||||||
|
* Windows images now show the proper warning again [Lorenzo Alberto Maria Ambrosi]
|
||||||
|
* Fix Update and install with DNF instructions [Mohamed Salah]
|
||||||
|
* Add possibile authorization as a query param [JSReds]
|
||||||
|
* update the windows part [Xtraim]
|
||||||
|
* Update SUPPORT.md [thambu1710]
|
||||||
|
* replace make webpack with npm run webpack [Seth Falco]
|
||||||
|
* Add loader on image select [JSReds]
|
||||||
|
* add pnp-webpack-plugin [Zane Hitchcox]
|
||||||
|
* Remove redundant codespell dependency/tests [Lorenzo Alberto Maria Ambrosi]
|
||||||
|
|
||||||
|
# v1.5.121
|
||||||
|
## (2021-07-05)
|
||||||
|
|
||||||
|
* patch: Delete Codeowners [Vipul Gupta]
|
||||||
|
* Add source maps for devtools [Lorenzo Alberto Maria Ambrosi]
|
||||||
|
* Clone submodules when initializing modules [Lorenzo Alberto Maria Ambrosi]
|
||||||
|
* patch: Select drive on list interaction rather than modal closing [Lorenzo Alberto Maria Ambrosi]
|
||||||
|
|
||||||
# v1.5.120
|
# v1.5.120
|
||||||
## (2021-05-11)
|
## (2021-05-11)
|
||||||
|
|
||||||
|
@@ -1,2 +0,0 @@
|
|||||||
* @thundron @zvin @jviotti
|
|
||||||
/scripts @nazrhom
|
|
4
Makefile
4
Makefile
@@ -66,6 +66,9 @@ else
|
|||||||
ifeq ($(shell uname -m),x86_64)
|
ifeq ($(shell uname -m),x86_64)
|
||||||
HOST_ARCH = x64
|
HOST_ARCH = x64
|
||||||
endif
|
endif
|
||||||
|
ifeq ($(shell uname -m),arm64)
|
||||||
|
HOST_ARCH = aarch64
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
@@ -86,6 +89,7 @@ TARGET_ARCH ?= $(HOST_ARCH)
|
|||||||
# Electron
|
# Electron
|
||||||
# ---------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
electron-develop:
|
electron-develop:
|
||||||
|
git submodule update --init && \
|
||||||
$(RESIN_SCRIPTS)/electron/install.sh \
|
$(RESIN_SCRIPTS)/electron/install.sh \
|
||||||
-b $(shell pwd) \
|
-b $(shell pwd) \
|
||||||
-r $(TARGET_ARCH) \
|
-r $(TARGET_ARCH) \
|
||||||
|
@@ -91,8 +91,6 @@ zypper rr balena-etcher-source
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
sudo dnf install -y balena-etcher-electron
|
sudo dnf install -y balena-etcher-electron
|
||||||
rm /etc/yum.repos.d/balena-etcher.repo
|
|
||||||
rm /etc/yum.repos.d/balena-etcher-source.repo
|
|
||||||
```
|
```
|
||||||
|
|
||||||
###### Uninstall
|
###### Uninstall
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
Getting help with Etcher
|
Getting help with BalenaEtcher
|
||||||
========================
|
===============================
|
||||||
|
|
||||||
There are various ways to get support for Etcher if you experience an issue or
|
There are various ways to get support for Etcher if you experience an issue or
|
||||||
have an idea you'd like to share with us.
|
have an idea you'd like to share with us.
|
||||||
@@ -22,7 +22,7 @@ a look at the existing threads before opening a new one!
|
|||||||
Make sure to mention the following information to help us provide better
|
Make sure to mention the following information to help us provide better
|
||||||
support:
|
support:
|
||||||
|
|
||||||
- The Etcher version you're running.
|
- The BalenaEtcher version you're running.
|
||||||
|
|
||||||
- The operating system you're running Etcher in.
|
- The operating system you're running Etcher in.
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ support:
|
|||||||
GitHub
|
GitHub
|
||||||
------
|
------
|
||||||
|
|
||||||
If you encounter an issue or have a suggestion, head on over to Etcher's [issue
|
If you encounter an issue or have a suggestion, head on over to BalenaEtcher's [issue
|
||||||
tracker][issues] and if there isn't a ticket covering it, [create
|
tracker][issues] and if there isn't a ticket covering it, [create
|
||||||
one][new-issue].
|
one][new-issue].
|
||||||
|
|
||||||
|
@@ -1,33 +0,0 @@
|
|||||||
'use strict'
|
|
||||||
|
|
||||||
const cp = require('child_process');
|
|
||||||
const rimraf = require('rimraf');
|
|
||||||
const process = require('process');
|
|
||||||
|
|
||||||
// Rebuild native modules for ia32 and run webpack again for the ia32 part of windows packages
|
|
||||||
exports.default = function(context) {
|
|
||||||
if (['windows', 'mac'].includes(context.platform.name)) {
|
|
||||||
const run = context.platform.name === 'windows' ? 'sh' : 'node';
|
|
||||||
cp.execFileSync(
|
|
||||||
run,
|
|
||||||
['node_modules/.bin/electron-rebuild', '--types', 'dev', '--arch', context.arch],
|
|
||||||
{
|
|
||||||
env: {
|
|
||||||
...process.env,
|
|
||||||
npm_config_msvs_version: '2019',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
rimraf.sync('generated');
|
|
||||||
cp.execFileSync(
|
|
||||||
run,
|
|
||||||
['node_modules/.bin/webpack'],
|
|
||||||
{
|
|
||||||
env: {
|
|
||||||
...process.env,
|
|
||||||
npm_config_target_arch: context.arch,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -91,7 +91,7 @@ make electron-develop
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
# Build the GUI
|
# Build the GUI
|
||||||
make webpack
|
npm run webpack
|
||||||
# Start Electron
|
# Start Electron
|
||||||
npm start
|
npm start
|
||||||
```
|
```
|
||||||
|
@@ -160,6 +160,18 @@ pre-installed in all modern Windows versions.
|
|||||||
- Run `clean`. This command will completely clean your drive by erasing any
|
- Run `clean`. This command will completely clean your drive by erasing any
|
||||||
existent filesystem.
|
existent filesystem.
|
||||||
|
|
||||||
|
- Run `create partition primary`. This command will create a new partition.
|
||||||
|
|
||||||
|
- Run `active`. This command will active the partition.
|
||||||
|
|
||||||
|
- Run `list partition`. This command will show available partition.
|
||||||
|
|
||||||
|
- Run `select partition N`, where `N` corresponds to the id of the newly available partition.
|
||||||
|
|
||||||
|
- Run `format override quick`. This command will format the partition. You can choose a specific formatting by adding `FS=xx` where `xx` could be `NTFS or FAT or FAT32` after `format`. Example : `format FS=NTFS override quick`
|
||||||
|
|
||||||
|
- Run `exit` to quit diskpart.
|
||||||
|
|
||||||
### OS X
|
### OS X
|
||||||
|
|
||||||
Run the following command in `Terminal.app`, replacing `N` by the corresponding
|
Run the following command in `Terminal.app`, replacing `N` by the corresponding
|
||||||
|
@@ -4,7 +4,6 @@ productName: balenaEtcher
|
|||||||
npmRebuild: true
|
npmRebuild: true
|
||||||
nodeGypRebuild: false
|
nodeGypRebuild: false
|
||||||
publish: null
|
publish: null
|
||||||
beforeBuild: "./beforeBuild.js"
|
|
||||||
afterPack: "./afterPack.js"
|
afterPack: "./afterPack.js"
|
||||||
asar: false
|
asar: false
|
||||||
files:
|
files:
|
||||||
|
@@ -217,8 +217,7 @@ function prepareDrive(drive: Drive) {
|
|||||||
disabled: true,
|
disabled: true,
|
||||||
icon: 'warning',
|
icon: 'warning',
|
||||||
size: null,
|
size: null,
|
||||||
link:
|
link: 'https://www.raspberrypi.org/documentation/hardware/computemodule/cm-emmc-flashing.md',
|
||||||
'https://www.raspberrypi.org/documentation/hardware/computemodule/cm-emmc-flashing.md',
|
|
||||||
linkCTA: 'Install',
|
linkCTA: 'Install',
|
||||||
linkTitle: 'Install missing drivers',
|
linkTitle: 'Install missing drivers',
|
||||||
linkMessage: outdent`
|
linkMessage: outdent`
|
||||||
@@ -335,7 +334,7 @@ window.addEventListener('beforeunload', async (event) => {
|
|||||||
flashingWorkflowUuid,
|
flashingWorkflowUuid,
|
||||||
});
|
});
|
||||||
popupExists = false;
|
popupExists = false;
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
exceptionReporter.report(error);
|
exceptionReporter.report(error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -43,6 +43,7 @@ import {
|
|||||||
} from '../../styled-components';
|
} from '../../styled-components';
|
||||||
|
|
||||||
import { SourceMetadata } from '../source-selector/source-selector';
|
import { SourceMetadata } from '../source-selector/source-selector';
|
||||||
|
import { middleEllipsis } from '../../utils/middle-ellipsis';
|
||||||
|
|
||||||
interface UsbbootDrive extends sourceDestination.UsbbootDrive {
|
interface UsbbootDrive extends sourceDestination.UsbbootDrive {
|
||||||
progress: number;
|
progress: number;
|
||||||
@@ -136,17 +137,18 @@ const InitProgress = styled(
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export interface DriveSelectorProps
|
export interface DriveSelectorProps
|
||||||
extends Omit<ModalProps, 'done' | 'cancel'> {
|
extends Omit<ModalProps, 'done' | 'cancel' | 'onSelect'> {
|
||||||
write: boolean;
|
write: boolean;
|
||||||
multipleSelection: boolean;
|
multipleSelection: boolean;
|
||||||
showWarnings?: boolean;
|
showWarnings?: boolean;
|
||||||
cancel: () => void;
|
cancel: (drives: DrivelistDrive[]) => void;
|
||||||
done: (drives: DrivelistDrive[]) => void;
|
done: (drives: DrivelistDrive[]) => void;
|
||||||
titleLabel: string;
|
titleLabel: string;
|
||||||
emptyListLabel: string;
|
emptyListLabel: string;
|
||||||
emptyListIcon: JSX.Element;
|
emptyListIcon: JSX.Element;
|
||||||
selectedList?: DrivelistDrive[];
|
selectedList?: DrivelistDrive[];
|
||||||
updateSelectedList?: () => DrivelistDrive[];
|
updateSelectedList?: () => DrivelistDrive[];
|
||||||
|
onSelect?: (drive: DrivelistDrive) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DriveSelectorState {
|
interface DriveSelectorState {
|
||||||
@@ -167,12 +169,14 @@ export class DriveSelector extends React.Component<
|
|||||||
> {
|
> {
|
||||||
private unsubscribe: (() => void) | undefined;
|
private unsubscribe: (() => void) | undefined;
|
||||||
tableColumns: Array<TableColumn<Drive>>;
|
tableColumns: Array<TableColumn<Drive>>;
|
||||||
|
originalList: DrivelistDrive[];
|
||||||
|
|
||||||
constructor(props: DriveSelectorProps) {
|
constructor(props: DriveSelectorProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
const defaultMissingDriversModalState: { drive?: DriverlessDrive } = {};
|
const defaultMissingDriversModalState: { drive?: DriverlessDrive } = {};
|
||||||
const selectedList = this.props.selectedList || [];
|
const selectedList = this.props.selectedList || [];
|
||||||
|
this.originalList = [...(this.props.selectedList || [])];
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
drives: getDrives(),
|
drives: getDrives(),
|
||||||
@@ -199,7 +203,9 @@ export class DriveSelector extends React.Component<
|
|||||||
fill={drive.isSystem ? '#fca321' : '#8f9297'}
|
fill={drive.isSystem ? '#fca321' : '#8f9297'}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Txt ml={(hasWarnings && 8) || 0}>{description}</Txt>
|
<Txt ml={(hasWarnings && 8) || 0}>
|
||||||
|
{middleEllipsis(description, 32)}
|
||||||
|
</Txt>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -259,7 +265,7 @@ export class DriveSelector extends React.Component<
|
|||||||
return (
|
return (
|
||||||
isUsbbootDrive(drive) ||
|
isUsbbootDrive(drive) ||
|
||||||
isDriverlessDrive(drive) ||
|
isDriverlessDrive(drive) ||
|
||||||
!isDriveValid(drive, image) ||
|
!isDriveValid(drive, image, this.props.write) ||
|
||||||
(this.props.write && drive.isReadOnly)
|
(this.props.write && drive.isReadOnly)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -348,16 +354,6 @@ export class DriveSelector extends React.Component<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private deselectingAll(rows: DrivelistDrive[]) {
|
|
||||||
return (
|
|
||||||
rows.length > 0 &&
|
|
||||||
rows.length === this.state.selectedList.length &&
|
|
||||||
this.state.selectedList.every(
|
|
||||||
(d) => rows.findIndex((r) => d.device === r.device) > -1,
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.unsubscribe = store.subscribe(() => {
|
this.unsubscribe = store.subscribe(() => {
|
||||||
const drives = getDrives();
|
const drives = getDrives();
|
||||||
@@ -383,8 +379,8 @@ export class DriveSelector extends React.Component<
|
|||||||
const displayedDrives = this.getDisplayedDrives(drives);
|
const displayedDrives = this.getDisplayedDrives(drives);
|
||||||
const disabledDrives = this.getDisabledDrives(drives, image);
|
const disabledDrives = this.getDisabledDrives(drives, image);
|
||||||
const numberOfSystemDrives = drives.filter(isSystemDrive).length;
|
const numberOfSystemDrives = drives.filter(isSystemDrive).length;
|
||||||
const numberOfDisplayedSystemDrives = displayedDrives.filter(isSystemDrive)
|
const numberOfDisplayedSystemDrives =
|
||||||
.length;
|
displayedDrives.filter(isSystemDrive).length;
|
||||||
const numberOfHiddenSystemDrives =
|
const numberOfHiddenSystemDrives =
|
||||||
numberOfSystemDrives - numberOfDisplayedSystemDrives;
|
numberOfSystemDrives - numberOfDisplayedSystemDrives;
|
||||||
const hasSystemDrives = selectedList.filter(isSystemDrive).length;
|
const hasSystemDrives = selectedList.filter(isSystemDrive).length;
|
||||||
@@ -408,7 +404,7 @@ export class DriveSelector extends React.Component<
|
|||||||
</Flex>
|
</Flex>
|
||||||
}
|
}
|
||||||
titleDetails={<Txt fontSize={11}>{getDrives().length} found</Txt>}
|
titleDetails={<Txt fontSize={11}>{getDrives().length} found</Txt>}
|
||||||
cancel={cancel}
|
cancel={() => cancel(this.originalList)}
|
||||||
done={() => done(selectedList)}
|
done={() => done(selectedList)}
|
||||||
action={`Select (${selectedList.length})`}
|
action={`Select (${selectedList.length})`}
|
||||||
primaryButtonProps={{
|
primaryButtonProps={{
|
||||||
@@ -448,14 +444,34 @@ export class DriveSelector extends React.Component<
|
|||||||
onCheck={(rows: Drive[]) => {
|
onCheck={(rows: Drive[]) => {
|
||||||
let newSelection = rows.filter(isDrivelistDrive);
|
let newSelection = rows.filter(isDrivelistDrive);
|
||||||
if (this.props.multipleSelection) {
|
if (this.props.multipleSelection) {
|
||||||
if (this.deselectingAll(newSelection)) {
|
if (rows.length === 0) {
|
||||||
newSelection = [];
|
newSelection = [];
|
||||||
}
|
}
|
||||||
|
const deselecting = selectedList.filter(
|
||||||
|
(selected) =>
|
||||||
|
newSelection.filter(
|
||||||
|
(row) => row.device === selected.device,
|
||||||
|
).length === 0,
|
||||||
|
);
|
||||||
|
const selecting = newSelection.filter(
|
||||||
|
(row) =>
|
||||||
|
selectedList.filter(
|
||||||
|
(selected) => row.device === selected.device,
|
||||||
|
).length === 0,
|
||||||
|
);
|
||||||
|
deselecting.concat(selecting).forEach((row) => {
|
||||||
|
if (this.props.onSelect) {
|
||||||
|
this.props.onSelect(row);
|
||||||
|
}
|
||||||
|
});
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedList: newSelection,
|
selectedList: newSelection,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (this.props.onSelect) {
|
||||||
|
this.props.onSelect(newSelection[newSelection.length - 1]);
|
||||||
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedList: newSelection.slice(newSelection.length - 1),
|
selectedList: newSelection.slice(newSelection.length - 1),
|
||||||
});
|
});
|
||||||
@@ -467,6 +483,9 @@ export class DriveSelector extends React.Component<
|
|||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (this.props.onSelect) {
|
||||||
|
this.props.onSelect(row);
|
||||||
|
}
|
||||||
const index = selectedList.findIndex(
|
const index = selectedList.findIndex(
|
||||||
(d) => d.device === row.device,
|
(d) => d.device === row.device,
|
||||||
);
|
);
|
||||||
@@ -515,7 +534,7 @@ export class DriveSelector extends React.Component<
|
|||||||
if (missingDriversModal.drive !== undefined) {
|
if (missingDriversModal.drive !== undefined) {
|
||||||
openExternal(missingDriversModal.drive.link);
|
openExternal(missingDriversModal.drive.link);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
logException(error);
|
logException(error);
|
||||||
} finally {
|
} finally {
|
||||||
this.setState({ missingDriversModal: {} });
|
this.setState({ missingDriversModal: {} });
|
||||||
|
@@ -59,13 +59,8 @@ function FinishPage({ goToMain }: { goToMain: () => void }) {
|
|||||||
).map(([, error]: [string, FlashError]) => ({
|
).map(([, error]: [string, FlashError]) => ({
|
||||||
...error,
|
...error,
|
||||||
}));
|
}));
|
||||||
const {
|
const { averageSpeed, blockmappedSize, bytesWritten, failed, size } =
|
||||||
averageSpeed,
|
flashState.getFlashState();
|
||||||
blockmappedSize,
|
|
||||||
bytesWritten,
|
|
||||||
failed,
|
|
||||||
size,
|
|
||||||
} = flashState.getFlashState();
|
|
||||||
const {
|
const {
|
||||||
skip,
|
skip,
|
||||||
results = {
|
results = {
|
||||||
|
@@ -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 FileSvg from '@fortawesome/fontawesome-free/svgs/solid/file.svg';
|
||||||
import LinkSvg from '@fortawesome/fontawesome-free/svgs/solid/link.svg';
|
import LinkSvg from '@fortawesome/fontawesome-free/svgs/solid/link.svg';
|
||||||
import ExclamationTriangleSvg from '@fortawesome/fontawesome-free/svgs/solid/exclamation-triangle.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 { sourceDestination } from 'etcher-sdk';
|
||||||
import { ipcRenderer, IpcRendererEvent } from 'electron';
|
import { ipcRenderer, IpcRendererEvent } from 'electron';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
@@ -33,6 +35,7 @@ import {
|
|||||||
Card as BaseCard,
|
Card as BaseCard,
|
||||||
Input,
|
Input,
|
||||||
Spinner,
|
Spinner,
|
||||||
|
Link,
|
||||||
} from 'rendition';
|
} from 'rendition';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
@@ -61,6 +64,8 @@ import ImageSvg from '../../../assets/image.svg';
|
|||||||
import SrcSvg from '../../../assets/src.svg';
|
import SrcSvg from '../../../assets/src.svg';
|
||||||
import { DriveSelector } from '../drive-selector/drive-selector';
|
import { DriveSelector } from '../drive-selector/drive-selector';
|
||||||
import { DrivelistDrive } from '../../../../shared/drive-constraints';
|
import { DrivelistDrive } from '../../../../shared/drive-constraints';
|
||||||
|
import axios, { AxiosRequestConfig } from 'axios';
|
||||||
|
import { isJson } from '../../../../shared/utils';
|
||||||
|
|
||||||
const recentUrlImagesKey = 'recentUrlImages';
|
const recentUrlImagesKey = 'recentUrlImages';
|
||||||
|
|
||||||
@@ -72,7 +77,7 @@ function normalizeRecentUrlImages(urls: any[]): URL[] {
|
|||||||
.map((url) => {
|
.map((url) => {
|
||||||
try {
|
try {
|
||||||
return new URL(url);
|
return new URL(url);
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
// Invalid URL, skip
|
// Invalid URL, skip
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -133,12 +138,15 @@ const URLSelector = ({
|
|||||||
done,
|
done,
|
||||||
cancel,
|
cancel,
|
||||||
}: {
|
}: {
|
||||||
done: (imageURL: string) => void;
|
done: (imageURL: string, auth?: Authentication) => void;
|
||||||
cancel: () => void;
|
cancel: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const [imageURL, setImageURL] = React.useState('');
|
const [imageURL, setImageURL] = React.useState('');
|
||||||
const [recentImages, setRecentImages] = React.useState<URL[]>([]);
|
const [recentImages, setRecentImages] = React.useState<URL[]>([]);
|
||||||
const [loading, setLoading] = React.useState(false);
|
const [loading, setLoading] = React.useState(false);
|
||||||
|
const [showBasicAuth, setShowBasicAuth] = React.useState(false);
|
||||||
|
const [username, setUsername] = React.useState('');
|
||||||
|
const [password, setPassword] = React.useState('');
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const fetchRecentUrlImages = async () => {
|
const fetchRecentUrlImages = async () => {
|
||||||
const recentUrlImages: URL[] = await getRecentUrlImages();
|
const recentUrlImages: URL[] = await getRecentUrlImages();
|
||||||
@@ -161,11 +169,12 @@ const URLSelector = ({
|
|||||||
imageURL,
|
imageURL,
|
||||||
]);
|
]);
|
||||||
setRecentUrlImages(normalizedRecentUrls);
|
setRecentUrlImages(normalizedRecentUrls);
|
||||||
await done(imageURL);
|
const auth = username ? { username, password } : undefined;
|
||||||
|
await done(imageURL, auth);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Flex flexDirection="column">
|
<Flex flexDirection="column">
|
||||||
<Flex style={{ width: '100%' }} flexDirection="column">
|
<Flex mb={15} style={{ width: '100%' }} flexDirection="column">
|
||||||
<Txt mb="10px" fontSize="24px">
|
<Txt mb="10px" fontSize="24px">
|
||||||
Use Image URL
|
Use Image URL
|
||||||
</Txt>
|
</Txt>
|
||||||
@@ -177,6 +186,49 @@ const URLSelector = ({
|
|||||||
setImageURL(evt.target.value)
|
setImageURL(evt.target.value)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
<Link
|
||||||
|
mt={15}
|
||||||
|
mb={15}
|
||||||
|
fontSize="14px"
|
||||||
|
onClick={() => {
|
||||||
|
if (showBasicAuth) {
|
||||||
|
setUsername('');
|
||||||
|
setPassword('');
|
||||||
|
}
|
||||||
|
setShowBasicAuth(!showBasicAuth);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Flex alignItems="center">
|
||||||
|
{showBasicAuth && (
|
||||||
|
<ChevronDownSvg height="1em" fill="currentColor" />
|
||||||
|
)}
|
||||||
|
{!showBasicAuth && (
|
||||||
|
<ChevronRightSvg height="1em" fill="currentColor" />
|
||||||
|
)}
|
||||||
|
<Txt ml={8}>Authentication</Txt>
|
||||||
|
</Flex>
|
||||||
|
</Link>
|
||||||
|
{showBasicAuth && (
|
||||||
|
<React.Fragment>
|
||||||
|
<Input
|
||||||
|
mb={15}
|
||||||
|
value={username}
|
||||||
|
placeholder="Enter username"
|
||||||
|
type="text"
|
||||||
|
onChange={(evt: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
|
setUsername(evt.target.value)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
value={password}
|
||||||
|
placeholder="Enter password"
|
||||||
|
type="password"
|
||||||
|
onChange={(evt: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
|
setPassword(evt.target.value)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</React.Fragment>
|
||||||
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
{recentImages.length > 0 && (
|
{recentImages.length > 0 && (
|
||||||
<Flex flexDirection="column" height="78.6%">
|
<Flex flexDirection="column" height="78.6%">
|
||||||
@@ -279,6 +331,12 @@ interface SourceSelectorState {
|
|||||||
showDriveSelector: boolean;
|
showDriveSelector: boolean;
|
||||||
defaultFlowActive: boolean;
|
defaultFlowActive: boolean;
|
||||||
imageSelectorOpen: boolean;
|
imageSelectorOpen: boolean;
|
||||||
|
imageLoading: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Authentication {
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SourceSelector extends React.Component<
|
export class SourceSelector extends React.Component<
|
||||||
@@ -297,6 +355,7 @@ export class SourceSelector extends React.Component<
|
|||||||
showDriveSelector: false,
|
showDriveSelector: false,
|
||||||
defaultFlowActive: true,
|
defaultFlowActive: true,
|
||||||
imageSelectorOpen: false,
|
imageSelectorOpen: false,
|
||||||
|
imageLoading: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Bind `this` since it's used in an event's callback
|
// Bind `this` since it's used in an event's callback
|
||||||
@@ -317,25 +376,52 @@ export class SourceSelector extends React.Component<
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async onSelectImage(_event: IpcRendererEvent, imagePath: string) {
|
private async onSelectImage(_event: IpcRendererEvent, imagePath: string) {
|
||||||
|
this.setState({ imageLoading: true });
|
||||||
await this.selectSource(
|
await this.selectSource(
|
||||||
imagePath,
|
imagePath,
|
||||||
isURL(imagePath) ? sourceDestination.Http : sourceDestination.File,
|
isURL(this.normalizeImagePath(imagePath))
|
||||||
|
? sourceDestination.Http
|
||||||
|
: sourceDestination.File,
|
||||||
).promise;
|
).promise;
|
||||||
|
this.setState({ imageLoading: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createSource(selected: string, SourceType: Source) {
|
private async createSource(
|
||||||
|
selected: string,
|
||||||
|
SourceType: Source,
|
||||||
|
auth?: Authentication,
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
selected = await replaceWindowsNetworkDriveLetter(selected);
|
selected = await replaceWindowsNetworkDriveLetter(selected);
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
analytics.logException(error);
|
analytics.logException(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isJson(decodeURIComponent(selected))) {
|
||||||
|
const config: AxiosRequestConfig = JSON.parse(
|
||||||
|
decodeURIComponent(selected),
|
||||||
|
);
|
||||||
|
return new sourceDestination.Http({
|
||||||
|
url: config.url!,
|
||||||
|
axiosInstance: axios.create(_.omit(config, ['url'])),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (SourceType === sourceDestination.File) {
|
if (SourceType === sourceDestination.File) {
|
||||||
return new sourceDestination.File({
|
return new sourceDestination.File({
|
||||||
path: selected,
|
path: selected,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return new sourceDestination.Http({ url: selected });
|
|
||||||
|
return new sourceDestination.Http({ url: selected, auth });
|
||||||
|
}
|
||||||
|
|
||||||
|
public normalizeImagePath(imgPath: string) {
|
||||||
|
const decodedPath = decodeURIComponent(imgPath);
|
||||||
|
if (isJson(decodedPath)) {
|
||||||
|
return JSON.parse(decodedPath).url ?? decodedPath;
|
||||||
|
}
|
||||||
|
return decodedPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
private reselectSource() {
|
private reselectSource() {
|
||||||
@@ -349,6 +435,7 @@ export class SourceSelector extends React.Component<
|
|||||||
private selectSource(
|
private selectSource(
|
||||||
selected: string | DrivelistDrive,
|
selected: string | DrivelistDrive,
|
||||||
SourceType: Source,
|
SourceType: Source,
|
||||||
|
auth?: Authentication,
|
||||||
): { promise: Promise<void>; cancel: () => void } {
|
): { promise: Promise<void>; cancel: () => void } {
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
return {
|
return {
|
||||||
@@ -360,7 +447,10 @@ export class SourceSelector extends React.Component<
|
|||||||
let source;
|
let source;
|
||||||
let metadata: SourceMetadata | undefined;
|
let metadata: SourceMetadata | undefined;
|
||||||
if (isString(selected)) {
|
if (isString(selected)) {
|
||||||
if (SourceType === sourceDestination.Http && !isURL(selected)) {
|
if (
|
||||||
|
SourceType === sourceDestination.Http &&
|
||||||
|
!isURL(this.normalizeImagePath(selected))
|
||||||
|
) {
|
||||||
this.handleError(
|
this.handleError(
|
||||||
'Unsupported protocol',
|
'Unsupported protocol',
|
||||||
selected,
|
selected,
|
||||||
@@ -378,7 +468,7 @@ export class SourceSelector extends React.Component<
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
source = await this.createSource(selected, SourceType);
|
source = await this.createSource(selected, SourceType, auth);
|
||||||
|
|
||||||
if (cancelled) {
|
if (cancelled) {
|
||||||
return;
|
return;
|
||||||
@@ -395,7 +485,7 @@ export class SourceSelector extends React.Component<
|
|||||||
}
|
}
|
||||||
metadata.SourceType = SourceType;
|
metadata.SourceType = SourceType;
|
||||||
|
|
||||||
if (!metadata.hasMBR) {
|
if (!metadata.hasMBR && this.state.warning === null) {
|
||||||
analytics.logEvent('Missing partition table', { metadata });
|
analytics.logEvent('Missing partition table', { metadata });
|
||||||
this.setState({
|
this.setState({
|
||||||
warning: {
|
warning: {
|
||||||
@@ -404,7 +494,7 @@ export class SourceSelector extends React.Component<
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
this.handleError(
|
this.handleError(
|
||||||
'Error opening source',
|
'Error opening source',
|
||||||
sourcePath,
|
sourcePath,
|
||||||
@@ -414,7 +504,7 @@ export class SourceSelector extends React.Component<
|
|||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
await source.close();
|
await source.close();
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
// Noop
|
// Noop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -504,7 +594,7 @@ export class SourceSelector extends React.Component<
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await this.selectSource(imagePath, sourceDestination.File).promise;
|
await this.selectSource(imagePath, sourceDestination.File).promise;
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
exceptionReporter.report(error);
|
exceptionReporter.report(error);
|
||||||
} finally {
|
} finally {
|
||||||
this.setState({ imageSelectorOpen: false });
|
this.setState({ imageSelectorOpen: false });
|
||||||
@@ -558,10 +648,21 @@ export class SourceSelector extends React.Component<
|
|||||||
this.setState({ defaultFlowActive });
|
this.setState({ defaultFlowActive });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private closeModal() {
|
||||||
|
this.setState({
|
||||||
|
showDriveSelector: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// TODO add a visual change when dragging a file over the selector
|
// TODO add a visual change when dragging a file over the selector
|
||||||
public render() {
|
public render() {
|
||||||
const { flashing } = this.props;
|
const { flashing } = this.props;
|
||||||
const { showImageDetails, showURLSelector, showDriveSelector } = this.state;
|
const {
|
||||||
|
showImageDetails,
|
||||||
|
showURLSelector,
|
||||||
|
showDriveSelector,
|
||||||
|
imageLoading,
|
||||||
|
} = this.state;
|
||||||
const selectionImage = selectionState.getImage();
|
const selectionImage = selectionState.getImage();
|
||||||
let image: SourceMetadata | DrivelistDrive =
|
let image: SourceMetadata | DrivelistDrive =
|
||||||
selectionImage !== undefined ? selectionImage : ({} as SourceMetadata);
|
selectionImage !== undefined ? selectionImage : ({} as SourceMetadata);
|
||||||
@@ -599,16 +700,18 @@ export class SourceSelector extends React.Component<
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{selectionImage !== undefined ? (
|
{selectionImage !== undefined || imageLoading ? (
|
||||||
<>
|
<>
|
||||||
<StepNameButton
|
<StepNameButton
|
||||||
plain
|
plain
|
||||||
onClick={() => this.showSelectedImageDetails()}
|
onClick={() => this.showSelectedImageDetails()}
|
||||||
tooltip={imageName || imageBasename}
|
tooltip={imageName || imageBasename}
|
||||||
>
|
>
|
||||||
{middleEllipsis(imageName || imageBasename, 20)}
|
<Spinner show={imageLoading}>
|
||||||
|
{middleEllipsis(imageName || imageBasename, 20)}
|
||||||
|
</Spinner>
|
||||||
</StepNameButton>
|
</StepNameButton>
|
||||||
{!flashing && (
|
{!flashing && !imageLoading && (
|
||||||
<ChangeButton
|
<ChangeButton
|
||||||
plain
|
plain
|
||||||
mb={14}
|
mb={14}
|
||||||
@@ -617,7 +720,7 @@ export class SourceSelector extends React.Component<
|
|||||||
Remove
|
Remove
|
||||||
</ChangeButton>
|
</ChangeButton>
|
||||||
)}
|
)}
|
||||||
{!_.isNil(imageSize) && (
|
{!_.isNil(imageSize) && !imageLoading && (
|
||||||
<DetailsText>{prettyBytes(imageSize)}</DetailsText>
|
<DetailsText>{prettyBytes(imageSize)}</DetailsText>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
@@ -661,6 +764,9 @@ export class SourceSelector extends React.Component<
|
|||||||
|
|
||||||
{this.state.warning != null && (
|
{this.state.warning != null && (
|
||||||
<SmallModal
|
<SmallModal
|
||||||
|
style={{
|
||||||
|
boxShadow: '0 3px 7px rgba(0, 0, 0, 0.3)',
|
||||||
|
}}
|
||||||
titleElement={
|
titleElement={
|
||||||
<span>
|
<span>
|
||||||
<ExclamationTriangleSvg fill="#fca321" height="1em" />{' '}
|
<ExclamationTriangleSvg fill="#fca321" height="1em" />{' '}
|
||||||
@@ -709,7 +815,7 @@ export class SourceSelector extends React.Component<
|
|||||||
showURLSelector: false,
|
showURLSelector: false,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
done={async (imageURL: string) => {
|
done={async (imageURL: string, auth?: Authentication) => {
|
||||||
// Avoid analytics and selection state changes
|
// Avoid analytics and selection state changes
|
||||||
// if no file was resolved from the dialog.
|
// if no file was resolved from the dialog.
|
||||||
if (!imageURL) {
|
if (!imageURL) {
|
||||||
@@ -719,6 +825,7 @@ export class SourceSelector extends React.Component<
|
|||||||
({ promise, cancel: cancelURLSelection } = this.selectSource(
|
({ promise, cancel: cancelURLSelection } = this.selectSource(
|
||||||
imageURL,
|
imageURL,
|
||||||
sourceDestination.Http,
|
sourceDestination.Http,
|
||||||
|
auth,
|
||||||
));
|
));
|
||||||
await promise;
|
await promise;
|
||||||
}
|
}
|
||||||
@@ -736,21 +843,30 @@ export class SourceSelector extends React.Component<
|
|||||||
titleLabel="Select source"
|
titleLabel="Select source"
|
||||||
emptyListLabel="Plug a source drive"
|
emptyListLabel="Plug a source drive"
|
||||||
emptyListIcon={<SrcSvg width="40px" />}
|
emptyListIcon={<SrcSvg width="40px" />}
|
||||||
cancel={() => {
|
cancel={(originalList) => {
|
||||||
this.setState({
|
if (originalList.length) {
|
||||||
showDriveSelector: false,
|
const originalSource = originalList[0];
|
||||||
});
|
if (selectionImage?.drive?.device !== originalSource.device) {
|
||||||
}}
|
this.selectSource(
|
||||||
done={async (drives: DrivelistDrive[]) => {
|
originalSource,
|
||||||
if (drives.length) {
|
sourceDestination.BlockDevice,
|
||||||
await this.selectSource(
|
);
|
||||||
drives[0],
|
}
|
||||||
sourceDestination.BlockDevice,
|
} else {
|
||||||
);
|
selectionState.deselectImage();
|
||||||
|
}
|
||||||
|
this.closeModal();
|
||||||
|
}}
|
||||||
|
done={() => this.closeModal()}
|
||||||
|
onSelect={(drive) => {
|
||||||
|
if (drive) {
|
||||||
|
if (
|
||||||
|
selectionState.getImage()?.drive?.device === drive?.device
|
||||||
|
) {
|
||||||
|
return selectionState.deselectImage();
|
||||||
|
}
|
||||||
|
this.selectSource(drive, sourceDestination.BlockDevice);
|
||||||
}
|
}
|
||||||
this.setState({
|
|
||||||
showDriveSelector: false,
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@@ -14,7 +14,6 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { scanner } from 'etcher-sdk';
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Flex, Txt } from 'rendition';
|
import { Flex, Txt } from 'rendition';
|
||||||
|
|
||||||
@@ -28,6 +27,7 @@ import {
|
|||||||
getSelectedDrives,
|
getSelectedDrives,
|
||||||
deselectDrive,
|
deselectDrive,
|
||||||
selectDrive,
|
selectDrive,
|
||||||
|
deselectAllDrives,
|
||||||
} from '../../models/selection-state';
|
} from '../../models/selection-state';
|
||||||
import { observe } from '../../models/store';
|
import { observe } from '../../models/store';
|
||||||
import * as analytics from '../../modules/analytics';
|
import * as analytics from '../../modules/analytics';
|
||||||
@@ -36,6 +36,7 @@ import { TargetSelectorButton } from './target-selector-button';
|
|||||||
import TgtSvg from '../../../assets/tgt.svg';
|
import TgtSvg from '../../../assets/tgt.svg';
|
||||||
import DriveSvg from '../../../assets/drive.svg';
|
import DriveSvg from '../../../assets/drive.svg';
|
||||||
import { warning } from '../../../../shared/messages';
|
import { warning } from '../../../../shared/messages';
|
||||||
|
import { DrivelistDrive } from '../../../../shared/drive-constraints';
|
||||||
|
|
||||||
export const getDriveListLabel = () => {
|
export const getDriveListLabel = () => {
|
||||||
return getSelectedDrives()
|
return getSelectedDrives()
|
||||||
@@ -69,9 +70,7 @@ export const TargetSelectorModal = (
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const selectAllTargets = (
|
export const selectAllTargets = (modalTargets: DrivelistDrive[]) => {
|
||||||
modalTargets: scanner.adapters.DrivelistDrive[],
|
|
||||||
) => {
|
|
||||||
const selectedDrivesFromState = getSelectedDrives();
|
const selectedDrivesFromState = getSelectedDrives();
|
||||||
const deselected = selectedDrivesFromState.filter(
|
const deselected = selectedDrivesFromState.filter(
|
||||||
(drive) =>
|
(drive) =>
|
||||||
@@ -113,9 +112,8 @@ export const TargetSelector = ({
|
|||||||
const [{ driveListLabel, targets }, setStateSlice] = React.useState(
|
const [{ driveListLabel, targets }, setStateSlice] = React.useState(
|
||||||
getDriveSelectionStateSlice(),
|
getDriveSelectionStateSlice(),
|
||||||
);
|
);
|
||||||
const [showTargetSelectorModal, setShowTargetSelectorModal] = React.useState(
|
const [showTargetSelectorModal, setShowTargetSelectorModal] =
|
||||||
false,
|
React.useState(false);
|
||||||
);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
return observe(() => {
|
return observe(() => {
|
||||||
@@ -164,11 +162,30 @@ export const TargetSelector = ({
|
|||||||
{showTargetSelectorModal && (
|
{showTargetSelectorModal && (
|
||||||
<TargetSelectorModal
|
<TargetSelectorModal
|
||||||
write={true}
|
write={true}
|
||||||
cancel={() => setShowTargetSelectorModal(false)}
|
cancel={(originalList) => {
|
||||||
done={(modalTargets) => {
|
if (originalList.length) {
|
||||||
selectAllTargets(modalTargets);
|
selectAllTargets(originalList);
|
||||||
|
} else {
|
||||||
|
deselectAllDrives();
|
||||||
|
}
|
||||||
setShowTargetSelectorModal(false);
|
setShowTargetSelectorModal(false);
|
||||||
}}
|
}}
|
||||||
|
done={(modalTargets) => {
|
||||||
|
if (modalTargets.length === 0) {
|
||||||
|
deselectAllDrives();
|
||||||
|
}
|
||||||
|
setShowTargetSelectorModal(false);
|
||||||
|
}}
|
||||||
|
onSelect={(drive) => {
|
||||||
|
if (
|
||||||
|
getSelectedDrives().find(
|
||||||
|
(selectedDrive) => selectedDrive.device === drive.device,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return deselectDrive(drive.device);
|
||||||
|
}
|
||||||
|
selectDrive(drive.device);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@@ -17,6 +17,7 @@
|
|||||||
import * as electron from 'electron';
|
import * as electron from 'electron';
|
||||||
import * as sdk from 'etcher-sdk';
|
import * as sdk from 'etcher-sdk';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
import { DrivelistDrive } from '../../../shared/drive-constraints';
|
||||||
|
|
||||||
import { bytesToMegabytes } from '../../../shared/units';
|
import { bytesToMegabytes } from '../../../shared/units';
|
||||||
import { Actions, store } from './store';
|
import { Actions, store } from './store';
|
||||||
@@ -84,7 +85,7 @@ export function addFailedDeviceError({
|
|||||||
device,
|
device,
|
||||||
error,
|
error,
|
||||||
}: {
|
}: {
|
||||||
device: sdk.scanner.adapters.DrivelistDrive;
|
device: DrivelistDrive;
|
||||||
error: Error;
|
error: Error;
|
||||||
}) {
|
}) {
|
||||||
const failedDeviceErrorsMap = new Map(
|
const failedDeviceErrorsMap = new Map(
|
||||||
|
@@ -18,29 +18,24 @@ import * as _ from 'lodash';
|
|||||||
import { Animator, AnimationFunction, Color, RGBLed } from 'sys-class-rgb-led';
|
import { Animator, AnimationFunction, Color, RGBLed } from 'sys-class-rgb-led';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
isSourceDrive,
|
|
||||||
DrivelistDrive,
|
DrivelistDrive,
|
||||||
|
isSourceDrive,
|
||||||
} from '../../../shared/drive-constraints';
|
} from '../../../shared/drive-constraints';
|
||||||
|
import { getDrives } from './available-drives';
|
||||||
|
import { getSelectedDrives } from './selection-state';
|
||||||
import * as settings from './settings';
|
import * as settings from './settings';
|
||||||
import { DEFAULT_STATE, observe } from './store';
|
import { observe, store } from './store';
|
||||||
|
|
||||||
const leds: Map<string, RGBLed> = new Map();
|
const leds: Map<string, RGBLed> = new Map();
|
||||||
const animator = new Animator([], 10);
|
const animator = new Animator([], 10);
|
||||||
|
|
||||||
const red: Color = [0.59, 0, 0];
|
|
||||||
const green: Color = [0, 0.59, 0];
|
|
||||||
const blue: Color = [0, 0, 0.59];
|
|
||||||
const white: Color = [0.04, 0.04, 0.04];
|
|
||||||
const black: Color = [0, 0, 0];
|
|
||||||
const purple: Color = [0.117, 0, 0.196];
|
|
||||||
|
|
||||||
function createAnimationFunction(
|
function createAnimationFunction(
|
||||||
intensityFunction: (t: number) => number,
|
intensityFunction: (t: number) => number,
|
||||||
color: Color,
|
color: Color,
|
||||||
): AnimationFunction {
|
): AnimationFunction {
|
||||||
return (t: number): Color => {
|
return (t: number): Color => {
|
||||||
const intensity = intensityFunction(t);
|
const intensity = intensityFunction(t);
|
||||||
return color.map((v) => v * intensity) as Color;
|
return color.map((v: number) => v * intensity) as Color;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,13 +47,27 @@ function one(_t: number) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const blinkGreen = createAnimationFunction(blink, green);
|
type LEDColors = {
|
||||||
const blinkPurple = createAnimationFunction(blink, purple);
|
green: Color;
|
||||||
const staticRed = createAnimationFunction(one, red);
|
purple: Color;
|
||||||
const staticGreen = createAnimationFunction(one, green);
|
red: Color;
|
||||||
const staticBlue = createAnimationFunction(one, blue);
|
blue: Color;
|
||||||
const staticWhite = createAnimationFunction(one, white);
|
white: Color;
|
||||||
const staticBlack = createAnimationFunction(one, black);
|
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 {
|
interface LedsState {
|
||||||
step: 'main' | 'flashing' | 'verifying' | 'finish';
|
step: 'main' | 'flashing' | 'verifying' | 'finish';
|
||||||
@@ -130,65 +139,77 @@ export function updateLeds({
|
|||||||
if (sourceDrive !== undefined) {
|
if (sourceDrive !== undefined) {
|
||||||
if (plugged.has(sourceDrive)) {
|
if (plugged.has(sourceDrive)) {
|
||||||
plugged.delete(sourceDrive);
|
plugged.delete(sourceDrive);
|
||||||
mapping.push(setLeds(staticBlue, new Set([sourceDrive])));
|
mapping.push(
|
||||||
|
setLeds(ledAnimationFunctions.staticBlue, new Set([sourceDrive])),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (step === 'main') {
|
if (step === 'main') {
|
||||||
mapping.push(
|
mapping.push(
|
||||||
setLeds(staticBlack, new Set([...unplugged, ...plugged])),
|
setLeds(
|
||||||
setLeds(staticWhite, new Set([...selectedOk, ...selectedFailed])),
|
ledAnimationFunctions.staticBlack,
|
||||||
|
new Set([...unplugged, ...plugged]),
|
||||||
|
),
|
||||||
|
setLeds(
|
||||||
|
ledAnimationFunctions.staticWhite,
|
||||||
|
new Set([...selectedOk, ...selectedFailed]),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
} else if (step === 'flashing') {
|
} else if (step === 'flashing') {
|
||||||
mapping.push(
|
mapping.push(
|
||||||
setLeds(staticBlack, new Set([...unplugged, ...plugged])),
|
setLeds(
|
||||||
setLeds(blinkPurple, selectedOk),
|
ledAnimationFunctions.staticBlack,
|
||||||
setLeds(staticRed, selectedFailed),
|
new Set([...unplugged, ...plugged]),
|
||||||
|
),
|
||||||
|
setLeds(ledAnimationFunctions.blinkPurple, selectedOk),
|
||||||
|
setLeds(ledAnimationFunctions.staticRed, selectedFailed),
|
||||||
);
|
);
|
||||||
} else if (step === 'verifying') {
|
} else if (step === 'verifying') {
|
||||||
mapping.push(
|
mapping.push(
|
||||||
setLeds(staticBlack, new Set([...unplugged, ...plugged])),
|
setLeds(
|
||||||
setLeds(blinkGreen, selectedOk),
|
ledAnimationFunctions.staticBlack,
|
||||||
setLeds(staticRed, selectedFailed),
|
new Set([...unplugged, ...plugged]),
|
||||||
|
),
|
||||||
|
setLeds(ledAnimationFunctions.blinkGreen, selectedOk),
|
||||||
|
setLeds(ledAnimationFunctions.staticRed, selectedFailed),
|
||||||
);
|
);
|
||||||
} else if (step === 'finish') {
|
} else if (step === 'finish') {
|
||||||
mapping.push(
|
mapping.push(
|
||||||
setLeds(staticBlack, new Set([...unplugged, ...plugged])),
|
setLeds(
|
||||||
setLeds(staticGreen, selectedOk),
|
ledAnimationFunctions.staticBlack,
|
||||||
setLeds(staticRed, selectedFailed),
|
new Set([...unplugged, ...plugged]),
|
||||||
|
),
|
||||||
|
setLeds(ledAnimationFunctions.staticGreen, selectedOk),
|
||||||
|
setLeds(ledAnimationFunctions.staticRed, selectedFailed),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
animator.mapping = mapping;
|
animator.mapping = mapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DeviceFromState {
|
|
||||||
devicePath?: string;
|
|
||||||
device: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
let ledsState: LedsState | undefined;
|
let ledsState: LedsState | undefined;
|
||||||
|
|
||||||
function stateObserver(state: typeof DEFAULT_STATE) {
|
function stateObserver() {
|
||||||
const s = state.toJS();
|
const s = store.getState().toJS();
|
||||||
let step: 'main' | 'flashing' | 'verifying' | 'finish';
|
let step: 'main' | 'flashing' | 'verifying' | 'finish';
|
||||||
if (s.isFlashing) {
|
if (s.isFlashing) {
|
||||||
step = s.flashState.type;
|
step = s.flashState.type;
|
||||||
} else {
|
} else {
|
||||||
step = s.lastAverageFlashingSpeed == null ? 'main' : 'finish';
|
step = s.lastAverageFlashingSpeed == null ? 'main' : 'finish';
|
||||||
}
|
}
|
||||||
const availableDrives = s.availableDrives.filter(
|
const availableDrives = getDrives().filter(
|
||||||
(d: DeviceFromState) => d.devicePath,
|
(d: DrivelistDrive) => d.devicePath,
|
||||||
);
|
);
|
||||||
const sourceDrivePath = availableDrives.filter((d: DrivelistDrive) =>
|
const sourceDrivePath = availableDrives.filter((d: DrivelistDrive) =>
|
||||||
isSourceDrive(d, s.selection.image),
|
isSourceDrive(d, s.selection.image),
|
||||||
)[0]?.devicePath;
|
)[0]?.devicePath;
|
||||||
const availableDrivesPaths = availableDrives.map(
|
const availableDrivesPaths = availableDrives.map(
|
||||||
(d: DeviceFromState) => d.devicePath,
|
(d: DrivelistDrive) => d.devicePath,
|
||||||
);
|
);
|
||||||
let selectedDrivesPaths: string[];
|
let selectedDrivesPaths: string[];
|
||||||
if (step === 'main') {
|
if (step === 'main') {
|
||||||
selectedDrivesPaths = availableDrives
|
selectedDrivesPaths = getSelectedDrives()
|
||||||
.filter((d: DrivelistDrive) => s.selection.devices.includes(d.device))
|
.filter((drive) => drive.devicePath !== null)
|
||||||
.map((d: DrivelistDrive) => d.devicePath);
|
.map((drive) => drive.devicePath) as string[];
|
||||||
} else {
|
} else {
|
||||||
selectedDrivesPaths = s.devicePaths;
|
selectedDrivesPaths = s.devicePaths;
|
||||||
}
|
}
|
||||||
@@ -201,7 +222,7 @@ function stateObserver(state: typeof DEFAULT_STATE) {
|
|||||||
availableDrives: availableDrivesPaths,
|
availableDrives: availableDrivesPaths,
|
||||||
selectedDrives: selectedDrivesPaths,
|
selectedDrives: selectedDrivesPaths,
|
||||||
failedDrives: failedDevicePaths,
|
failedDrives: failedDevicePaths,
|
||||||
};
|
} as LedsState;
|
||||||
if (!_.isEqual(newLedsState, ledsState)) {
|
if (!_.isEqual(newLedsState, ledsState)) {
|
||||||
updateLeds(newLedsState);
|
updateLeds(newLedsState);
|
||||||
ledsState = newLedsState;
|
ledsState = newLedsState;
|
||||||
@@ -224,6 +245,16 @@ export async function init(): Promise<void> {
|
|||||||
for (const [drivePath, ledsNames] of Object.entries(ledsMapping)) {
|
for (const [drivePath, ledsNames] of Object.entries(ledsMapping)) {
|
||||||
leds.set('/dev/disk/by-path/' + drivePath, new RGBLed(ledsNames));
|
leds.set('/dev/disk/by-path/' + drivePath, new RGBLed(ledsNames));
|
||||||
}
|
}
|
||||||
|
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 }));
|
observe(_.debounce(stateObserver, 1000, { maxWait: 1000 }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -51,7 +51,7 @@ async function readConfigFile(filename: string): Promise<_.Dictionary<any>> {
|
|||||||
let contents = '{}';
|
let contents = '{}';
|
||||||
try {
|
try {
|
||||||
contents = await fs.readFile(filename, { encoding: 'utf8' });
|
contents = await fs.readFile(filename, { encoding: 'utf8' });
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
// noop
|
// noop
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -104,7 +104,7 @@ export async function set(
|
|||||||
settings[key] = value;
|
settings[key] = value;
|
||||||
try {
|
try {
|
||||||
await writeConfigFileFn(CONFIG_PATH, settings);
|
await writeConfigFileFn(CONFIG_PATH, settings);
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
// Revert to previous value if persisting settings failed
|
// Revert to previous value if persisting settings failed
|
||||||
settings[key] = previousValue;
|
settings[key] = previousValue;
|
||||||
throw error;
|
throw error;
|
||||||
|
@@ -102,10 +102,9 @@ function validateMixpanelConfig(config: {
|
|||||||
* This function sends the debug message to product analytics services.
|
* This function sends the debug message to product analytics services.
|
||||||
*/
|
*/
|
||||||
export function logEvent(message: string, data: _.Dictionary<any> = {}) {
|
export function logEvent(message: string, data: _.Dictionary<any> = {}) {
|
||||||
const {
|
const { applicationSessionUuid, flashingWorkflowUuid } = store
|
||||||
applicationSessionUuid,
|
.getState()
|
||||||
flashingWorkflowUuid,
|
.toJS();
|
||||||
} = store.getState().toJS();
|
|
||||||
resinCorvus.logEvent(message, {
|
resinCorvus.logEvent(message, {
|
||||||
...data,
|
...data,
|
||||||
sample: mixpanelSample,
|
sample: mixpanelSample,
|
||||||
|
@@ -15,10 +15,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import * as sdk from 'etcher-sdk';
|
import * as sdk from 'etcher-sdk';
|
||||||
|
import {
|
||||||
|
Adapter,
|
||||||
|
BlockDeviceAdapter,
|
||||||
|
UsbbootDeviceAdapter,
|
||||||
|
} from 'etcher-sdk/build/scanner/adapters';
|
||||||
import { geteuid, platform } from 'process';
|
import { geteuid, platform } from 'process';
|
||||||
|
|
||||||
const adapters: sdk.scanner.adapters.Adapter[] = [
|
const adapters: Adapter[] = [
|
||||||
new sdk.scanner.adapters.BlockDeviceAdapter({
|
new BlockDeviceAdapter({
|
||||||
includeSystemDrives: () => true,
|
includeSystemDrives: () => true,
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
@@ -26,14 +31,15 @@ const adapters: sdk.scanner.adapters.Adapter[] = [
|
|||||||
// Can't use permissions.isElevated() here as it returns a promise and we need to set
|
// Can't use permissions.isElevated() here as it returns a promise and we need to set
|
||||||
// module.exports = scanner right now.
|
// module.exports = scanner right now.
|
||||||
if (platform !== 'linux' || geteuid() === 0) {
|
if (platform !== 'linux' || geteuid() === 0) {
|
||||||
adapters.push(new sdk.scanner.adapters.UsbbootDeviceAdapter());
|
adapters.push(new UsbbootDeviceAdapter());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (platform === 'win32') {
|
||||||
platform === 'win32' &&
|
const {
|
||||||
sdk.scanner.adapters.DriverlessDeviceAdapter !== undefined
|
DriverlessDeviceAdapter: driverless,
|
||||||
) {
|
// tslint:disable-next-line:no-var-requires
|
||||||
adapters.push(new sdk.scanner.adapters.DriverlessDeviceAdapter());
|
} = require('etcher-sdk/build/scanner/adapters/driverless');
|
||||||
|
adapters.push(new driverless());
|
||||||
}
|
}
|
||||||
|
|
||||||
export const scanner = new sdk.scanner.Scanner(adapters);
|
export const scanner = new sdk.scanner.Scanner(adapters);
|
||||||
|
@@ -218,7 +218,7 @@ async function performWrite(
|
|||||||
});
|
});
|
||||||
flashResults.cancelled = cancelled || results.cancelled;
|
flashResults.cancelled = cancelled || results.cancelled;
|
||||||
flashResults.skip = skip;
|
flashResults.skip = skip;
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
// This happens when the child is killed using SIGKILL
|
// This happens when the child is killed using SIGKILL
|
||||||
const SIGKILL_EXIT_CODE = 137;
|
const SIGKILL_EXIT_CODE = 137;
|
||||||
if (error.code === SIGKILL_EXIT_CODE) {
|
if (error.code === SIGKILL_EXIT_CODE) {
|
||||||
@@ -287,7 +287,7 @@ export async function flash(
|
|||||||
try {
|
try {
|
||||||
const result = await write(image, drives, flashState.setProgressState);
|
const result = await write(image, drives, flashState.setProgressState);
|
||||||
await flashState.unsetFlashingFlag(result);
|
await flashState.unsetFlashingFlag(result);
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
await flashState.unsetFlashingFlag({
|
await flashState.unsetFlashingFlag({
|
||||||
cancelled: false,
|
cancelled: false,
|
||||||
errorCode: error.code,
|
errorCode: error.code,
|
||||||
@@ -349,7 +349,7 @@ export async function cancel(type: string) {
|
|||||||
if (socket !== undefined) {
|
if (socket !== undefined) {
|
||||||
ipc.server.emit(socket, status);
|
ipc.server.emit(socket, status);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
analytics.logException(error);
|
analytics.logException(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -27,7 +27,7 @@ async function mountSourceDrive() {
|
|||||||
if (sourceDrivePath) {
|
if (sourceDrivePath) {
|
||||||
try {
|
try {
|
||||||
await electron.ipcRenderer.invoke('mount-drive', sourceDrivePath);
|
await electron.ipcRenderer.invoke('mount-drive', sourceDrivePath);
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
// noop
|
// noop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -117,7 +117,7 @@ async function flashImageToDrive(
|
|||||||
}
|
}
|
||||||
goToSuccess();
|
goToSuccess();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
notifyFailure(iconPath, basename, drives);
|
notifyFailure(iconPath, basename, drives);
|
||||||
let errorMessage = getErrorMessageFromCode(error.code);
|
let errorMessage = getErrorMessageFromCode(error.code);
|
||||||
if (!errorMessage) {
|
if (!errorMessage) {
|
||||||
|
@@ -276,7 +276,7 @@ export class MainPage extends React.Component<
|
|||||||
style={{
|
style={{
|
||||||
// Allow window to be dragged from header
|
// Allow window to be dragged from header
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
'-webkit-app-region': 'drag',
|
WebkitAppRegion: 'drag',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
zIndex: 2,
|
zIndex: 2,
|
||||||
}}
|
}}
|
||||||
@@ -304,7 +304,7 @@ export class MainPage extends React.Component<
|
|||||||
onClick={() => this.setState({ hideSettings: false })}
|
onClick={() => this.setState({ hideSettings: false })}
|
||||||
style={{
|
style={{
|
||||||
// Make touch events click instead of dragging
|
// Make touch events click instead of dragging
|
||||||
'-webkit-app-region': 'no-drag',
|
WebkitAppRegion: 'no-drag',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{!settings.getSync('disableExternalLinks') && (
|
{!settings.getSync('disableExternalLinks') && (
|
||||||
@@ -319,7 +319,7 @@ export class MainPage extends React.Component<
|
|||||||
tabIndex={6}
|
tabIndex={6}
|
||||||
style={{
|
style={{
|
||||||
// Make touch events click instead of dragging
|
// Make touch events click instead of dragging
|
||||||
'-webkit-app-region': 'no-drag',
|
WebkitAppRegion: 'no-drag',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@@ -35,8 +35,10 @@ import { totalmem } from 'os';
|
|||||||
|
|
||||||
import { toJSON } from '../../shared/errors';
|
import { toJSON } from '../../shared/errors';
|
||||||
import { GENERAL_ERROR, SUCCESS } from '../../shared/exit-codes';
|
import { GENERAL_ERROR, SUCCESS } from '../../shared/exit-codes';
|
||||||
import { delay } from '../../shared/utils';
|
import { delay, isJson } from '../../shared/utils';
|
||||||
import { SourceMetadata } from '../app/components/source-selector/source-selector';
|
import { SourceMetadata } from '../app/components/source-selector/source-selector';
|
||||||
|
import axios from 'axios';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
ipc.config.id = process.env.IPC_CLIENT_ID as string;
|
ipc.config.id = process.env.IPC_CLIENT_ID as string;
|
||||||
ipc.config.socketRoot = process.env.IPC_SOCKET_ROOT as string;
|
ipc.config.socketRoot = process.env.IPC_SOCKET_ROOT as string;
|
||||||
@@ -171,6 +173,7 @@ interface WriteOptions {
|
|||||||
autoBlockmapping: boolean;
|
autoBlockmapping: boolean;
|
||||||
decompressFirst: boolean;
|
decompressFirst: boolean;
|
||||||
SourceType: string;
|
SourceType: string;
|
||||||
|
httpRequest?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
ipc.connectTo(IPC_SERVER_ID, () => {
|
ipc.connectTo(IPC_SERVER_ID, () => {
|
||||||
@@ -281,7 +284,17 @@ ipc.connectTo(IPC_SERVER_ID, () => {
|
|||||||
path: imagePath,
|
path: imagePath,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
source = new Http({ url: imagePath, avoidRandomAccess: true });
|
const decodedImagePath = decodeURIComponent(imagePath);
|
||||||
|
if (isJson(decodedImagePath)) {
|
||||||
|
const imagePathObject = JSON.parse(decodedImagePath);
|
||||||
|
source = new Http({
|
||||||
|
url: imagePathObject.url,
|
||||||
|
avoidRandomAccess: true,
|
||||||
|
axiosInstance: axios.create(_.omit(imagePathObject, ['url'])),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
source = new Http({ url: imagePath, avoidRandomAccess: true });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const results = await writeAndValidate({
|
const results = await writeAndValidate({
|
||||||
@@ -300,7 +313,7 @@ ipc.connectTo(IPC_SERVER_ID, () => {
|
|||||||
ipc.of[IPC_SERVER_ID].emit('done', { results });
|
ipc.of[IPC_SERVER_ID].emit('done', { results });
|
||||||
await delay(DISCONNECT_DELAY);
|
await delay(DISCONNECT_DELAY);
|
||||||
await terminate(exitCode);
|
await terminate(exitCode);
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
exitCode = GENERAL_ERROR;
|
exitCode = GENERAL_ERROR;
|
||||||
ipc.of[IPC_SERVER_ID].emit('error', toJSON(error));
|
ipc.of[IPC_SERVER_ID].emit('error', toJSON(error));
|
||||||
}
|
}
|
||||||
|
@@ -50,7 +50,7 @@ export async function sudo(
|
|||||||
stdout: stdout.slice(EXPECTED_SUCCESSFUL_AUTH_MARKER.length),
|
stdout: stdout.slice(EXPECTED_SUCCESSFUL_AUTH_MARKER.length),
|
||||||
stderr,
|
stderr,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
if (error.code === 1) {
|
if (error.code === 1) {
|
||||||
if (!error.stdout.startsWith(EXPECTED_SUCCESSFUL_AUTH_MARKER)) {
|
if (!error.stdout.startsWith(EXPECTED_SUCCESSFUL_AUTH_MARKER)) {
|
||||||
return { cancelled: true };
|
return { cancelled: true };
|
||||||
|
@@ -104,24 +104,19 @@ export function isDriveLargeEnough(
|
|||||||
return driveSize >= (image.size || UNKNOWN_SIZE);
|
return driveSize >= (image.size || UNKNOWN_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary Check if a drive is disabled (i.e. not ready for selection)
|
|
||||||
*/
|
|
||||||
export function isDriveDisabled(drive: DrivelistDrive): boolean {
|
|
||||||
return drive.disabled || false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Check if a drive is valid, i.e. large enough for an image
|
* @summary Check if a drive is valid, i.e. large enough for an image
|
||||||
*/
|
*/
|
||||||
export function isDriveValid(
|
export function isDriveValid(
|
||||||
drive: DrivelistDrive,
|
drive: DrivelistDrive,
|
||||||
image?: SourceMetadata,
|
image?: SourceMetadata,
|
||||||
|
write: boolean = true,
|
||||||
): boolean {
|
): boolean {
|
||||||
return (
|
return (
|
||||||
isDriveLargeEnough(drive, image) &&
|
!write ||
|
||||||
!isSourceDrive(drive, image as SourceMetadata) &&
|
(!drive.disabled &&
|
||||||
!isDriveDisabled(drive)
|
isDriveLargeEnough(drive, image) &&
|
||||||
|
!isSourceDrive(drive, image as SourceMetadata))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -60,7 +60,7 @@ export async function isElevated(): Promise<boolean> {
|
|||||||
// See http://stackoverflow.com/a/28268802
|
// See http://stackoverflow.com/a/28268802
|
||||||
try {
|
try {
|
||||||
await execAsync('fltmc');
|
await execAsync('fltmc');
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
if (error.code === os.constants.errno.EPERM) {
|
if (error.code === os.constants.errno.EPERM) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -146,7 +146,7 @@ async function elevateScriptCatalina(
|
|||||||
try {
|
try {
|
||||||
const { cancelled } = await catalinaSudo(cmd);
|
const { cancelled } = await catalinaSudo(cmd);
|
||||||
return { cancelled };
|
return { cancelled };
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
throw errors.createError({ title: error.stderr });
|
throw errors.createError({ title: error.stderr });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -190,7 +190,7 @@ export async function elevateCommand(
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return await elevateScriptUnix(path, options.applicationName);
|
return await elevateScriptUnix(path, options.applicationName);
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
// We're hardcoding internal error messages declared by `sudo-prompt`.
|
// We're hardcoding internal error messages declared by `sudo-prompt`.
|
||||||
// There doesn't seem to be a better way to handle these errors, so
|
// There doesn't seem to be a better way to handle these errors, so
|
||||||
// for now, we should make sure we double check if the error messages
|
// for now, we should make sure we double check if the error messages
|
||||||
|
@@ -61,3 +61,12 @@ export function getAppPath(): string {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isJson(jsonString: string) {
|
||||||
|
try {
|
||||||
|
JSON.parse(jsonString);
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
8847
npm-shrinkwrap.json → package-lock.json
generated
8847
npm-shrinkwrap.json → package-lock.json
generated
File diff suppressed because it is too large
Load Diff
150
package.json
150
package.json
@@ -2,7 +2,7 @@
|
|||||||
"name": "balena-etcher",
|
"name": "balena-etcher",
|
||||||
"private": true,
|
"private": true,
|
||||||
"displayName": "balenaEtcher",
|
"displayName": "balenaEtcher",
|
||||||
"version": "1.5.120",
|
"version": "1.6.0",
|
||||||
"packageType": "local",
|
"packageType": "local",
|
||||||
"main": "generated/etcher.js",
|
"main": "generated/etcher.js",
|
||||||
"description": "Flash OS images to SD cards and USB drives, safely and easily.",
|
"description": "Flash OS images to SD cards and USB drives, safely and easily.",
|
||||||
@@ -15,15 +15,14 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"lint-ts": "balena-lint --fix --typescript typings lib tests scripts/clean-shrinkwrap.ts webpack.config.ts",
|
"lint-ts": "balena-lint --fix --typescript typings lib tests scripts/clean-shrinkwrap.ts webpack.config.ts",
|
||||||
"lint-css": "prettier --write lib/**/*.css",
|
"lint-css": "prettier --write lib/**/*.css",
|
||||||
"lint-spell": "codespell --dictionary - --dictionary dictionary.txt --skip *.ttf *.svg *.gz,*.bz2,*.xz,*.zip,*.img,*.dmg,*.iso,*.rpi-sdcard,*.wic,.DS_Store,*.dtb,*.dtbo,*.dat,*.elf,*.bin,*.foo,xz-without-extension lib tests docs Makefile *.md LICENSE",
|
"lint": "npm run lint-ts && npm run lint-css",
|
||||||
"lint": "npm run lint-ts && npm run lint-css && npm run lint-spell",
|
|
||||||
"test-spectron": "mocha --recursive --reporter spec --require ts-node/register --require-main tests/gui/allow-renderer-process-reuse.ts tests/spectron/runner.spec.ts",
|
"test-spectron": "mocha --recursive --reporter spec --require ts-node/register --require-main tests/gui/allow-renderer-process-reuse.ts tests/spectron/runner.spec.ts",
|
||||||
"test-gui": "electron-mocha --recursive --reporter spec --require ts-node/register --require-main tests/gui/allow-renderer-process-reuse.ts --full-trace --no-sandbox --renderer tests/gui/**/*.ts",
|
"test-gui": "electron-mocha --recursive --reporter spec --require ts-node/register --require-main tests/gui/allow-renderer-process-reuse.ts --full-trace --no-sandbox --renderer tests/gui/**/*.ts",
|
||||||
"test-shared": "electron-mocha --recursive --reporter spec --require ts-node/register --require-main tests/gui/allow-renderer-process-reuse.ts --full-trace --no-sandbox tests/shared/**/*.ts",
|
"test-shared": "electron-mocha --recursive --reporter spec --require ts-node/register --require-main tests/gui/allow-renderer-process-reuse.ts --full-trace --no-sandbox tests/shared/**/*.ts",
|
||||||
"test": "npm run lint && npm run test-gui && npm run test-shared && npm run test-spectron && npm run sanity-checks",
|
"test": "npm run lint && npm run test-gui && npm run test-shared && npm run test-spectron && npm run sanity-checks",
|
||||||
"sanity-checks": "bash scripts/ci/ensure-all-file-extensions-in-gitattributes.sh",
|
"sanity-checks": "bash scripts/ci/ensure-all-file-extensions-in-gitattributes.sh",
|
||||||
"start": "./node_modules/.bin/electron .",
|
"start": "./node_modules/.bin/electron .",
|
||||||
"postshrinkwrap": "ts-node ./scripts/clean-shrinkwrap.ts",
|
"postinstall": "electron-builder install-app-deps",
|
||||||
"webpack": "webpack",
|
"webpack": "webpack",
|
||||||
"watch": "webpack serve --no-optimization-minimize --config ./webpack.dev.config.ts",
|
"watch": "webpack serve --no-optimization-minimize --config ./webpack.dev.config.ts",
|
||||||
"concourse-build-electron": "npm run webpack",
|
"concourse-build-electron": "npm run webpack",
|
||||||
@@ -45,83 +44,76 @@
|
|||||||
},
|
},
|
||||||
"author": "Balena Inc. <hello@etcher.io>",
|
"author": "Balena Inc. <hello@etcher.io>",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"platformSpecificDependencies": [
|
"dependencies": {
|
||||||
"xmlbuilder",
|
"@fortawesome/fontawesome-free": "5.13.1",
|
||||||
"xmldom",
|
|
||||||
"@types/plist",
|
|
||||||
"@types/verror",
|
|
||||||
"crc",
|
|
||||||
"iconv-corefoundation",
|
|
||||||
"plist",
|
|
||||||
"dmg-license",
|
|
||||||
"fsevents",
|
|
||||||
"winusb-driver-generator"
|
|
||||||
],
|
|
||||||
"devDependencies": {
|
|
||||||
"@balena/lint": "^5.3.0",
|
|
||||||
"@fortawesome/fontawesome-free": "^5.13.1",
|
|
||||||
"@svgr/webpack": "^5.5.0",
|
|
||||||
"@types/chai": "^4.2.7",
|
|
||||||
"@types/copy-webpack-plugin": "^6.0.0",
|
|
||||||
"@types/mime-types": "^2.1.0",
|
|
||||||
"@types/mini-css-extract-plugin": "^1.2.2",
|
|
||||||
"@types/mocha": "^8.0.3",
|
|
||||||
"@types/node": "^14.14.41",
|
|
||||||
"@types/node-ipc": "^9.1.2",
|
|
||||||
"@types/react-dom": "^16.8.4",
|
|
||||||
"@types/semver": "^7.1.0",
|
|
||||||
"@types/sinon": "^9.0.0",
|
|
||||||
"@types/terser-webpack-plugin": "^5.0.2",
|
|
||||||
"@types/tmp": "^0.2.0",
|
|
||||||
"@types/webpack-node-externals": "^2.5.0",
|
|
||||||
"aws4-axios": "2.2.1",
|
"aws4-axios": "2.2.1",
|
||||||
"chai": "^4.2.0",
|
"d3": "4.13.0",
|
||||||
"copy-webpack-plugin": "^7.0.0",
|
"debug": "4.2.0",
|
||||||
"css-loader": "^5.0.1",
|
"etcher-sdk": "6.3.0",
|
||||||
"d3": "^4.13.0",
|
"immutable": "3.8.1",
|
||||||
"debug": "^4.2.0",
|
"lodash": "4.17.10",
|
||||||
"electron": "12.0.2",
|
"node-ipc": "9.1.1",
|
||||||
"electron-builder": "^22.10.5",
|
|
||||||
"electron-mocha": "^9.3.2",
|
|
||||||
"electron-notarize": "^1.0.0",
|
|
||||||
"electron-rebuild": "^2.3.2",
|
|
||||||
"electron-updater": "^4.3.5",
|
|
||||||
"etcher-sdk": "^6.2.1",
|
|
||||||
"file-loader": "^6.2.0",
|
|
||||||
"husky": "^4.2.5",
|
|
||||||
"immutable": "^3.8.1",
|
|
||||||
"lint-staged": "^10.2.2",
|
|
||||||
"lodash": "^4.17.10",
|
|
||||||
"mini-css-extract-plugin": "^1.3.3",
|
|
||||||
"mocha": "^8.0.1",
|
|
||||||
"native-addon-loader": "^2.0.1",
|
|
||||||
"node-ipc": "^9.1.1",
|
|
||||||
"omit-deep-lodash": "1.1.4",
|
"omit-deep-lodash": "1.1.4",
|
||||||
"outdent": "^0.7.1",
|
"outdent": "0.7.1",
|
||||||
"path-is-inside": "^1.0.2",
|
"path-is-inside": "1.0.2",
|
||||||
"pretty-bytes": "^5.3.0",
|
"pretty-bytes": "5.3.0",
|
||||||
"react": "^16.8.5",
|
"react": "16.8.5",
|
||||||
"react-dom": "^16.8.5",
|
"react-dom": "16.8.5",
|
||||||
"redux": "^4.0.5",
|
"redux": "4.0.5",
|
||||||
"rendition": "^19.2.0",
|
"rendition": "19.2.0",
|
||||||
"resin-corvus": "^2.0.5",
|
"resin-corvus": "2.0.5",
|
||||||
"semver": "^7.3.2",
|
"semver": "7.3.2",
|
||||||
"simple-progress-webpack-plugin": "^1.1.2",
|
"styled-components": "5.1.0",
|
||||||
"sinon": "^9.0.2",
|
|
||||||
"spectron": "^14.0.0",
|
|
||||||
"string-replace-loader": "^3.0.1",
|
|
||||||
"style-loader": "^2.0.0",
|
|
||||||
"styled-components": "^5.1.0",
|
|
||||||
"sudo-prompt": "github:zvin/sudo-prompt#7cdede2f0da28fbcc2db48402d7d935f3a825c91",
|
"sudo-prompt": "github:zvin/sudo-prompt#7cdede2f0da28fbcc2db48402d7d935f3a825c91",
|
||||||
"sys-class-rgb-led": "^3.0.0",
|
"sys-class-rgb-led": "3.0.0",
|
||||||
"ts-loader": "^8.0.12",
|
"url-loader": "4.1.1",
|
||||||
"ts-node": "^9.1.1",
|
"uuid": "8.1.0"
|
||||||
"tslib": "^2.0.0",
|
},
|
||||||
"typescript": "^4.2.2",
|
"devDependencies": {
|
||||||
"url-loader": "^4.1.1",
|
"@balena/lint": "5.3.0",
|
||||||
"uuid": "^8.1.0",
|
"@svgr/webpack": "5.5.0",
|
||||||
"webpack": "^5.11.0",
|
"@types/chai": "4.2.7",
|
||||||
"webpack-cli": "^4.2.0",
|
"@types/copy-webpack-plugin": "6.0.0",
|
||||||
"webpack-dev-server": "^3.11.2"
|
"@types/mime-types": "2.1.0",
|
||||||
|
"@types/mini-css-extract-plugin": "1.2.2",
|
||||||
|
"@types/mocha": "8.0.3",
|
||||||
|
"@types/node": "14.14.41",
|
||||||
|
"@types/node-ipc": "9.1.2",
|
||||||
|
"@types/react-dom": "16.8.4",
|
||||||
|
"@types/semver": "7.1.0",
|
||||||
|
"@types/sinon": "9.0.0",
|
||||||
|
"@types/terser-webpack-plugin": "5.0.2",
|
||||||
|
"@types/tmp": "0.2.0",
|
||||||
|
"@types/webpack-node-externals": "2.5.0",
|
||||||
|
"chai": "4.2.0",
|
||||||
|
"copy-webpack-plugin": "7.0.0",
|
||||||
|
"css-loader": "5.0.1",
|
||||||
|
"electron": "12.0.2",
|
||||||
|
"electron-builder": "22.10.5",
|
||||||
|
"electron-mocha": "9.3.2",
|
||||||
|
"electron-notarize": "1.0.0",
|
||||||
|
"electron-updater": "4.3.5",
|
||||||
|
"file-loader": "6.2.0",
|
||||||
|
"husky": "4.2.5",
|
||||||
|
"lint-staged": "10.2.2",
|
||||||
|
"mini-css-extract-plugin": "1.3.3",
|
||||||
|
"mocha": "8.0.1",
|
||||||
|
"native-addon-loader": "2.0.1",
|
||||||
|
"pnp-webpack-plugin": "1.6.4",
|
||||||
|
"simple-progress-webpack-plugin": "1.1.2",
|
||||||
|
"sinon": "9.0.2",
|
||||||
|
"spectron": "14.0.0",
|
||||||
|
"string-replace-loader": "3.0.1",
|
||||||
|
"style-loader": "2.0.0",
|
||||||
|
"ts-loader": "8.0.12",
|
||||||
|
"ts-node": "9.1.1",
|
||||||
|
"tslib": "2.0.0",
|
||||||
|
"typescript": "4.2.2",
|
||||||
|
"webpack": "5.11.0",
|
||||||
|
"webpack-cli": "4.2.0",
|
||||||
|
"webpack-dev-server": "3.11.2"
|
||||||
|
},
|
||||||
|
"versionist": {
|
||||||
|
"publishedAt": "2021-09-20T10:42:04.882Z"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,3 +1,2 @@
|
|||||||
codespell==1.12.0
|
|
||||||
awscli==1.11.87
|
awscli==1.11.87
|
||||||
shyaml==0.5.0
|
shyaml==0.5.0
|
||||||
|
@@ -37,7 +37,7 @@ async function main() {
|
|||||||
SHRINKWRAP_FILENAME,
|
SHRINKWRAP_FILENAME,
|
||||||
JSON.stringify(cleaned, null, JSON_INDENT),
|
JSON.stringify(cleaned, null, JSON_INDENT),
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
console.log(`[ERROR] Couldn't write shrinkwrap file: ${error.stack}`);
|
console.log(`[ERROR] Couldn't write shrinkwrap file: ${error.stack}`);
|
||||||
process.exitCode = 1;
|
process.exitCode = 1;
|
||||||
}
|
}
|
||||||
|
Submodule scripts/resin updated: d1b05ad312...8dfa21cfc2
@@ -573,7 +573,8 @@ describe('Model: flashState', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('.getFlashUuid()', function () {
|
describe('.getFlashUuid()', function () {
|
||||||
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
const UUID_REGEX =
|
||||||
|
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
||||||
|
|
||||||
it('should be initially undefined', function () {
|
it('should be initially undefined', function () {
|
||||||
expect(flashState.getFlashUuid()).to.be.undefined;
|
expect(flashState.getFlashUuid()).to.be.undefined;
|
||||||
|
@@ -23,7 +23,7 @@ import * as settings from '../../../lib/gui/app/models/settings';
|
|||||||
async function checkError(promise: Promise<any>, fn: (err: Error) => any) {
|
async function checkError(promise: Promise<any>, fn: (err: Error) => any) {
|
||||||
try {
|
try {
|
||||||
await promise;
|
await promise;
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
await fn(error);
|
await fn(error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -83,7 +83,7 @@ describe('Browser: imageWriter', () => {
|
|||||||
imageWriter.flash(image, [fakeDrive], performWriteStub),
|
imageWriter.flash(image, [fakeDrive], performWriteStub),
|
||||||
]);
|
]);
|
||||||
assert.fail('Writing twice should fail');
|
assert.fail('Writing twice should fail');
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
expect(error.message).to.equal(
|
expect(error.message).to.equal(
|
||||||
'There is already a flash in progress',
|
'There is already a flash in progress',
|
||||||
);
|
);
|
||||||
@@ -133,7 +133,7 @@ describe('Browser: imageWriter', () => {
|
|||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
await imageWriter.flash(image, [fakeDrive], performWriteStub);
|
await imageWriter.flash(image, [fakeDrive], performWriteStub);
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
expect(error).to.be.an.instanceof(Error);
|
expect(error).to.be.an.instanceof(Error);
|
||||||
expect(error.message).to.equal('write error');
|
expect(error.message).to.equal('write error');
|
||||||
}
|
}
|
||||||
|
@@ -514,40 +514,6 @@ describe('Shared: DriveConstraints', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('.isDriveDisabled()', function () {
|
|
||||||
it('should return true if the drive is disabled', function () {
|
|
||||||
const result = constraints.isDriveDisabled(({
|
|
||||||
device: '/dev/disk1',
|
|
||||||
size: 1000000000,
|
|
||||||
isReadOnly: false,
|
|
||||||
disabled: true,
|
|
||||||
} as unknown) as constraints.DrivelistDrive);
|
|
||||||
|
|
||||||
expect(result).to.be.true;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return false if the drive is not disabled', function () {
|
|
||||||
const result = constraints.isDriveDisabled(({
|
|
||||||
device: '/dev/disk1',
|
|
||||||
size: 1000000000,
|
|
||||||
isReadOnly: false,
|
|
||||||
disabled: false,
|
|
||||||
} as unknown) as constraints.DrivelistDrive);
|
|
||||||
|
|
||||||
expect(result).to.be.false;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return false if "disabled" is undefined', function () {
|
|
||||||
const result = constraints.isDriveDisabled({
|
|
||||||
device: '/dev/disk1',
|
|
||||||
size: 1000000000,
|
|
||||||
isReadOnly: false,
|
|
||||||
} as constraints.DrivelistDrive);
|
|
||||||
|
|
||||||
expect(result).to.be.false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('.isDriveSizeRecommended()', function () {
|
describe('.isDriveSizeRecommended()', function () {
|
||||||
const image: SourceMetadata = {
|
const image: SourceMetadata = {
|
||||||
description: 'rpi.img',
|
description: 'rpi.img',
|
||||||
@@ -1192,7 +1158,7 @@ describe('Shared: DriveConstraints', function () {
|
|||||||
'/dev/disk6',
|
'/dev/disk6',
|
||||||
];
|
];
|
||||||
const drives = [
|
const drives = [
|
||||||
({
|
{
|
||||||
device: drivePaths[0],
|
device: drivePaths[0],
|
||||||
description: 'My Drive',
|
description: 'My Drive',
|
||||||
size: 123456789,
|
size: 123456789,
|
||||||
@@ -1200,8 +1166,8 @@ describe('Shared: DriveConstraints', function () {
|
|||||||
mountpoints: [{ path: __dirname }],
|
mountpoints: [{ path: __dirname }],
|
||||||
isSystem: false,
|
isSystem: false,
|
||||||
isReadOnly: false,
|
isReadOnly: false,
|
||||||
} as unknown) as constraints.DrivelistDrive,
|
} as unknown as constraints.DrivelistDrive,
|
||||||
({
|
{
|
||||||
device: drivePaths[1],
|
device: drivePaths[1],
|
||||||
description: 'My Other Drive',
|
description: 'My Other Drive',
|
||||||
size: 123456789,
|
size: 123456789,
|
||||||
@@ -1209,8 +1175,8 @@ describe('Shared: DriveConstraints', function () {
|
|||||||
mountpoints: [],
|
mountpoints: [],
|
||||||
isSystem: false,
|
isSystem: false,
|
||||||
isReadOnly: true,
|
isReadOnly: true,
|
||||||
} as unknown) as constraints.DrivelistDrive,
|
} as unknown as constraints.DrivelistDrive,
|
||||||
({
|
{
|
||||||
device: drivePaths[2],
|
device: drivePaths[2],
|
||||||
description: 'My Drive',
|
description: 'My Drive',
|
||||||
size: 1234567,
|
size: 1234567,
|
||||||
@@ -1218,8 +1184,8 @@ describe('Shared: DriveConstraints', function () {
|
|||||||
mountpoints: [],
|
mountpoints: [],
|
||||||
isSystem: false,
|
isSystem: false,
|
||||||
isReadOnly: false,
|
isReadOnly: false,
|
||||||
} as unknown) as constraints.DrivelistDrive,
|
} as unknown as constraints.DrivelistDrive,
|
||||||
({
|
{
|
||||||
device: drivePaths[3],
|
device: drivePaths[3],
|
||||||
description: 'My Drive',
|
description: 'My Drive',
|
||||||
size: 123456789,
|
size: 123456789,
|
||||||
@@ -1227,8 +1193,8 @@ describe('Shared: DriveConstraints', function () {
|
|||||||
mountpoints: [],
|
mountpoints: [],
|
||||||
isSystem: true,
|
isSystem: true,
|
||||||
isReadOnly: false,
|
isReadOnly: false,
|
||||||
} as unknown) as constraints.DrivelistDrive,
|
} as unknown as constraints.DrivelistDrive,
|
||||||
({
|
{
|
||||||
device: drivePaths[4],
|
device: drivePaths[4],
|
||||||
description: 'My Drive',
|
description: 'My Drive',
|
||||||
size: 128000000001,
|
size: 128000000001,
|
||||||
@@ -1236,8 +1202,8 @@ describe('Shared: DriveConstraints', function () {
|
|||||||
mountpoints: [],
|
mountpoints: [],
|
||||||
isSystem: false,
|
isSystem: false,
|
||||||
isReadOnly: false,
|
isReadOnly: false,
|
||||||
} as unknown) as constraints.DrivelistDrive,
|
} as unknown as constraints.DrivelistDrive,
|
||||||
({
|
{
|
||||||
device: drivePaths[5],
|
device: drivePaths[5],
|
||||||
description: 'My Drive',
|
description: 'My Drive',
|
||||||
size: 12345678,
|
size: 12345678,
|
||||||
@@ -1245,8 +1211,8 @@ describe('Shared: DriveConstraints', function () {
|
|||||||
mountpoints: [],
|
mountpoints: [],
|
||||||
isSystem: false,
|
isSystem: false,
|
||||||
isReadOnly: false,
|
isReadOnly: false,
|
||||||
} as unknown) as constraints.DrivelistDrive,
|
} as unknown as constraints.DrivelistDrive,
|
||||||
({
|
{
|
||||||
device: drivePaths[6],
|
device: drivePaths[6],
|
||||||
description: 'My Drive',
|
description: 'My Drive',
|
||||||
size: 123456789,
|
size: 123456789,
|
||||||
@@ -1254,7 +1220,7 @@ describe('Shared: DriveConstraints', function () {
|
|||||||
mountpoints: [],
|
mountpoints: [],
|
||||||
isSystem: false,
|
isSystem: false,
|
||||||
isReadOnly: false,
|
isReadOnly: false,
|
||||||
} as unknown) as constraints.DrivelistDrive,
|
} as unknown as constraints.DrivelistDrive,
|
||||||
];
|
];
|
||||||
|
|
||||||
const image: SourceMetadata = {
|
const image: SourceMetadata = {
|
||||||
|
@@ -30,9 +30,8 @@ describe('Shared: SupportedFormats', function () {
|
|||||||
],
|
],
|
||||||
(imagePath) => {
|
(imagePath) => {
|
||||||
it(`should return true if filename is ${imagePath}`, function () {
|
it(`should return true if filename is ${imagePath}`, function () {
|
||||||
const looksLikeWindowsImage = supportedFormats.looksLikeWindowsImage(
|
const looksLikeWindowsImage =
|
||||||
imagePath,
|
supportedFormats.looksLikeWindowsImage(imagePath);
|
||||||
);
|
|
||||||
expect(looksLikeWindowsImage).to.be.true;
|
expect(looksLikeWindowsImage).to.be.true;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -45,9 +44,8 @@ describe('Shared: SupportedFormats', function () {
|
|||||||
],
|
],
|
||||||
(imagePath) => {
|
(imagePath) => {
|
||||||
it(`should return false if filename is ${imagePath}`, function () {
|
it(`should return false if filename is ${imagePath}`, function () {
|
||||||
const looksLikeWindowsImage = supportedFormats.looksLikeWindowsImage(
|
const looksLikeWindowsImage =
|
||||||
imagePath,
|
supportedFormats.looksLikeWindowsImage(imagePath);
|
||||||
);
|
|
||||||
expect(looksLikeWindowsImage).to.be.false;
|
expect(looksLikeWindowsImage).to.be.false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@@ -28,7 +28,7 @@ if (platform() !== 'darwin') {
|
|||||||
this.timeout(40000);
|
this.timeout(40000);
|
||||||
|
|
||||||
const app = new Application({
|
const app = new Application({
|
||||||
path: (electronPath as unknown) as string,
|
path: electronPath as unknown as string,
|
||||||
args: ['--no-sandbox', '.'],
|
args: ['--no-sandbox', '.'],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
1
typings/pnp-webpack-plugin/index.d.ts
vendored
Normal file
1
typings/pnp-webpack-plugin/index.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
declare module 'pnp-webpack-plugin';
|
@@ -24,6 +24,7 @@ import { env } from 'process';
|
|||||||
import * as SimpleProgressWebpackPlugin from 'simple-progress-webpack-plugin';
|
import * as SimpleProgressWebpackPlugin from 'simple-progress-webpack-plugin';
|
||||||
import * as TerserPlugin from 'terser-webpack-plugin';
|
import * as TerserPlugin from 'terser-webpack-plugin';
|
||||||
import { BannerPlugin, NormalModuleReplacementPlugin } from 'webpack';
|
import { BannerPlugin, NormalModuleReplacementPlugin } from 'webpack';
|
||||||
|
import * as PnpWebpackPlugin from 'pnp-webpack-plugin';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Don't webpack package.json as mixpanel & sentry tokens
|
* Don't webpack package.json as mixpanel & sentry tokens
|
||||||
@@ -293,6 +294,7 @@ const commonConfig = {
|
|||||||
extensions: ['.node', '.js', '.json', '.ts', '.tsx'],
|
extensions: ['.node', '.js', '.json', '.ts', '.tsx'],
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
PnpWebpackPlugin,
|
||||||
new SimpleProgressWebpackPlugin({
|
new SimpleProgressWebpackPlugin({
|
||||||
format: process.env.WEBPACK_PROGRESS || 'verbose',
|
format: process.env.WEBPACK_PROGRESS || 'verbose',
|
||||||
}),
|
}),
|
||||||
@@ -303,6 +305,9 @@ const commonConfig = {
|
|||||||
'./http.js',
|
'./http.js',
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
resolveLoader: {
|
||||||
|
plugins: [PnpWebpackPlugin.moduleLoader(module)],
|
||||||
|
},
|
||||||
output: {
|
output: {
|
||||||
path: path.join(__dirname, 'generated'),
|
path: path.join(__dirname, 'generated'),
|
||||||
filename: '[name].js',
|
filename: '[name].js',
|
||||||
@@ -354,6 +359,7 @@ const guiConfig = {
|
|||||||
entry: {
|
entry: {
|
||||||
gui: path.join(__dirname, 'lib', 'gui', 'app', 'renderer.ts'),
|
gui: path.join(__dirname, 'lib', 'gui', 'app', 'renderer.ts'),
|
||||||
},
|
},
|
||||||
|
// entry: path.join(__dirname, 'lib', 'gui', 'app', 'renderer.ts'),
|
||||||
plugins: [
|
plugins: [
|
||||||
...commonConfig.plugins,
|
...commonConfig.plugins,
|
||||||
new CopyPlugin({
|
new CopyPlugin({
|
||||||
|
@@ -10,6 +10,8 @@ const [
|
|||||||
|
|
||||||
configs.forEach((config) => {
|
configs.forEach((config) => {
|
||||||
config.mode = 'development';
|
config.mode = 'development';
|
||||||
|
// @ts-ignore
|
||||||
|
config.devtool = 'source-map';
|
||||||
});
|
});
|
||||||
|
|
||||||
guiConfig.devServer = {
|
guiConfig.devServer = {
|
||||||
|
Reference in New Issue
Block a user