mirror of
https://github.com/balena-io/etcher.git
synced 2025-11-06 17:08:31 +00:00
Compare commits
93 Commits
v1.5.111
...
build-inst
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de93d6ac78 | ||
|
|
6a25c9cda3 | ||
|
|
936afec1a5 | ||
|
|
2be29e22c2 | ||
|
|
ccb08a48f1 | ||
|
|
a8f3d45b12 | ||
|
|
7e333caaf9 | ||
|
|
70229e8684 | ||
|
|
261700389b | ||
|
|
250aed2eb1 | ||
|
|
ed1f008fe2 | ||
|
|
e9ce270dab | ||
|
|
1ee110bc95 | ||
|
|
33dd07c675 | ||
|
|
39ccbbeeda | ||
|
|
55d2400ac7 | ||
|
|
0bdea5c54c | ||
|
|
3be372d49f | ||
|
|
d0c66b2c48 | ||
|
|
65082c4790 | ||
|
|
e87ed9beed | ||
|
|
bc5563d9c2 | ||
|
|
ad83ab5dcc | ||
|
|
0dc1cf9701 | ||
|
|
11489c6538 | ||
|
|
2619d4bc86 | ||
|
|
3730efd350 | ||
|
|
6ece32c546 | ||
|
|
fd9996a3cc | ||
|
|
f06cc89152 | ||
|
|
c1d7ab3fa9 | ||
|
|
b206483c7c | ||
|
|
c3eb8c7b56 | ||
|
|
0849d4f435 | ||
|
|
1dba3ae19b | ||
|
|
f33f2e3771 | ||
|
|
e56aaed973 | ||
|
|
a4659f038e | ||
|
|
cd462818da | ||
|
|
37769efbed | ||
|
|
0f70c4bbce | ||
|
|
48b5e8b9d9 | ||
|
|
1f138f0ecc | ||
|
|
73f67e99ca | ||
|
|
9114da2445 | ||
|
|
554bbcc780 | ||
|
|
4db2289cfd | ||
|
|
c15b56bc23 | ||
|
|
9f52dda6ae | ||
|
|
fadcefb11a | ||
|
|
361c32913c | ||
|
|
5c2042198e | ||
|
|
99df53098c | ||
|
|
aa563c87bd | ||
|
|
1188888956 | ||
|
|
f9d7991dc8 | ||
|
|
53954e81fd | ||
|
|
f82996bfd1 | ||
|
|
b74069eb41 | ||
|
|
e8c7591751 | ||
|
|
3521b61a81 | ||
|
|
93db90c725 | ||
|
|
1dc56aed14 | ||
|
|
d814202424 | ||
|
|
c54856a616 | ||
|
|
fc45df270a | ||
|
|
3cde2faed0 | ||
|
|
b4b8c89aad | ||
|
|
36d05724c0 | ||
|
|
b1e4e681d1 | ||
|
|
3987078c11 | ||
|
|
de0010eb72 | ||
|
|
1f94f44b18 | ||
|
|
fe0b45cae6 | ||
|
|
c32e485f27 | ||
|
|
409b78fc21 | ||
|
|
2f08142f5a | ||
|
|
8c4edaabba | ||
|
|
05497ce85c | ||
|
|
d3df2fe57e | ||
|
|
a0f07082f2 | ||
|
|
b7efa8e1f0 | ||
|
|
3647457bb5 | ||
|
|
2e5a39dcd8 | ||
|
|
edabacfb3a | ||
|
|
f46176fd10 | ||
|
|
2158e20380 | ||
|
|
fa593e33d1 | ||
|
|
50730bd3df | ||
|
|
4e68955981 | ||
|
|
3c0084d012 | ||
|
|
8bd11a01ae | ||
|
|
da3a22d0f6 |
@@ -15,7 +15,7 @@
|
||||
},
|
||||
"builder": {
|
||||
"appId": "io.balena.etcher",
|
||||
"copyright": "Copyright 2016-2020 Balena Ltd",
|
||||
"copyright": "Copyright 2016-2021 Balena Ltd",
|
||||
"productName": "balenaEtcher",
|
||||
"nodeGypRebuild": false,
|
||||
"afterPack": "./afterPack.js",
|
||||
@@ -30,7 +30,8 @@
|
||||
"category": "public.app-category.developer-tools",
|
||||
"hardenedRuntime": true,
|
||||
"entitlements": "entitlements.mac.plist",
|
||||
"entitlementsInherit": "entitlements.mac.plist"
|
||||
"entitlementsInherit": "entitlements.mac.plist",
|
||||
"artifactName": "${productName}-${version}.${ext}"
|
||||
},
|
||||
"dmg": {
|
||||
"iconSize": 110,
|
||||
|
||||
11237
.versionbot/CHANGELOG.yml
Normal file
11237
.versionbot/CHANGELOG.yml
Normal file
File diff suppressed because it is too large
Load Diff
93
BUILD.md
Normal file
93
BUILD.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# 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
|
||||
671
CHANGELOG.md
671
CHANGELOG.md
@@ -3,6 +3,677 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
# v1.5.120
|
||||
## (2021-05-11)
|
||||
|
||||
* Update README to reference Cloudsmith [Lorenzo Alberto Maria Ambrosi]
|
||||
|
||||
# v1.5.119
|
||||
## (2021-04-30)
|
||||
|
||||
* Update readme for new PPA provider [Lorenzo Alberto Maria Ambrosi]
|
||||
|
||||
# v1.5.118
|
||||
## (2021-04-27)
|
||||
|
||||
* patch: development environment [Zane Hitchcox]
|
||||
* patch: watch files for electron [Zane Hitchcox]
|
||||
|
||||
# v1.5.117
|
||||
## (2021-04-02)
|
||||
|
||||
* Rename mac releases (keep old naming) [Alexis Svinartchouk]
|
||||
* Disable spectron tests on macOS [Alexis Svinartchouk]
|
||||
* Update electron to v12.0.2 [Alexis Svinartchouk]
|
||||
|
||||
<details>
|
||||
<summary> Update etcher-sdk from 6.1.1 to 6.2.1 [Alexis Svinartchouk] </summary>
|
||||
|
||||
> ## etcher-sdk-6.2.1
|
||||
> ### (2021-03-26)
|
||||
>
|
||||
>
|
||||
> <details>
|
||||
> <summary> Update node-raspberrypi-usbboot from 0.2.11 to 0.3.0 [Alexis Svinartchouk] </summary>
|
||||
>
|
||||
>> ### node-raspberrypi-usbboot-0.3.0
|
||||
>> #### (2021-03-26)
|
||||
>>
|
||||
>> * Add support for compute module 4 [Alexis Svinartchouk]
|
||||
>> * Fix size endianness of boot_message_t message [Alexis Svinartchouk]
|
||||
>>
|
||||
> </details>
|
||||
>
|
||||
>
|
||||
> ## etcher-sdk-6.2.0
|
||||
> ### (2021-02-18)
|
||||
>
|
||||
> * Added BeagleBone USB Boot example [Parthiban Gandhi]
|
||||
> * Added BeagleBone USB Boot support [Parthiban Gandhi]
|
||||
>
|
||||
</details>
|
||||
|
||||
* Fix getAppPath() returning an asar file on macOS [Alexis Svinartchouk]
|
||||
* Grammar fix [Andrew Scheller]
|
||||
* (docs) update README.md [vlad doster]
|
||||
* Update copyright year in electron-builder.yml [Andrew Scheller]
|
||||
* Update copyright year in .resinci.json [Andrew Scheller]
|
||||
* Separate the Yum and DNF instructions. [Dugan Chen]
|
||||
* Set msvs_version to 2019 when rebuilding [Alexis Svinartchouk]
|
||||
* Use moduleIds: 'natural' in webpack config to keep js files in arm64 and x64 mac builds identical [Alexis Svinartchouk]
|
||||
* Update electron-builder to 22.10.5 [Alexis Svinartchouk]
|
||||
* Update spectron to v13 [Alexis Svinartchouk]
|
||||
* Update dependencies, use aws4-axios@2.2.1 to avoid adding more dependiencies [Alexis Svinartchouk]
|
||||
* Update scripts to build universal mac dmgs on the ci [Alexis Svinartchouk]
|
||||
* Fix beforeBuild.js script to also work on mac [Alexis Svinartchouk]
|
||||
* Support building universal dmgs (x64 and arm64) for mac [Alexis Svinartchouk]
|
||||
* Update electron-builder to 22.10.4 [Alexis Svinartchouk]
|
||||
* Fix titlebar z-index [Alexis Svinartchouk]
|
||||
* Explicitly set contextIsolation to false [Alexis Svinartchouk]
|
||||
* Update electron from 9.4.1 to 11.2.3 [Alexis Svinartchouk]
|
||||
|
||||
<details>
|
||||
<summary> Update etcher-sdk from 6.1.0 to 6.1.1 [Alexis Svinartchouk] </summary>
|
||||
|
||||
> ## etcher-sdk-6.1.1
|
||||
> ### (2021-02-10)
|
||||
>
|
||||
>
|
||||
> <details>
|
||||
> <summary> Update node-raspberrypi-usbboot from 0.2.10 to 0.2.11 [Alexis Svinartchouk] </summary>
|
||||
>
|
||||
>> ### node-raspberrypi-usbboot-0.2.11
|
||||
>> #### (2021-02-10)
|
||||
>>
|
||||
>> * Update @balena.io/usb from 1.3.12 to 1.3.14 [Alexis Svinartchouk]
|
||||
>>
|
||||
> </details>
|
||||
>
|
||||
>
|
||||
</details>
|
||||
|
||||
# v1.5.116
|
||||
## (2021-02-03)
|
||||
|
||||
* Only cleanup temporary decompressed files in child-writer [Alexis Svinartchouk]
|
||||
* Add .versionbot/CHANGELOG.yml [Alexis Svinartchouk]
|
||||
* Stop using node-tmp, use withTmpFile from etcher-sdk instead [Alexis Svinartchouk]
|
||||
|
||||
<details>
|
||||
<summary> Update etcher-sdk from 5.2.2 to 6.1.0 [Alexis Svinartchouk] </summary>
|
||||
|
||||
> ## etcher-sdk-6.1.0
|
||||
> ### (2021-02-03)
|
||||
>
|
||||
> * Prefix temporary decompressed images filenames [Alexis Svinartchouk]
|
||||
>
|
||||
> ## etcher-sdk-6.0.1
|
||||
> ### (2021-02-02)
|
||||
>
|
||||
> * Ignore ENOENT errors on unlink in withTmpFile [Alexis Svinartchouk]
|
||||
>
|
||||
> ## etcher-sdk-6.0.0
|
||||
> ### (2021-02-01)
|
||||
>
|
||||
> * Export tmp and add prefix and postfix options [Alexis Svinartchouk]
|
||||
>
|
||||
> ## etcher-sdk-5.2.3
|
||||
> ### (2021-01-26)
|
||||
>
|
||||
> * upgrade lint [Zane Hitchcox]
|
||||
>
|
||||
</details>
|
||||
|
||||
* Revert "Change some border colors to have higher contrast" [Alexis Svinartchouk]
|
||||
* Update electron to v9.4.1 [Alexis Svinartchouk]
|
||||
|
||||
<details>
|
||||
<summary> Update etcher-sdk from 5.2.1 to 5.2.2 [Alexis Svinartchouk] </summary>
|
||||
|
||||
> ## etcher-sdk-5.2.2
|
||||
> ### (2021-01-19)
|
||||
>
|
||||
>
|
||||
> <details>
|
||||
> <summary> Update drivelist from 9.2.2 to 9.2.4 [Alexis Svinartchouk] </summary>
|
||||
>
|
||||
>> ### drivelist-9.2.4
|
||||
>> #### (2021-01-19)
|
||||
>>
|
||||
>> * Pass strings between methods as std::string instead of char * [Floris Bos]
|
||||
>>
|
||||
>> ### drivelist-9.2.3
|
||||
>> #### (2021-01-19)
|
||||
>>
|
||||
>> * Support lsblk versions that do no support the pttype column [Alexis Svinartchouk]
|
||||
>>
|
||||
> </details>
|
||||
>
|
||||
>
|
||||
</details>
|
||||
|
||||
# v1.5.115
|
||||
## (2021-01-18)
|
||||
|
||||
|
||||
<details>
|
||||
<summary> Update etcher-sdk from 5.1.12 to 5.2.1 [Alexis Svinartchouk] </summary>
|
||||
|
||||
> ## etcher-sdk-5.2.1
|
||||
> ### (2021-01-15)
|
||||
>
|
||||
> * Only run one diskpart at a time [Alexis Svinartchouk]
|
||||
> * Ignore diskpart VDS_E_DISK_IS_OFFLINE errors [Alexis Svinartchouk]
|
||||
>
|
||||
> ## etcher-sdk-5.2.0
|
||||
> ### (2021-01-06)
|
||||
>
|
||||
> * Store progress on usbboot devices [Alexis Svinartchouk]
|
||||
>
|
||||
</details>
|
||||
|
||||
# v1.5.114
|
||||
## (2021-01-12)
|
||||
|
||||
* Remove libappindicator1 debian dependency [Alexis Svinartchouk]
|
||||
|
||||
<details>
|
||||
<summary> Update etcher-sdk from 5.1.11 to 5.1.12 [Alexis Svinartchouk] </summary>
|
||||
|
||||
> ## etcher-sdk-5.1.12
|
||||
> ### (2021-01-06)
|
||||
>
|
||||
> * Remove BlockDevice.mountpoints incorrect typing [Alexis Svinartchouk]
|
||||
> * Update axios to 0.21.1 and aws4-axios to 2.0.1 [Alexis Svinartchouk]
|
||||
>
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary> Update rendition from 18.8.3 to 19.2.0 [Alexis Svinartchouk] </summary>
|
||||
|
||||
> ## rendition-19.2.0
|
||||
> ### (2020-12-29)
|
||||
>
|
||||
> * Add truncate property to Txt component [JSReds]
|
||||
>
|
||||
> ## rendition-19.1.0
|
||||
> ### (2020-12-29)
|
||||
>
|
||||
> * Add fallback image source to Img component [Stevche Radevski]
|
||||
>
|
||||
> ## rendition-19.0.0
|
||||
> ### (2020-12-21)
|
||||
>
|
||||
> * Remove Arcslider component [Stevche Radevski]
|
||||
>
|
||||
> ## rendition-18.20.4
|
||||
> ### (2020-12-17)
|
||||
>
|
||||
> * Upgrade rehype-raw to latest version [Kakhaber]
|
||||
>
|
||||
> ## rendition-18.20.3
|
||||
> ### (2020-12-17)
|
||||
>
|
||||
> * Fix disabled button tooltip [JSReds]
|
||||
>
|
||||
> ## rendition-18.20.2
|
||||
> ### (2020-12-16)
|
||||
>
|
||||
> * Turn keydown handler into an arrow function [Stevche Radevski]
|
||||
>
|
||||
> ## rendition-18.20.1
|
||||
> ### (2020-12-14)
|
||||
>
|
||||
> * Fix form not getting the Enter key event when nested in a modal [Stevche Radevski]
|
||||
>
|
||||
> ## rendition-18.20.0
|
||||
> ### (2020-12-14)
|
||||
>
|
||||
> * feat: Add new StatsBar component [Graham McCulloch]
|
||||
>
|
||||
> ## rendition-18.19.2
|
||||
> ### (2020-12-14)
|
||||
>
|
||||
> * Update snapshots [Graham McCulloch]
|
||||
> * Removed out-of-date documentation and template text [Graham McCulloch]
|
||||
>
|
||||
> ## rendition-18.19.1
|
||||
> ### (2020-12-04)
|
||||
>
|
||||
> * Markdown: Fix line breaks [Kakhaber]
|
||||
>
|
||||
> ## rendition-18.19.0
|
||||
> ### (2020-12-02)
|
||||
>
|
||||
> * Make card size responsive [Stevche Radevski]
|
||||
>
|
||||
> ## rendition-18.18.0
|
||||
> ### (2020-12-02)
|
||||
>
|
||||
> * Allow passing responsive values to datagrid width props [Stevche Radevski]
|
||||
>
|
||||
> ## rendition-18.17.2
|
||||
> ### (2020-12-01)
|
||||
>
|
||||
> * Update snapshots due to a Card change [JSReds]
|
||||
>
|
||||
> ## rendition-18.17.1
|
||||
> ### (2020-12-01)
|
||||
>
|
||||
> * Card: make body to be full height [JSReds]
|
||||
>
|
||||
> ## rendition-18.17.0
|
||||
> ### (2020-12-01)
|
||||
>
|
||||
> * Add star rating component [Kakhaber]
|
||||
>
|
||||
> ## rendition-18.16.0
|
||||
> ### (2020-11-23)
|
||||
>
|
||||
> * Completely revamp the development setup for rendition [Stevche Radevski]
|
||||
>
|
||||
> ## rendition-18.15.1
|
||||
> ### (2020-11-16)
|
||||
>
|
||||
> * Modal: Change the button margins to use the predefined spacing palette [Thodoris Greasidis]
|
||||
>
|
||||
> ## rendition-18.15.0
|
||||
> ### (2020-11-16)
|
||||
>
|
||||
> * Modal: Move the cancel button first for dangerous & warning actions [Thodoris Greasidis]
|
||||
>
|
||||
> ## rendition-18.14.0
|
||||
> ### (2020-11-16)
|
||||
>
|
||||
> * Allow passing checked items as a prop to Table [Stevche Radevski]
|
||||
>
|
||||
> ## rendition-18.13.4
|
||||
> ### (2020-11-16)
|
||||
>
|
||||
> * Fix accidental complete lodash import [Thodoris Greasidis]
|
||||
>
|
||||
> ## rendition-18.13.3
|
||||
> ### (2020-11-16)
|
||||
>
|
||||
> * Form: Remove the flaky Captcha sceenshot test [Thodoris Greasidis]
|
||||
> * Update react-simplemde-editor & snapshots for upstream versions [Thodoris Greasidis]
|
||||
>
|
||||
> ## rendition-18.13.2
|
||||
> ### (2020-10-29)
|
||||
>
|
||||
> * Updated snapshots [Graham McCulloch]
|
||||
> * Fix: Confirm only depends on the files it needs [Graham McCulloch]
|
||||
>
|
||||
> ## rendition-18.13.1
|
||||
> ### (2020-10-23)
|
||||
>
|
||||
> * Button: Preserve event during confirmation [Kakhaber]
|
||||
>
|
||||
> ## rendition-18.13.0
|
||||
> ### (2020-10-22)
|
||||
>
|
||||
> * Button: Add confirmation property [Kakhaber]
|
||||
>
|
||||
> ## rendition-18.12.2
|
||||
> ### (2020-10-21)
|
||||
>
|
||||
> * Tabs: changed interfaces and props [JSReds]
|
||||
>
|
||||
> ## rendition-18.12.1
|
||||
> ### (2020-10-20)
|
||||
>
|
||||
> * Fix Tabs typings [Stevche Radevski]
|
||||
>
|
||||
> ## rendition-18.12.0
|
||||
> ### (2020-10-19)
|
||||
>
|
||||
> * Add a Grid component [Stevche Radevski]
|
||||
>
|
||||
> ## rendition-18.11.3
|
||||
> ### (2020-10-14)
|
||||
>
|
||||
> * Added more documentation for JsonSchemaRenderer [Graham McCulloch]
|
||||
>
|
||||
> ## rendition-18.11.2
|
||||
> ### (2020-10-14)
|
||||
>
|
||||
> * fix: UI schema for JsonSchemaRenderer DropDownButton and ButtonGroup widgets [Graham McCulloch]
|
||||
>
|
||||
> ## rendition-18.11.1
|
||||
> ### (2020-10-13)
|
||||
>
|
||||
> * Add dark mode to storybook [Stevche Radevski]
|
||||
>
|
||||
> ## rendition-18.11.0
|
||||
> ### (2020-10-08)
|
||||
>
|
||||
> * Allow passing widget to extraFormats field [Stevche Radevski]
|
||||
>
|
||||
> ## rendition-18.10.2
|
||||
> ### (2020-09-30)
|
||||
>
|
||||
> * Resolve module path not relying on node_moules dir [Kakhaber]
|
||||
>
|
||||
> ## rendition-18.10.1
|
||||
> ### (2020-09-29)
|
||||
>
|
||||
> * Set tabpanel height so it stretches to full height [StefKors]
|
||||
> * Specify tabs width to fix layout problems [StefKors]
|
||||
>
|
||||
> ## rendition-18.10.0
|
||||
> ### (2020-09-24)
|
||||
>
|
||||
> * feat: Add ColorWidget for JsonSchemaRenderer [Graham McCulloch]
|
||||
>
|
||||
> ## rendition-18.9.2
|
||||
> ### (2020-09-22)
|
||||
>
|
||||
> * Markdown: Ignore decorators inside a code block [Kakhaber]
|
||||
>
|
||||
> ## rendition-18.9.1
|
||||
> ### (2020-09-21)
|
||||
>
|
||||
> * Add compact variation to tabs [StefKors]
|
||||
>
|
||||
> ## rendition-18.9.0
|
||||
> ### (2020-09-18)
|
||||
>
|
||||
> * Improve spacing for Modal and Select components [Stevche Radevski]
|
||||
>
|
||||
> ## rendition-18.8.4
|
||||
> ### (2020-09-17)
|
||||
>
|
||||
> * fix: Use widget's display name to reference the widget [Graham McCulloch]
|
||||
>
|
||||
</details>
|
||||
|
||||
* Update dependencies [Alexis Svinartchouk]
|
||||
* Update @balena/lint to 5.3.0 [Alexis Svinartchouk]
|
||||
* Update webpack to v5 [Alexis Svinartchouk]
|
||||
* Fix typo in webpack.config.ts comment [Alexis Svinartchouk]
|
||||
* docs: fix quote marks [Aaron Shaw]
|
||||
* Disable screensaver while flashing (on balena-electron-env) [Alexis Svinartchouk]
|
||||
|
||||
# v1.5.113
|
||||
## (2020-12-16)
|
||||
|
||||
* Show the first error for each drive (not the last) [Alexis Svinartchouk]
|
||||
* Fix red leds not showing for failed devices [Alexis Svinartchouk]
|
||||
* docs: add documentation links [Aaron Shaw]
|
||||
* docs: update macOS version [Aaron Shaw]
|
||||
* Improve hover message when the drive is too small [Alexis Svinartchouk]
|
||||
* Update electron to v9.4.0 [Alexis Svinartchouk]
|
||||
* Update npm to v6.14.8 [Giovanni Garufi]
|
||||
* Update rgb leds colors [Alexis Svinartchouk]
|
||||
* Remove unmountOnSuccess setting [Alexis Svinartchouk]
|
||||
* Only show auto-updates setting on supported targets [Alexis Svinartchouk]
|
||||
* Remove dead code in settings modal [Alexis Svinartchouk]
|
||||
* Fix effective flashing speed calculation for compressed images [Alexis Svinartchouk]
|
||||
* Change some border colors to have higher contrast [Lorenzo Alberto Maria Ambrosi]
|
||||
|
||||
<details>
|
||||
<summary> Update etcher-sdk from 5.1.10 to 5.1.11 [Alexis Svinartchouk] </summary>
|
||||
|
||||
> ## etcher-sdk-5.1.11
|
||||
> ### (2020-12-07)
|
||||
>
|
||||
> * Don't use the O_SYNC flag for block devices, only O_DIRECT [Alexis Svinartchouk]
|
||||
>
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary> Update sys-class-rgb-led from 2.1.1 to 3.0.0 [Alexis Svinartchouk] </summary>
|
||||
|
||||
> ## sys-class-rgb-led-3.0.0
|
||||
> ### (2020-12-03)
|
||||
>
|
||||
> * Add example etcher-pro rainbow animation [Alexis Svinartchouk]
|
||||
> * Use one setInterval instead of a loop for each led, t in seconds [Alexis Svinartchouk]
|
||||
>
|
||||
</details>
|
||||
|
||||
# v1.5.112
|
||||
## (2020-12-02)
|
||||
|
||||
* Add rendition and sys-class-rgb-led to repo.yml [Alexis Svinartchouk]
|
||||
|
||||
<details>
|
||||
<summary> Update sys-class-rgb-led from 2.1.0 to 2.1.1 [Alexis Svinartchouk] </summary>
|
||||
|
||||
> ## sys-class-rgb-led-2.1.1
|
||||
> ### (2020-12-01)
|
||||
>
|
||||
> * Replace resin-lint with @balena/lint [Alexis Svinartchouk]
|
||||
> * Update typescript to v4.1.2 [Alexis Svinartchouk]
|
||||
> * Add versionbot changelog [Alexis Svinartchouk]
|
||||
>
|
||||
</details>
|
||||
|
||||
* Fix layout when the featured project is not showing [Alexis Svinartchouk]
|
||||
* Improve flashing error handling [Alexis Svinartchouk]
|
||||
* Fix modal content height on Windows [Alexis Svinartchouk]
|
||||
|
||||
<details>
|
||||
<summary> Update etcher-sdk from 5.1.5 to 5.1.10 [Alexis Svinartchouk] </summary>
|
||||
|
||||
> ## etcher-sdk-5.1.10
|
||||
> ### (2020-12-02)
|
||||
>
|
||||
>
|
||||
> <details>
|
||||
> <summary> Update balena-image-fs from 7.0.5 to 7.0.6 [Alexis Svinartchouk] </summary>
|
||||
>
|
||||
>> ### balena-image-fs-7.0.6
|
||||
>> #### (2020-12-02)
|
||||
>>
|
||||
>>
|
||||
>> <details>
|
||||
>> <summary> Update ext2fs from 3.0.4 to 3.0.5 [Alexis Svinartchouk] </summary>
|
||||
>>
|
||||
>>> #### node-ext2fs-3.0.5
|
||||
>>> ##### (2020-12-02)
|
||||
>>>
|
||||
>>> * Fix reading and discarding with offsets > 32 bits [Alexis Svinartchouk]
|
||||
>>>
|
||||
>> </details>
|
||||
>>
|
||||
>>
|
||||
> </details>
|
||||
>
|
||||
>
|
||||
> ## etcher-sdk-5.1.9
|
||||
> ### (2020-12-01)
|
||||
>
|
||||
> * Add repo.yml file [Alexis Svinartchouk]
|
||||
> * Update @balena/udif from 1.1.0 to 1.1.1 [Alexis Svinartchouk]
|
||||
>
|
||||
> <details>
|
||||
> <summary> Update zip-part-stream from 1.0.2 to 1.0.3 [Alexis Svinartchouk] </summary>
|
||||
>
|
||||
>> ### zip-part-stream-1.0.3
|
||||
>> #### (2020-11-30)
|
||||
>>
|
||||
>> * Add versionbot changelog [Alexis Svinartchouk]
|
||||
>>
|
||||
> </details>
|
||||
>
|
||||
>
|
||||
> <details>
|
||||
> <summary> Update node-raspberrypi-usbboot from 0.2.9 to 0.2.10 [Alexis Svinartchouk] </summary>
|
||||
>
|
||||
>> ### node-raspberrypi-usbboot-0.2.10
|
||||
>> #### (2020-11-30)
|
||||
>>
|
||||
>> * Update typescript to v4.1.2 [Alexis Svinartchouk]
|
||||
>> * Add versionbot changelog [Alexis Svinartchouk]
|
||||
>>
|
||||
> </details>
|
||||
>
|
||||
>
|
||||
> <details>
|
||||
> <summary> Update mountutils from 1.3.19 to 1.3.20 [Alexis Svinartchouk] </summary>
|
||||
>
|
||||
>> ### mountutils-1.3.20
|
||||
>> #### (2020-11-30)
|
||||
>>
|
||||
>> * Add versionbot changelog [Alexis Svinartchouk]
|
||||
>>
|
||||
> </details>
|
||||
>
|
||||
>
|
||||
> <details>
|
||||
> <summary> Update gzip-stream from 1.1.1 to 1.1.2 [Alexis Svinartchouk] </summary>
|
||||
>
|
||||
>> ### gzip-stream-1.1.2
|
||||
>> #### (2020-11-30)
|
||||
>>
|
||||
>> * Add versionbot changelog [Alexis Svinartchouk]
|
||||
>>
|
||||
> </details>
|
||||
>
|
||||
>
|
||||
> <details>
|
||||
> <summary> Update drivelist from 9.2.1 to 9.2.2 [Alexis Svinartchouk] </summary>
|
||||
>
|
||||
>> ### drivelist-9.2.2
|
||||
>> #### (2020-11-30)
|
||||
>>
|
||||
>> * Update typescript to v4.1.2 [Alexis Svinartchouk]
|
||||
>> * Add versionbot changelog [Alexis Svinartchouk]
|
||||
>>
|
||||
> </details>
|
||||
>
|
||||
>
|
||||
> <details>
|
||||
> <summary> Update blockmap from 4.0.2 to 4.0.3 [Alexis Svinartchouk] </summary>
|
||||
>
|
||||
>> ### blockmap-4.0.3
|
||||
>> #### (2020-11-30)
|
||||
>>
|
||||
>> * Update typescript to v4.1.2 [Alexis Svinartchouk]
|
||||
>> * Add versionbot changelog [Alexis Svinartchouk]
|
||||
>>
|
||||
> </details>
|
||||
>
|
||||
>
|
||||
> <details>
|
||||
> <summary> Update partitioninfo from 6.0.1 to 6.0.2 [Alexis Svinartchouk] </summary>
|
||||
>
|
||||
>> ### partitioninfo-6.0.2
|
||||
>> #### (2020-11-27)
|
||||
>>
|
||||
>>
|
||||
>> <details>
|
||||
>> <summary> Update file-disk from 8.0.0 to 8.0.1 [Alexis Svinartchouk] </summary>
|
||||
>>
|
||||
>>> #### file-disk-8.0.1
|
||||
>>> ##### (2020-11-26)
|
||||
>>>
|
||||
>>> * Add versionbot changelog [Alexis Svinartchouk]
|
||||
>>>
|
||||
>> </details>
|
||||
>>
|
||||
>> * Add versionbot changelog [Alexis Svinartchouk]
|
||||
>>
|
||||
> </details>
|
||||
>
|
||||
>
|
||||
> <details>
|
||||
> <summary> Update file-disk from 8.0.0 to 8.0.1 [Alexis Svinartchouk] </summary>
|
||||
>
|
||||
>> ### file-disk-8.0.1
|
||||
>> #### (2020-11-26)
|
||||
>>
|
||||
>> * Add versionbot changelog [Alexis Svinartchouk]
|
||||
>>
|
||||
>> ### file-disk-8.0.1
|
||||
>> #### (2020-11-26)
|
||||
>>
|
||||
>> * Add versionbot changelog [Alexis Svinartchouk]
|
||||
>>
|
||||
> </details>
|
||||
>
|
||||
>
|
||||
> <details>
|
||||
> <summary> Update balena-image-fs from 7.0.4 to 7.0.5 [Alexis Svinartchouk] </summary>
|
||||
>
|
||||
>> ### balena-image-fs-7.0.5
|
||||
>> #### (2020-11-27)
|
||||
>>
|
||||
>>
|
||||
>> <details>
|
||||
>> <summary> Update file-disk from 8.0.0 to 8.0.1 [Alexis Svinartchouk] </summary>
|
||||
>>
|
||||
>>> #### file-disk-8.0.1
|
||||
>>> ##### (2020-11-26)
|
||||
>>>
|
||||
>>> * Add versionbot changelog [Alexis Svinartchouk]
|
||||
>>>
|
||||
>> </details>
|
||||
>>
|
||||
>>
|
||||
>> <details>
|
||||
>> <summary> Update ext2fs from 3.0.3 to 3.0.4 [Alexis Svinartchouk] </summary>
|
||||
>>
|
||||
>>> #### node-ext2fs-3.0.4
|
||||
>>> ##### (2020-11-26)
|
||||
>>>
|
||||
>>> * Add versionbot changelog [Alexis Svinartchouk]
|
||||
>>>
|
||||
>> </details>
|
||||
>>
|
||||
>>
|
||||
>> <details>
|
||||
>> <summary> Update partitioninfo from 6.0.1 to 6.0.2 [Alexis Svinartchouk] </summary>
|
||||
>>
|
||||
>>> #### partitioninfo-6.0.2
|
||||
>>> ##### (2020-11-27)
|
||||
>>>
|
||||
>>>
|
||||
>>> <details>
|
||||
>>> <summary> Update file-disk from 8.0.0 to 8.0.1 [Alexis Svinartchouk] </summary>
|
||||
>>>
|
||||
>>>> ##### file-disk-8.0.1
|
||||
>>>> ###### (2020-11-26)
|
||||
>>>>
|
||||
>>>> * Add versionbot changelog [Alexis Svinartchouk]
|
||||
>>>>
|
||||
>>> </details>
|
||||
>>>
|
||||
>>> * Add versionbot changelog [Alexis Svinartchouk]
|
||||
>>>
|
||||
>> </details>
|
||||
>>
|
||||
>> * Add versionbot changelog [Alexis Svinartchouk]
|
||||
>>
|
||||
> </details>
|
||||
>
|
||||
>
|
||||
> ## etcher-sdk-5.1.8
|
||||
> ### (2020-11-26)
|
||||
>
|
||||
> * Add versionbot changelog [Alexis Svinartchouk]
|
||||
>
|
||||
> ## etcher-sdk-5.1.7
|
||||
> ### (2020-11-25)
|
||||
>
|
||||
> * Don't start opening drives in advance to avoid unhandled rejections [Alexis Svinartchouk]
|
||||
> * Update generated docs [Alexis Svinartchouk]
|
||||
>
|
||||
> ## etcher-sdk-5.1.6
|
||||
> ### (2020-11-24)
|
||||
>
|
||||
> * Do not unmount source drives [Alexis Svinartchouk]
|
||||
> * Factorize retrying transient errors [Alexis Svinartchouk]
|
||||
> * Retry opening files & block devices on transient errors [Alexis Svinartchouk]
|
||||
> * Update generated docs [Alexis Svinartchouk]
|
||||
>
|
||||
</details>
|
||||
|
||||
* Set useContentSize to true so the size is the same on all platforms [Alexis Svinartchouk]
|
||||
|
||||
# v1.5.111
|
||||
## (2020-11-23)
|
||||
|
||||
|
||||
4
FAQ.md
4
FAQ.md
@@ -37,10 +37,10 @@ modules=xwayland.so
|
||||
Sometimes, things might go wrong, and you end up with a half-flashed drive that is unusable by your operating systems, and common graphical tools might even refuse to get it back to a normal state.
|
||||
To solve these kinds of problems, we've collected [a list of fail-proof methods](https://github.com/balena-io/etcher/blob/master/docs/USER-DOCUMENTATION.md#recovering-broken-drives) to completely erase your drive in major operating systems.
|
||||
|
||||
## I receive ”No polkit authentication agent found” error in GNU/Linux
|
||||
## I receive "No polkit authentication agent found" error in GNU/Linux
|
||||
|
||||
Etcher requires an available [polkit authentication agent](https://wiki.archlinux.org/index.php/Polkit#Authentication_agents) in your system in order to show a secure password prompt dialog to perform elevation. Make sure you have one installed for the desktop environment of your choice.
|
||||
|
||||
## May I run Etcher in older macOS versions?
|
||||
|
||||
Etcher GUI is based on the [Electron](http://electron.atom.io/) framework, [which only supports macOS 10.9 and newer versions](https://github.com/electron/electron/blob/master/docs/tutorial/support.md#supported-platforms).
|
||||
Etcher GUI is based on the [Electron](http://electron.atom.io/) framework, [which only supports macOS 10.10 and newer versions](https://github.com/electron/electron/blob/master/docs/tutorial/support.md#supported-platforms).
|
||||
|
||||
2
Makefile
2
Makefile
@@ -3,7 +3,7 @@
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
RESIN_SCRIPTS ?= ./scripts/resin
|
||||
export NPM_VERSION ?= 6.14.5
|
||||
export NPM_VERSION ?= 6.14.8
|
||||
S3_BUCKET = artifacts.ci.balena-cloud.com
|
||||
|
||||
# This directory will be completely deleted by the `clean` rule
|
||||
|
||||
133
README.md
133
README.md
@@ -5,16 +5,16 @@
|
||||
Etcher is a powerful OS image flasher built with web technologies to ensure
|
||||
flashing an SDCard or USB drive is a pleasant and safe experience. It protects
|
||||
you from accidentally writing to your hard-drives, ensures every byte of data
|
||||
was written correctly and much more. It can also flash directly Raspberry Pi devices that support the usbboot protocol
|
||||
was written correctly, and much more. It can also directly flash Raspberry Pi devices that support [USB device boot mode](https://www.raspberrypi.org/documentation/hardware/raspberrypi/bootmodes/device.md).
|
||||
|
||||
[](https://balena.io/etcher)
|
||||
[](https://github.com/balena-io/etcher/blob/master/LICENSE)
|
||||
[](https://david-dm.org/balena-io/etcher)
|
||||
[](https://forums.balena.io/c/etcher)
|
||||
|
||||
***
|
||||
---
|
||||
|
||||
[**Download**][etcher] | [**Support**][SUPPORT] | [**Documentation**][USER-DOCUMENTATION] | [**Contributing**][CONTRIBUTING] | [**Roadmap**][milestones]
|
||||
[**Download**][etcher] | [**Support**][support] | [**Documentation**][user-documentation] | [**Contributing**][contributing] | [**Roadmap**][milestones]
|
||||
|
||||
## Supported Operating Systems
|
||||
|
||||
@@ -22,7 +22,7 @@ was written correctly and much more. It can also flash directly Raspberry Pi dev
|
||||
- macOS 10.10 (Yosemite) and later
|
||||
- Microsoft Windows 7 and later
|
||||
|
||||
Note that Etcher will run on any platform officially supported by
|
||||
**Note**: Etcher will run on any platform officially supported by
|
||||
[Electron][electron]. Read more in their
|
||||
[documentation][electron-supported-platforms].
|
||||
|
||||
@@ -31,81 +31,99 @@ Note that Etcher will run on any platform officially supported by
|
||||
Refer to the [downloads page][etcher] for the latest pre-made
|
||||
installers for all supported operating systems.
|
||||
|
||||
> Note: Our deb and rpm packages are now hosted on [Cloudsmith](https://cloudsmith.com)!
|
||||
|
||||
#### Debian and Ubuntu based Package Repository (GNU/Linux x86/x64)
|
||||
|
||||
1. Add Etcher debian repository:
|
||||
1. Add Etcher Debian repository:
|
||||
|
||||
```sh
|
||||
echo "deb https://deb.etcher.io stable etcher" | sudo tee /etc/apt/sources.list.d/balena-etcher.list
|
||||
```
|
||||
```sh
|
||||
curl -1sLf \
|
||||
'https://dl.cloudsmith.io/public/balena/etcher/setup.deb.sh' \
|
||||
| sudo -E bash
|
||||
```
|
||||
|
||||
2. Trust Bintray.com's GPG key:
|
||||
2. Update and install:
|
||||
|
||||
```sh
|
||||
sudo apt-key adv --keyserver hkps://keyserver.ubuntu.com:443 --recv-keys 379CE192D401AB61
|
||||
```
|
||||
|
||||
3. Update and install:
|
||||
|
||||
```sh
|
||||
sudo apt-get update
|
||||
sudo apt-get install balena-etcher-electron
|
||||
```
|
||||
```sh
|
||||
sudo apt-get update
|
||||
sudo apt-get install balena-etcher-electron
|
||||
```
|
||||
|
||||
##### Uninstall
|
||||
|
||||
```sh
|
||||
sudo apt-get remove balena-etcher-electron
|
||||
sudo rm /etc/apt/sources.list.d/balena-etcher.list
|
||||
sudo apt-get update
|
||||
rm /etc/apt/sources.list.d/balena-etcher.list
|
||||
apt-get clean
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
apt-get update
|
||||
```
|
||||
|
||||
##### OpenSUSE LEAP & Tumbleweed install
|
||||
|
||||
```sh
|
||||
sudo zypper ar https://balena.io/etcher/static/etcher-rpm.repo
|
||||
sudo zypper ref
|
||||
sudo zypper in balena-etcher-electron
|
||||
curl -1sLf \
|
||||
'https://dl.cloudsmith.io/public/balena/etcher/setup.rpm.sh' \
|
||||
| sudo -E bash
|
||||
```
|
||||
|
||||
##### Uninstall
|
||||
|
||||
```sh
|
||||
sudo zypper rm balena-etcher-electron
|
||||
zypper rr balena-etcher
|
||||
zypper rr balena-etcher-source
|
||||
```
|
||||
|
||||
#### Redhat (RHEL) and Fedora based Package Repository (GNU/Linux x86/x64)
|
||||
#### Redhat (RHEL) and Fedora-based Package Repository (GNU/Linux x86/x64)
|
||||
|
||||
##### DNF
|
||||
|
||||
1. Add Etcher rpm repository:
|
||||
|
||||
```sh
|
||||
sudo wget https://balena.io/etcher/static/etcher-rpm.repo -O /etc/yum.repos.d/etcher-rpm.repo
|
||||
```
|
||||
```sh
|
||||
curl -1sLf \
|
||||
'https://dl.cloudsmith.io/public/balena/etcher/setup.rpm.sh' \
|
||||
| sudo -E bash
|
||||
```
|
||||
|
||||
2. Update and install:
|
||||
|
||||
```sh
|
||||
sudo yum install -y balena-etcher-electron
|
||||
```
|
||||
or
|
||||
```sh
|
||||
sudo dnf install -y balena-etcher-electron
|
||||
```
|
||||
```sh
|
||||
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
|
||||
|
||||
```sh
|
||||
rm /etc/yum.repos.d/balena-etcher.repo
|
||||
rm /etc/yum.repos.d/balena-etcher-source.repo
|
||||
```
|
||||
|
||||
##### Yum
|
||||
|
||||
1. Add Etcher rpm repository:
|
||||
|
||||
```sh
|
||||
curl -1sLf \
|
||||
'https://dl.cloudsmith.io/public/balena/etcher/setup.rpm.sh' \
|
||||
| sudo -E bash
|
||||
```
|
||||
|
||||
2. Update and install:
|
||||
|
||||
```sh
|
||||
sudo yum install -y balena-etcher-electron
|
||||
```
|
||||
|
||||
###### Uninstall
|
||||
|
||||
```sh
|
||||
sudo yum remove -y balena-etcher-electron
|
||||
sudo rm /etc/yum.repos.d/etcher-rpm.repo
|
||||
sudo yum clean all
|
||||
sudo yum makecache fast
|
||||
```
|
||||
or
|
||||
```sh
|
||||
sudo dnf remove -y balena-etcher-electron
|
||||
sudo rm /etc/yum.repos.d/etcher-rpm.repo
|
||||
sudo dnf clean all
|
||||
sudo dnf makecache
|
||||
rm /etc/yum.repos.d/balena-etcher.repo
|
||||
rm /etc/yum.repos.d/balena-etcher-source.repo
|
||||
```
|
||||
|
||||
#### Solus (GNU/Linux x64)
|
||||
@@ -120,11 +138,10 @@ sudo eopkg it etcher
|
||||
sudo eopkg rm etcher
|
||||
```
|
||||
|
||||
#### Arch Linux / Manjaro (GNU/Linux x64)
|
||||
#### Arch/Manjaro Linux (GNU/Linux x64)
|
||||
|
||||
Etcher is offered through the Arch User Repository and can be installed on both Manjaro and Arch systems. You can compile it from the source code in this repository using [`balena-etcher`](https://aur.archlinux.org/packages/balena-etcher/). The following example uses a common AUR helper to install the latest release:
|
||||
|
||||
|
||||
```sh
|
||||
yay -S balena-etcher
|
||||
```
|
||||
@@ -135,20 +152,20 @@ yay -S balena-etcher
|
||||
yay -R balena-etcher
|
||||
```
|
||||
|
||||
#### Brew Cask (macOS)
|
||||
#### Brew (macOS)
|
||||
|
||||
Note that the Etcher Cask has to be updated manually to point to new versions,
|
||||
**Note**: Etcher has to be updated manually to point to new versions,
|
||||
so it might not refer to the latest version immediately after an Etcher
|
||||
release.
|
||||
|
||||
```sh
|
||||
brew cask install balenaetcher
|
||||
brew install balenaetcher
|
||||
```
|
||||
|
||||
##### Uninstall
|
||||
|
||||
```sh
|
||||
brew cask uninstall balenaetcher
|
||||
brew uninstall balenaetcher
|
||||
```
|
||||
|
||||
#### Chocolatey (Windows)
|
||||
@@ -168,20 +185,20 @@ choco uninstall etcher
|
||||
|
||||
## Support
|
||||
|
||||
If you're having any problem, please [raise an issue][newissue] on GitHub and
|
||||
If you're having any problem, please [raise an issue][newissue] on GitHub, and
|
||||
the balena.io team will be happy to help.
|
||||
|
||||
## License
|
||||
|
||||
Etcher is free software, and may be redistributed under the terms specified in
|
||||
Etcher is free software and may be redistributed under the terms specified in
|
||||
the [license].
|
||||
|
||||
[etcher]: https://balena.io/etcher
|
||||
[electron]: https://electronjs.org/
|
||||
[electron-supported-platforms]: https://electronjs.org/docs/tutorial/support#supported-platforms
|
||||
[SUPPORT]: https://github.com/balena-io/etcher/blob/master/SUPPORT.md
|
||||
[CONTRIBUTING]: https://github.com/balena-io/etcher/blob/master/docs/CONTRIBUTING.md
|
||||
[USER-DOCUMENTATION]: https://github.com/balena-io/etcher/blob/master/docs/USER-DOCUMENTATION.md
|
||||
[support]: https://github.com/balena-io/etcher/blob/master/SUPPORT.md
|
||||
[contributing]: https://github.com/balena-io/etcher/blob/master/docs/CONTRIBUTING.md
|
||||
[user-documentation]: https://github.com/balena-io/etcher/blob/master/docs/USER-DOCUMENTATION.md
|
||||
[milestones]: https://github.com/balena-io/etcher/milestones
|
||||
[newissue]: https://github.com/balena-io/etcher/issues/new
|
||||
[license]: https://github.com/balena-io/etcher/blob/master/LICENSE
|
||||
|
||||
@@ -4,6 +4,13 @@ Getting help with Etcher
|
||||
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.
|
||||
|
||||
Documentation
|
||||
------
|
||||
|
||||
We have answers to a variety of frequently asked questions in the [user
|
||||
documentation][documentation] and also in the [FAQs][faq] on the Etcher website.
|
||||
|
||||
|
||||
Forums
|
||||
------
|
||||
|
||||
@@ -32,3 +39,5 @@ one][new-issue].
|
||||
[discourse]: https://forums.balena.io/c/etcher
|
||||
[issues]: https://github.com/balena-io/etcher/issues
|
||||
[new-issue]: https://github.com/balena-io/etcher/issues/new
|
||||
[documentation]: https://github.com/balena-io/etcher/blob/master/docs/USER-DOCUMENTATION.md
|
||||
[faq]: https://etcher.io
|
||||
|
||||
@@ -6,21 +6,28 @@ 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 (context.platform.name === 'windows') {
|
||||
cp.execFileSync(
|
||||
'bash',
|
||||
['./node_modules/.bin/electron-rebuild', '--types', 'dev', '--arch', context.arch],
|
||||
);
|
||||
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(
|
||||
'bash',
|
||||
['./node_modules/.bin/webpack'],
|
||||
cp.execFileSync(
|
||||
run,
|
||||
['node_modules/.bin/webpack'],
|
||||
{
|
||||
env: {
|
||||
...process.env,
|
||||
npm_config_target_arch: context.arch,
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
appId: io.balena.etcher
|
||||
copyright: Copyright 2016-2020 Balena Ltd
|
||||
copyright: Copyright 2016-2021 Balena Ltd
|
||||
productName: balenaEtcher
|
||||
npmRebuild: true
|
||||
nodeGypRebuild: false
|
||||
@@ -16,6 +16,7 @@ mac:
|
||||
hardenedRuntime: true
|
||||
entitlements: "entitlements.mac.plist"
|
||||
entitlementsInherit: "entitlements.mac.plist"
|
||||
artifactName: "${productName}-${version}.${ext}"
|
||||
dmg:
|
||||
background: assets/dmg/background.tiff
|
||||
icon: assets/icon.icns
|
||||
@@ -54,7 +55,6 @@ deb:
|
||||
depends:
|
||||
- gconf2
|
||||
- gconf-service
|
||||
- libappindicator1
|
||||
- libasound2
|
||||
- libatk1.0-0
|
||||
- libc6
|
||||
|
||||
@@ -38,6 +38,7 @@ import * as exceptionReporter from './modules/exception-reporter';
|
||||
import * as osDialog from './os/dialog';
|
||||
import * as windowProgress from './os/window-progress';
|
||||
import MainPage from './pages/main/MainPage';
|
||||
import './css/main.css';
|
||||
|
||||
window.addEventListener(
|
||||
'unhandledrejection',
|
||||
@@ -339,7 +340,7 @@ window.addEventListener('beforeunload', async (event) => {
|
||||
}
|
||||
});
|
||||
|
||||
async function main() {
|
||||
export async function main() {
|
||||
await ledsInit();
|
||||
ReactDOM.render(
|
||||
React.createElement(MainPage),
|
||||
@@ -356,5 +357,3 @@ async function main() {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
main();
|
||||
|
||||
@@ -303,9 +303,9 @@ export class DriveSelector extends React.Component<
|
||||
case compatibility.system():
|
||||
return warning.systemDrive();
|
||||
case compatibility.tooSmall():
|
||||
const recommendedDriveSize =
|
||||
const size =
|
||||
this.state.image?.recommendedDriveSize || this.state.image?.size || 0;
|
||||
return warning.unrecommendedDriveSize({ recommendedDriveSize }, drive);
|
||||
return warning.tooSmall({ size }, drive);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
import CircleSvg from '@fortawesome/fontawesome-free/svgs/solid/circle.svg';
|
||||
import CheckCircleSvg from '@fortawesome/fontawesome-free/svgs/solid/check-circle.svg';
|
||||
import TimesCircleSvg from '@fortawesome/fontawesome-free/svgs/solid/times-circle.svg';
|
||||
import * as _ from 'lodash';
|
||||
import outdent from 'outdent';
|
||||
import * as React from 'react';
|
||||
import { Flex, FlexProps, Link, TableColumn, Txt } from 'rendition';
|
||||
@@ -104,6 +103,19 @@ const columns: Array<TableColumn<FlashError>> = [
|
||||
},
|
||||
];
|
||||
|
||||
function getEffectiveSpeed(results: {
|
||||
sourceMetadata: {
|
||||
size: number;
|
||||
blockmappedSize?: number;
|
||||
};
|
||||
averageFlashingSpeed: number;
|
||||
}) {
|
||||
const flashedSize =
|
||||
results.sourceMetadata.blockmappedSize ?? results.sourceMetadata.size;
|
||||
const timeSpent = flashedSize / results.averageFlashingSpeed;
|
||||
return results.sourceMetadata.size / timeSpent;
|
||||
}
|
||||
|
||||
export function FlashResults({
|
||||
goToMain,
|
||||
image = '',
|
||||
@@ -117,23 +129,18 @@ export function FlashResults({
|
||||
errors: FlashError[];
|
||||
skip: boolean;
|
||||
results: {
|
||||
bytesWritten: number;
|
||||
sourceMetadata: {
|
||||
size: number;
|
||||
blockmappedSize: number;
|
||||
blockmappedSize?: number;
|
||||
};
|
||||
averageFlashingSpeed: number;
|
||||
devices: { failed: number; successful: number };
|
||||
};
|
||||
} & FlexProps) {
|
||||
const [showErrorsInfo, setShowErrorsInfo] = React.useState(false);
|
||||
const allFailed = results.devices.successful === 0;
|
||||
const allFailed = !skip && results.devices.successful === 0;
|
||||
const someFailed = results.devices.failed !== 0 || errors.length !== 0;
|
||||
const effectiveSpeed = _.round(
|
||||
bytesToMegabytes(
|
||||
results.sourceMetadata.size /
|
||||
(results.bytesWritten / results.averageFlashingSpeed),
|
||||
),
|
||||
const effectiveSpeed = bytesToMegabytes(getEffectiveSpeed(results)).toFixed(
|
||||
1,
|
||||
);
|
||||
return (
|
||||
@@ -155,7 +162,7 @@ export function FlashResults({
|
||||
<Txt>{middleEllipsis(image, 24)}</Txt>
|
||||
</Flex>
|
||||
<Txt fontSize={24} color="#fff" mb="17px">
|
||||
Flash Complete!
|
||||
Flash {allFailed ? 'Failed' : 'Complete'}!
|
||||
</Txt>
|
||||
{skip ? <Txt color="#7e8085">Validation has been skipped</Txt> : null}
|
||||
</Flex>
|
||||
|
||||
@@ -23,7 +23,7 @@ import { StepButton } from '../../styled-components';
|
||||
|
||||
const FlashProgressBar = styled(ProgressBar)`
|
||||
> div {
|
||||
width: 220px;
|
||||
width: 100%;
|
||||
height: 12px;
|
||||
color: white !important;
|
||||
text-shadow: none !important;
|
||||
@@ -33,7 +33,7 @@ const FlashProgressBar = styled(ProgressBar)`
|
||||
}
|
||||
}
|
||||
|
||||
width: 220px;
|
||||
width: 100%;
|
||||
height: 12px;
|
||||
margin-bottom: 6px;
|
||||
border-radius: 14px;
|
||||
|
||||
@@ -31,9 +31,7 @@ interface ReducedFlashingInfosProps {
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
export class ReducedFlashingInfos extends React.Component<
|
||||
ReducedFlashingInfosProps
|
||||
> {
|
||||
export class ReducedFlashingInfos extends React.Component<ReducedFlashingInfosProps> {
|
||||
constructor(props: ReducedFlashingInfosProps) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
import GithubSvg from '@fortawesome/fontawesome-free/svgs/brands/github.svg';
|
||||
import * as _ from 'lodash';
|
||||
import * as os from 'os';
|
||||
import * as React from 'react';
|
||||
import { Flex, Checkbox, Txt } from 'rendition';
|
||||
|
||||
@@ -26,40 +25,25 @@ import * as analytics from '../../modules/analytics';
|
||||
import { open as openExternal } from '../../os/open-external/services/open-external';
|
||||
import { Modal } from '../../styled-components';
|
||||
|
||||
const platform = os.platform();
|
||||
|
||||
interface Setting {
|
||||
name: string;
|
||||
label: string | JSX.Element;
|
||||
options?: {
|
||||
description: string;
|
||||
confirmLabel: string;
|
||||
};
|
||||
hide?: boolean;
|
||||
}
|
||||
|
||||
async function getSettingsList(): Promise<Setting[]> {
|
||||
return [
|
||||
const list: Setting[] = [
|
||||
{
|
||||
name: 'errorReporting',
|
||||
label: 'Anonymously report errors and usage statistics to balena.io',
|
||||
},
|
||||
{
|
||||
name: 'unmountOnSuccess',
|
||||
/**
|
||||
* On Windows, "Unmounting" basically means "ejecting".
|
||||
* On top of that, Windows users are usually not even
|
||||
* familiar with the meaning of "unmount", which comes
|
||||
* from the UNIX world.
|
||||
*/
|
||||
label: `${platform === 'win32' ? 'Eject' : 'Auto-unmount'} on success`,
|
||||
},
|
||||
{
|
||||
];
|
||||
if (['appimage', 'nsis', 'dmg'].includes(packageType)) {
|
||||
list.push({
|
||||
name: 'updatesEnabled',
|
||||
label: 'Auto-updates enabled',
|
||||
hide: ['rpm', 'deb'].includes(packageType),
|
||||
},
|
||||
];
|
||||
});
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
interface SettingsModalProps {
|
||||
@@ -86,25 +70,14 @@ export function SettingsModal({ toggleModal }: SettingsModalProps) {
|
||||
})();
|
||||
});
|
||||
|
||||
const toggleSetting = async (
|
||||
setting: string,
|
||||
options?: Setting['options'],
|
||||
) => {
|
||||
const toggleSetting = async (setting: string) => {
|
||||
const value = currentSettings[setting];
|
||||
const dangerous = options !== undefined;
|
||||
|
||||
analytics.logEvent('Toggle setting', {
|
||||
setting,
|
||||
value,
|
||||
dangerous,
|
||||
});
|
||||
|
||||
analytics.logEvent('Toggle setting', { setting, value });
|
||||
await settings.set(setting, !value);
|
||||
setCurrentSettings({
|
||||
...currentSettings,
|
||||
[setting]: !value,
|
||||
});
|
||||
return;
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -118,14 +91,14 @@ export function SettingsModal({ toggleModal }: SettingsModalProps) {
|
||||
>
|
||||
<Flex flexDirection="column">
|
||||
{settingsList.map((setting: Setting, i: number) => {
|
||||
return setting.hide ? null : (
|
||||
return (
|
||||
<Flex key={setting.name} mb={14}>
|
||||
<Checkbox
|
||||
toggle
|
||||
tabIndex={6 + i}
|
||||
label={setting.label}
|
||||
checked={currentSettings[setting.name]}
|
||||
onChange={() => toggleSetting(setting.name, setting.options)}
|
||||
onChange={() => toggleSetting(setting.name)}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
|
||||
12
lib/gui/app/index.dev.html
Normal file
12
lib/gui/app/index.dev.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Etcher</title>
|
||||
<link rel="stylesheet" type="text/css" href="index.css">
|
||||
</head>
|
||||
<body>
|
||||
<main id="main"></main>
|
||||
<script src="http://localhost:3030/gui.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as electron from 'electron';
|
||||
import * as sdk from 'etcher-sdk';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
@@ -45,6 +46,8 @@ export function isFlashing(): boolean {
|
||||
* start a flash process.
|
||||
*/
|
||||
export function setFlashingFlag() {
|
||||
// see https://github.com/balenablocks/balena-electron-env/blob/4fce9c461f294d4a768db8f247eea6f75d7b08b0/README.md#remote-methods
|
||||
electron.ipcRenderer.send('disable-screensaver');
|
||||
store.dispatch({
|
||||
type: Actions.SET_FLASHING_FLAG,
|
||||
data: {},
|
||||
@@ -66,6 +69,8 @@ export function unsetFlashingFlag(results: {
|
||||
type: Actions.UNSET_FLASHING_FLAG,
|
||||
data: results,
|
||||
});
|
||||
// see https://github.com/balenablocks/balena-electron-env/blob/4fce9c461f294d4a768db8f247eea6f75d7b08b0/README.md#remote-methods
|
||||
electron.ipcRenderer.send('enable-screensaver');
|
||||
}
|
||||
|
||||
export function setDevicePaths(devicePaths: string[]) {
|
||||
@@ -85,6 +90,10 @@ export function addFailedDeviceError({
|
||||
const failedDeviceErrorsMap = new Map(
|
||||
store.getState().toJS().failedDeviceErrors,
|
||||
);
|
||||
if (failedDeviceErrorsMap.has(device.device)) {
|
||||
// Only store the first error
|
||||
return;
|
||||
}
|
||||
failedDeviceErrorsMap.set(device.device, {
|
||||
description: device.description,
|
||||
device: device.device,
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import * as _ from 'lodash';
|
||||
import { AnimationFunction, Color, RGBLed } from 'sys-class-rgb-led';
|
||||
import { Animator, AnimationFunction, Color, RGBLed } from 'sys-class-rgb-led';
|
||||
|
||||
import {
|
||||
isSourceDrive,
|
||||
@@ -25,30 +25,14 @@ import * as settings from './settings';
|
||||
import { DEFAULT_STATE, observe } from './store';
|
||||
|
||||
const leds: Map<string, RGBLed> = new Map();
|
||||
const animator = new Animator([], 10);
|
||||
|
||||
function setLeds(
|
||||
drivesPaths: Set<string>,
|
||||
colorOrAnimation: Color | AnimationFunction,
|
||||
frequency?: number,
|
||||
) {
|
||||
for (const path of drivesPaths) {
|
||||
const led = leds.get(path);
|
||||
if (led) {
|
||||
if (Array.isArray(colorOrAnimation)) {
|
||||
led.setStaticColor(colorOrAnimation);
|
||||
} else {
|
||||
led.setAnimation(colorOrAnimation, frequency);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const red: Color = [1, 0, 0];
|
||||
const green: Color = [0, 1, 0];
|
||||
const blue: Color = [0, 0, 1];
|
||||
const white: Color = [1, 1, 1];
|
||||
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.5, 0, 0.5];
|
||||
const purple: Color = [0.117, 0, 0.196];
|
||||
|
||||
function createAnimationFunction(
|
||||
intensityFunction: (t: number) => number,
|
||||
@@ -61,16 +45,20 @@ function createAnimationFunction(
|
||||
}
|
||||
|
||||
function blink(t: number) {
|
||||
return Math.floor(t / 1000) % 2;
|
||||
return Math.floor(t) % 2;
|
||||
}
|
||||
|
||||
function breathe(t: number) {
|
||||
return (1 + Math.sin(t / 1000)) / 2;
|
||||
function one(_t: number) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const breatheBlue = createAnimationFunction(breathe, blue);
|
||||
const blinkGreen = createAnimationFunction(blink, green);
|
||||
const blinkPurple = createAnimationFunction(blink, purple);
|
||||
const staticRed = createAnimationFunction(one, red);
|
||||
const staticGreen = createAnimationFunction(one, green);
|
||||
const staticBlue = createAnimationFunction(one, blue);
|
||||
const staticWhite = createAnimationFunction(one, white);
|
||||
const staticBlack = createAnimationFunction(one, black);
|
||||
|
||||
interface LedsState {
|
||||
step: 'main' | 'flashing' | 'verifying' | 'finish';
|
||||
@@ -80,6 +68,17 @@ interface LedsState {
|
||||
failedDrives: string[];
|
||||
}
|
||||
|
||||
function setLeds(animation: AnimationFunction, drivesPaths: Set<string>) {
|
||||
const rgbLeds: RGBLed[] = [];
|
||||
for (const path of drivesPaths) {
|
||||
const led = leds.get(path);
|
||||
if (led) {
|
||||
rgbLeds.push(led);
|
||||
}
|
||||
}
|
||||
return { animation, rgbLeds };
|
||||
}
|
||||
|
||||
// Source slot (1st slot): behaves as a target unless it is chosen as source
|
||||
// No drive: black
|
||||
// Drive plugged: blue - on
|
||||
@@ -110,6 +109,7 @@ export function updateLeds({
|
||||
// Remove selected devices from plugged set
|
||||
for (const d of selectedOk) {
|
||||
plugged.delete(d);
|
||||
unplugged.delete(d);
|
||||
}
|
||||
|
||||
// Remove plugged devices from unplugged set
|
||||
@@ -122,38 +122,42 @@ export function updateLeds({
|
||||
selectedOk.delete(d);
|
||||
}
|
||||
|
||||
const mapping: Array<{
|
||||
animation: AnimationFunction;
|
||||
rgbLeds: RGBLed[];
|
||||
}> = [];
|
||||
// Handle source slot
|
||||
if (sourceDrive !== undefined) {
|
||||
if (unplugged.has(sourceDrive)) {
|
||||
unplugged.delete(sourceDrive);
|
||||
// TODO
|
||||
setLeds(new Set([sourceDrive]), breatheBlue, 2);
|
||||
} else if (plugged.has(sourceDrive)) {
|
||||
if (plugged.has(sourceDrive)) {
|
||||
plugged.delete(sourceDrive);
|
||||
setLeds(new Set([sourceDrive]), blue);
|
||||
mapping.push(setLeds(staticBlue, new Set([sourceDrive])));
|
||||
}
|
||||
}
|
||||
if (step === 'main') {
|
||||
setLeds(unplugged, black);
|
||||
setLeds(plugged, black);
|
||||
setLeds(selectedOk, white);
|
||||
setLeds(selectedFailed, white);
|
||||
mapping.push(
|
||||
setLeds(staticBlack, new Set([...unplugged, ...plugged])),
|
||||
setLeds(staticWhite, new Set([...selectedOk, ...selectedFailed])),
|
||||
);
|
||||
} else if (step === 'flashing') {
|
||||
setLeds(unplugged, black);
|
||||
setLeds(plugged, black);
|
||||
setLeds(selectedOk, blinkPurple, 2);
|
||||
setLeds(selectedFailed, red);
|
||||
mapping.push(
|
||||
setLeds(staticBlack, new Set([...unplugged, ...plugged])),
|
||||
setLeds(blinkPurple, selectedOk),
|
||||
setLeds(staticRed, selectedFailed),
|
||||
);
|
||||
} else if (step === 'verifying') {
|
||||
setLeds(unplugged, black);
|
||||
setLeds(plugged, black);
|
||||
setLeds(selectedOk, blinkGreen, 2);
|
||||
setLeds(selectedFailed, red);
|
||||
mapping.push(
|
||||
setLeds(staticBlack, new Set([...unplugged, ...plugged])),
|
||||
setLeds(blinkGreen, selectedOk),
|
||||
setLeds(staticRed, selectedFailed),
|
||||
);
|
||||
} else if (step === 'finish') {
|
||||
setLeds(unplugged, black);
|
||||
setLeds(plugged, black);
|
||||
setLeds(selectedOk, green);
|
||||
setLeds(selectedFailed, red);
|
||||
mapping.push(
|
||||
setLeds(staticBlack, new Set([...unplugged, ...plugged])),
|
||||
setLeds(staticGreen, selectedOk),
|
||||
setLeds(staticRed, selectedFailed),
|
||||
);
|
||||
}
|
||||
animator.mapping = mapping;
|
||||
}
|
||||
|
||||
interface DeviceFromState {
|
||||
@@ -189,7 +193,7 @@ function stateObserver(state: typeof DEFAULT_STATE) {
|
||||
selectedDrivesPaths = s.devicePaths;
|
||||
}
|
||||
const failedDevicePaths = s.failedDeviceErrors.map(
|
||||
([devicePath]: [string]) => devicePath,
|
||||
([, { devicePath }]: [string, { devicePath: string }]) => devicePath,
|
||||
);
|
||||
const newLedsState = {
|
||||
step,
|
||||
|
||||
@@ -77,8 +77,7 @@ export async function writeConfigFile(
|
||||
|
||||
const DEFAULT_SETTINGS: _.Dictionary<any> = {
|
||||
errorReporting: true,
|
||||
unmountOnSuccess: true,
|
||||
updatesEnabled: !_.includes(['rpm', 'deb'], packageJSON.packageType),
|
||||
updatesEnabled: ['appimage', 'nsis', 'dmg'].includes(packageJSON.packageType),
|
||||
desktopNotifications: true,
|
||||
autoBlockmapping: true,
|
||||
decompressFirst: true,
|
||||
|
||||
@@ -15,9 +15,8 @@
|
||||
*/
|
||||
|
||||
import { Drive as DrivelistDrive } from 'drivelist';
|
||||
import * as electron from 'electron';
|
||||
import * as sdk from 'etcher-sdk';
|
||||
import * as _ from 'lodash';
|
||||
import { Dictionary } from 'lodash';
|
||||
import * as ipc from 'node-ipc';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
@@ -25,6 +24,7 @@ import * as path from 'path';
|
||||
import * as packageJSON from '../../../../package.json';
|
||||
import * as errors from '../../../shared/errors';
|
||||
import * as permissions from '../../../shared/permissions';
|
||||
import { getAppPath } from '../../../shared/utils';
|
||||
import { SourceMetadata } from '../components/source-selector/source-selector';
|
||||
import * as flashState from '../models/flash-state';
|
||||
import * as selectionState from '../models/selection-state';
|
||||
@@ -93,11 +93,7 @@ function terminateServer() {
|
||||
}
|
||||
|
||||
function writerArgv(): string[] {
|
||||
let entryPoint = path.join(
|
||||
electron.remote.app.getAppPath(),
|
||||
'generated',
|
||||
'child-writer.js',
|
||||
);
|
||||
let entryPoint = path.join(getAppPath(), 'generated', 'child-writer.js');
|
||||
// AppImages run over FUSE, so the files inside the mount point
|
||||
// can only be accessed by the user that mounted the AppImage.
|
||||
// This means we can't re-spawn Etcher as root from the same
|
||||
@@ -133,6 +129,14 @@ function writerEnv() {
|
||||
interface FlashResults {
|
||||
skip?: boolean;
|
||||
cancelled?: boolean;
|
||||
results?: {
|
||||
bytesWritten: number;
|
||||
devices: {
|
||||
failed: number;
|
||||
successful: number;
|
||||
};
|
||||
errors: Error[];
|
||||
};
|
||||
}
|
||||
|
||||
async function performWrite(
|
||||
@@ -143,11 +147,7 @@ async function performWrite(
|
||||
let cancelled = false;
|
||||
let skip = false;
|
||||
ipc.serve();
|
||||
const {
|
||||
unmountOnSuccess,
|
||||
autoBlockmapping,
|
||||
decompressFirst,
|
||||
} = await settings.getAll();
|
||||
const { autoBlockmapping, decompressFirst } = await settings.getAll();
|
||||
return await new Promise((resolve, reject) => {
|
||||
ipc.server.on('error', (error) => {
|
||||
terminateServer();
|
||||
@@ -166,7 +166,6 @@ async function performWrite(
|
||||
driveCount: drives.length,
|
||||
uuid: flashState.getFlashUuid(),
|
||||
flashInstanceUuid: flashState.getFlashUuid(),
|
||||
unmountOnSuccess,
|
||||
};
|
||||
|
||||
ipc.server.on('fail', ({ device, error }) => {
|
||||
@@ -177,10 +176,12 @@ async function performWrite(
|
||||
});
|
||||
|
||||
ipc.server.on('done', (event) => {
|
||||
event.results.errors = _.map(event.results.errors, (data) => {
|
||||
return errors.fromJSON(data);
|
||||
});
|
||||
_.merge(flashResults, event);
|
||||
event.results.errors = event.results.errors.map(
|
||||
(data: Dictionary<any> & { message: string }) => {
|
||||
return errors.fromJSON(data);
|
||||
},
|
||||
);
|
||||
flashResults.results = event.results;
|
||||
});
|
||||
|
||||
ipc.server.on('abort', () => {
|
||||
@@ -201,7 +202,6 @@ async function performWrite(
|
||||
destinations: drives,
|
||||
SourceType: image.SourceType.name,
|
||||
autoBlockmapping,
|
||||
unmountOnSuccess,
|
||||
decompressFirst,
|
||||
});
|
||||
});
|
||||
@@ -209,7 +209,7 @@ async function performWrite(
|
||||
const argv = writerArgv();
|
||||
|
||||
ipc.server.on('start', async () => {
|
||||
console.log(`Elevating command: ${_.join(argv, ' ')}`);
|
||||
console.log(`Elevating command: ${argv.join(' ')}`);
|
||||
const env = writerEnv();
|
||||
try {
|
||||
const results = await permissions.elevateCommand(argv, {
|
||||
@@ -231,11 +231,11 @@ async function performWrite(
|
||||
}
|
||||
console.log('Flash results', flashResults);
|
||||
|
||||
// This likely means the child died halfway through
|
||||
// The flash wasn't cancelled and we didn't get a 'done' event
|
||||
if (
|
||||
!flashResults.cancelled &&
|
||||
!flashResults.skip &&
|
||||
!_.get(flashResults, ['results', 'bytesWritten'])
|
||||
flashResults.results === undefined
|
||||
) {
|
||||
reject(
|
||||
errors.createUserError({
|
||||
@@ -268,7 +268,7 @@ export async function flash(
|
||||
throw new Error('There is already a flash in progress');
|
||||
}
|
||||
|
||||
flashState.setFlashingFlag();
|
||||
await flashState.setFlashingFlag();
|
||||
flashState.setDevicePaths(
|
||||
drives.map((d) => d.devicePath).filter((p) => p != null) as string[],
|
||||
);
|
||||
@@ -280,16 +280,18 @@ export async function flash(
|
||||
uuid: flashState.getFlashUuid(),
|
||||
status: 'started',
|
||||
flashInstanceUuid: flashState.getFlashUuid(),
|
||||
unmountOnSuccess: await settings.get('unmountOnSuccess'),
|
||||
};
|
||||
|
||||
analytics.logEvent('Flash', analyticsData);
|
||||
|
||||
try {
|
||||
const result = await write(image, drives, flashState.setProgressState);
|
||||
flashState.unsetFlashingFlag(result);
|
||||
await flashState.unsetFlashingFlag(result);
|
||||
} catch (error) {
|
||||
flashState.unsetFlashingFlag({ cancelled: false, errorCode: error.code });
|
||||
await flashState.unsetFlashingFlag({
|
||||
cancelled: false,
|
||||
errorCode: error.code,
|
||||
});
|
||||
windowProgress.clear();
|
||||
const { results = {} } = flashState.getFlashResults();
|
||||
const eventData = {
|
||||
@@ -335,7 +337,6 @@ export async function cancel(type: string) {
|
||||
driveCount: drives.length,
|
||||
uuid: flashState.getFlashUuid(),
|
||||
flashInstanceUuid: flashState.getFlashUuid(),
|
||||
unmountOnSuccess: await settings.get('unmountOnSuccess'),
|
||||
status,
|
||||
};
|
||||
analytics.logEvent('Cancel', analyticsData);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
import { exec } from 'child_process';
|
||||
import { withTmpFile } from 'etcher-sdk/build/tmp';
|
||||
import { readFile } from 'fs';
|
||||
import { chain, trim } from 'lodash';
|
||||
import { platform } from 'os';
|
||||
@@ -22,8 +23,6 @@ import { join } from 'path';
|
||||
import { env } from 'process';
|
||||
import { promisify } from 'util';
|
||||
|
||||
import { withTmpFile } from '../../../shared/tmp';
|
||||
|
||||
const readFileAsync = promisify(readFile);
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
@@ -41,11 +40,11 @@ async function getWmicNetworkDrivesOutput(): Promise<string> {
|
||||
// So we just redirect to a file and read it afterwards as we know it will be ucs2 encoded.
|
||||
const options = {
|
||||
// Close the file once it's created
|
||||
discardDescriptor: true,
|
||||
keepOpen: false,
|
||||
// Wmic fails with "Invalid global switch" when the "/output:" switch filename contains a dash ("-")
|
||||
prefix: 'tmp',
|
||||
};
|
||||
return withTmpFile(options, async (path) => {
|
||||
return withTmpFile(options, async ({ path }) => {
|
||||
const command = [
|
||||
join(env.SystemRoot as string, 'System32', 'Wbem', 'wmic'),
|
||||
'path',
|
||||
|
||||
@@ -59,6 +59,27 @@ const getErrorMessageFromCode = (errorCode: string) => {
|
||||
return '';
|
||||
};
|
||||
|
||||
function notifySuccess(
|
||||
iconPath: string,
|
||||
basename: string,
|
||||
drives: any,
|
||||
devices: { successful: number; failed: number },
|
||||
) {
|
||||
notification.send(
|
||||
'Flash complete!',
|
||||
messages.info.flashComplete(basename, drives, devices),
|
||||
iconPath,
|
||||
);
|
||||
}
|
||||
|
||||
function notifyFailure(iconPath: string, basename: string, drives: any) {
|
||||
notification.send(
|
||||
'Oops! Looks like the flash failed.',
|
||||
messages.error.flashFailure(basename, drives),
|
||||
iconPath,
|
||||
);
|
||||
}
|
||||
|
||||
async function flashImageToDrive(
|
||||
isFlashing: boolean,
|
||||
goToSuccess: () => void,
|
||||
@@ -84,20 +105,20 @@ async function flashImageToDrive(
|
||||
if (!flashState.wasLastFlashCancelled()) {
|
||||
const {
|
||||
results = { devices: { successful: 0, failed: 0 } },
|
||||
skip,
|
||||
cancelled,
|
||||
} = flashState.getFlashResults();
|
||||
notification.send(
|
||||
'Flash complete!',
|
||||
messages.info.flashComplete(basename, drives as any, results.devices),
|
||||
iconPath,
|
||||
);
|
||||
if (!skip && !cancelled) {
|
||||
if (results.devices.successful > 0) {
|
||||
notifySuccess(iconPath, basename, drives, results.devices);
|
||||
} else {
|
||||
notifyFailure(iconPath, basename, drives);
|
||||
}
|
||||
}
|
||||
goToSuccess();
|
||||
}
|
||||
} catch (error) {
|
||||
notification.send(
|
||||
'Oops! Looks like the flash failed.',
|
||||
messages.error.flashFailure(path.basename(image.path), drives),
|
||||
iconPath,
|
||||
);
|
||||
notifyFailure(iconPath, basename, drives);
|
||||
let errorMessage = getErrorMessageFromCode(error.code);
|
||||
if (!errorMessage) {
|
||||
error.image = basename;
|
||||
@@ -135,6 +156,7 @@ interface FlashStepProps {
|
||||
failed: number;
|
||||
speed?: number;
|
||||
eta?: number;
|
||||
width: string;
|
||||
}
|
||||
|
||||
export interface DriveWithWarnings extends constraints.DrivelistDrive {
|
||||
@@ -241,6 +263,7 @@ export class FlashStep extends React.PureComponent<
|
||||
<Flex
|
||||
flexDirection="column"
|
||||
alignItems="start"
|
||||
width={this.props.width}
|
||||
style={this.props.style}
|
||||
>
|
||||
<FlashSvg
|
||||
|
||||
@@ -239,6 +239,7 @@ export class MainPage extends React.Component<
|
||||
)}
|
||||
|
||||
<FlashStep
|
||||
width={this.state.isWebviewShowing ? '220px' : '200px'}
|
||||
goToSuccess={() => this.setState({ current: 'success' })}
|
||||
shouldFlashStepBeDisabled={shouldFlashStepBeDisabled}
|
||||
isFlashing={this.state.isFlashing}
|
||||
@@ -277,7 +278,7 @@ export class MainPage extends React.Component<
|
||||
// @ts-ignore
|
||||
'-webkit-app-region': 'drag',
|
||||
position: 'relative',
|
||||
zIndex: 1,
|
||||
zIndex: 2,
|
||||
}}
|
||||
>
|
||||
<Flex width="100%" />
|
||||
|
||||
10
lib/gui/app/renderer.ts
Normal file
10
lib/gui/app/renderer.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
// @ts-nocheck
|
||||
import { main } from './app';
|
||||
|
||||
if (module.hot) {
|
||||
module.hot.accept('./app', () => {
|
||||
main();
|
||||
});
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -149,7 +149,7 @@ export const Modal = styled(({ style, children, ...props }) => {
|
||||
})`
|
||||
> div {
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
height: 99%;
|
||||
|
||||
> div:first-child {
|
||||
height: 81%;
|
||||
|
||||
@@ -100,6 +100,7 @@ export const theme = _.merge({}, Theme, {
|
||||
font-size: 16px;
|
||||
|
||||
&& {
|
||||
width: 200px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
|
||||
@@ -133,7 +133,7 @@ async function createMainWindow() {
|
||||
width,
|
||||
height,
|
||||
frame: !fullscreen,
|
||||
useContentSize: false,
|
||||
useContentSize: true,
|
||||
show: false,
|
||||
resizable: false,
|
||||
maximizable: false,
|
||||
@@ -147,6 +147,7 @@ async function createMainWindow() {
|
||||
webPreferences: {
|
||||
backgroundThrottling: false,
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false,
|
||||
webviewTag: true,
|
||||
zoomFactor: width / defaultWidth,
|
||||
enableRemoteModule: true,
|
||||
|
||||
@@ -15,12 +15,24 @@
|
||||
*/
|
||||
|
||||
import { Drive as DrivelistDrive } from 'drivelist';
|
||||
import * as sdk from 'etcher-sdk';
|
||||
import {
|
||||
BlockDevice,
|
||||
File,
|
||||
Http,
|
||||
Metadata,
|
||||
SourceDestination,
|
||||
} from 'etcher-sdk/build/source-destination';
|
||||
import {
|
||||
MultiDestinationProgress,
|
||||
OnProgressFunction,
|
||||
OnFailFunction,
|
||||
decompressThenFlash,
|
||||
DECOMPRESSED_IMAGE_PREFIX,
|
||||
} from 'etcher-sdk/build/multi-write';
|
||||
import { cleanupTmpFiles } from 'etcher-sdk/build/tmp';
|
||||
import * as ipc from 'node-ipc';
|
||||
import { totalmem } from 'os';
|
||||
|
||||
import { BlockDevice, File, Http } from 'etcher-sdk/build/source-destination';
|
||||
import { toJSON } from '../../shared/errors';
|
||||
import { GENERAL_ERROR, SUCCESS } from '../../shared/exit-codes';
|
||||
import { delay } from '../../shared/utils';
|
||||
@@ -57,7 +69,7 @@ function log(message: string) {
|
||||
*/
|
||||
async function terminate(exitCode: number) {
|
||||
ipc.disconnect(IPC_SERVER_ID);
|
||||
await cleanupTmpFiles(Date.now());
|
||||
await cleanupTmpFiles(Date.now(), DECOMPRESSED_IMAGE_PREFIX);
|
||||
process.nextTick(() => {
|
||||
process.exit(exitCode || SUCCESS);
|
||||
});
|
||||
@@ -85,7 +97,7 @@ export interface WriteResult {
|
||||
successful: number;
|
||||
};
|
||||
errors: FlashError[];
|
||||
sourceMetadata?: sdk.sourceDestination.Metadata;
|
||||
sourceMetadata?: Metadata;
|
||||
}
|
||||
|
||||
export interface FlashResults extends WriteResult {
|
||||
@@ -112,19 +124,15 @@ async function writeAndValidate({
|
||||
onProgress,
|
||||
onFail,
|
||||
}: {
|
||||
source: sdk.sourceDestination.SourceDestination;
|
||||
destinations: sdk.sourceDestination.BlockDevice[];
|
||||
source: SourceDestination;
|
||||
destinations: BlockDevice[];
|
||||
verify: boolean;
|
||||
autoBlockmapping: boolean;
|
||||
decompressFirst: boolean;
|
||||
onProgress: sdk.multiWrite.OnProgressFunction;
|
||||
onFail: sdk.multiWrite.OnFailFunction;
|
||||
onProgress: OnProgressFunction;
|
||||
onFail: OnFailFunction;
|
||||
}): Promise<WriteResult> {
|
||||
const {
|
||||
sourceMetadata,
|
||||
failures,
|
||||
bytesWritten,
|
||||
} = await sdk.multiWrite.decompressThenFlash({
|
||||
const { sourceMetadata, failures, bytesWritten } = await decompressThenFlash({
|
||||
source,
|
||||
destinations,
|
||||
onFail,
|
||||
@@ -149,7 +157,7 @@ async function writeAndValidate({
|
||||
};
|
||||
for (const [destination, error] of failures) {
|
||||
const err = error as FlashError;
|
||||
const drive = destination as sdk.sourceDestination.BlockDevice;
|
||||
const drive = destination as BlockDevice;
|
||||
err.device = drive.device;
|
||||
err.description = drive.description;
|
||||
result.errors.push(err);
|
||||
@@ -160,7 +168,6 @@ async function writeAndValidate({
|
||||
interface WriteOptions {
|
||||
image: SourceMetadata;
|
||||
destinations: DrivelistDrive[];
|
||||
unmountOnSuccess: boolean;
|
||||
autoBlockmapping: boolean;
|
||||
decompressFirst: boolean;
|
||||
SourceType: string;
|
||||
@@ -201,7 +208,7 @@ ipc.connectTo(IPC_SERVER_ID, () => {
|
||||
* @example
|
||||
* writer.on('progress', onProgress)
|
||||
*/
|
||||
const onProgress = (state: sdk.multiWrite.MultiDestinationProgress) => {
|
||||
const onProgress = (state: MultiDestinationProgress) => {
|
||||
ipc.of[IPC_SERVER_ID].emit('state', state);
|
||||
};
|
||||
|
||||
@@ -237,10 +244,7 @@ ipc.connectTo(IPC_SERVER_ID, () => {
|
||||
* @example
|
||||
* writer.on('fail', onFail)
|
||||
*/
|
||||
const onFail = (
|
||||
destination: sdk.sourceDestination.SourceDestination,
|
||||
error: Error,
|
||||
) => {
|
||||
const onFail = (destination: SourceDestination, error: Error) => {
|
||||
ipc.of[IPC_SERVER_ID].emit('fail', {
|
||||
// TODO: device should be destination
|
||||
// @ts-ignore (destination.drive is private)
|
||||
@@ -253,13 +257,12 @@ ipc.connectTo(IPC_SERVER_ID, () => {
|
||||
const imagePath = options.image.path;
|
||||
log(`Image: ${imagePath}`);
|
||||
log(`Devices: ${destinations.join(', ')}`);
|
||||
log(`Umount on success: ${options.unmountOnSuccess}`);
|
||||
log(`Auto blockmapping: ${options.autoBlockmapping}`);
|
||||
log(`Decompress first: ${options.decompressFirst}`);
|
||||
const dests = options.destinations.map((destination) => {
|
||||
return new sdk.sourceDestination.BlockDevice({
|
||||
return new BlockDevice({
|
||||
drive: destination,
|
||||
unmountOnSuccess: options.unmountOnSuccess,
|
||||
unmountOnSuccess: true,
|
||||
write: true,
|
||||
direct: true,
|
||||
});
|
||||
@@ -298,7 +301,6 @@ ipc.connectTo(IPC_SERVER_ID, () => {
|
||||
await delay(DISCONNECT_DELAY);
|
||||
await terminate(exitCode);
|
||||
} catch (error) {
|
||||
log(`Error: ${error.message}`);
|
||||
exitCode = GENERAL_ERROR;
|
||||
ipc.of[IPC_SERVER_ID].emit('error', toJSON(error));
|
||||
}
|
||||
|
||||
@@ -15,11 +15,12 @@
|
||||
*/
|
||||
|
||||
import { execFile } from 'child_process';
|
||||
import { app, remote } from 'electron';
|
||||
import { join } from 'path';
|
||||
import { env } from 'process';
|
||||
import { promisify } from 'util';
|
||||
|
||||
import { getAppPath } from '../utils';
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
|
||||
const SUCCESSFUL_AUTH_MARKER = 'AUTHENTICATION SUCCEEDED';
|
||||
@@ -37,7 +38,7 @@ export async function sudo(
|
||||
env: {
|
||||
PATH: env.PATH,
|
||||
SUDO_ASKPASS: join(
|
||||
(app || remote.app).getAppPath(),
|
||||
getAppPath(),
|
||||
__dirname,
|
||||
'sudo-askpass.osascript.js',
|
||||
),
|
||||
|
||||
@@ -81,13 +81,10 @@ export const compatibility = {
|
||||
} as const;
|
||||
|
||||
export const warning = {
|
||||
unrecommendedDriveSize: (
|
||||
image: { recommendedDriveSize: number },
|
||||
drive: { device: string; size: number },
|
||||
) => {
|
||||
tooSmall: (source: { size: number }, target: { size: number }) => {
|
||||
return outdent({ newline: ' ' })`
|
||||
This image recommends a ${prettyBytes(image.recommendedDriveSize)}
|
||||
drive, however ${drive.device} is only ${prettyBytes(drive.size)}.
|
||||
The selected source is ${prettyBytes(source.size - target.size)}
|
||||
larger than this drive.
|
||||
`;
|
||||
},
|
||||
|
||||
@@ -126,7 +123,7 @@ export const warning = {
|
||||
},
|
||||
|
||||
largeDriveSize: () => {
|
||||
return 'This is a large drive! Make sure it doesn\'t contain files that you want to keep.';
|
||||
return "This is a large drive! Make sure it doesn't contain files that you want to keep.";
|
||||
},
|
||||
|
||||
systemDrive: () => {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
import * as childProcess from 'child_process';
|
||||
import { withTmpFile } from 'etcher-sdk/build/tmp';
|
||||
import { promises as fs } from 'fs';
|
||||
import * as _ from 'lodash';
|
||||
import * as os from 'os';
|
||||
@@ -24,7 +25,6 @@ import { promisify } from 'util';
|
||||
|
||||
import { sudo as catalinaSudo } from './catalina-sudo/sudo';
|
||||
import * as errors from './errors';
|
||||
import { withTmpFile } from './tmp';
|
||||
|
||||
const execAsync = promisify(childProcess.exec);
|
||||
const execFileAsync = promisify(childProcess.execFile);
|
||||
@@ -172,10 +172,11 @@ export async function elevateCommand(
|
||||
);
|
||||
return await withTmpFile(
|
||||
{
|
||||
keepOpen: false,
|
||||
prefix: 'balena-etcher-electron-',
|
||||
postfix: '.cmd',
|
||||
},
|
||||
async (path) => {
|
||||
async ({ path }) => {
|
||||
await fs.writeFile(path, launchScript);
|
||||
if (isWindows) {
|
||||
return elevateScriptWindows(path, options.applicationName);
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import * as tmp from 'tmp';
|
||||
|
||||
function tmpFileAsync(
|
||||
options: tmp.FileOptions,
|
||||
): Promise<{ path: string; cleanup: () => void }> {
|
||||
return new Promise((resolve, reject) => {
|
||||
tmp.file(options, (error, path, _fd, cleanup) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve({ path, cleanup });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export async function withTmpFile<T>(
|
||||
options: tmp.FileOptions,
|
||||
fn: (path: string) => Promise<T>,
|
||||
): Promise<T> {
|
||||
const { path, cleanup } = await tmpFileAsync(options);
|
||||
try {
|
||||
return await fn(path);
|
||||
} finally {
|
||||
cleanup();
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
import axios from 'axios';
|
||||
import { app, remote } from 'electron';
|
||||
import { Dictionary } from 'lodash';
|
||||
|
||||
import * as errors from './errors';
|
||||
@@ -47,3 +48,16 @@ export async function delay(duration: number): Promise<void> {
|
||||
setTimeout(resolve, duration);
|
||||
});
|
||||
}
|
||||
|
||||
export function getAppPath(): string {
|
||||
return (
|
||||
(app || remote.app)
|
||||
.getAppPath()
|
||||
// With macOS universal builds, getAppPath() returns the path to an app.asar file containing an index.js file which will
|
||||
// include the app-x64 or app-arm64 folder depending on the arch.
|
||||
// We don't care about the app.asar file, we want the actual folder.
|
||||
.replace(/\.asar$/, () =>
|
||||
process.platform === 'darwin' ? '-' + process.arch : '',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
11299
npm-shrinkwrap.json
generated
11299
npm-shrinkwrap.json
generated
File diff suppressed because it is too large
Load Diff
65
package.json
65
package.json
@@ -2,7 +2,7 @@
|
||||
"name": "balena-etcher",
|
||||
"private": true,
|
||||
"displayName": "balenaEtcher",
|
||||
"version": "1.5.111",
|
||||
"version": "1.5.120",
|
||||
"packageType": "local",
|
||||
"main": "generated/etcher.js",
|
||||
"description": "Flash OS images to SD cards and USB drives, safely and easily.",
|
||||
@@ -25,10 +25,10 @@
|
||||
"start": "./node_modules/.bin/electron .",
|
||||
"postshrinkwrap": "ts-node ./scripts/clean-shrinkwrap.ts",
|
||||
"webpack": "webpack",
|
||||
"watch": "webpack --watch",
|
||||
"watch": "webpack serve --no-optimization-minimize --config ./webpack.dev.config.ts",
|
||||
"concourse-build-electron": "npm run webpack",
|
||||
"concourse-test": "npx npm@6.14.5 test",
|
||||
"concourse-test-electron": "npx npm@6.14.5 test"
|
||||
"concourse-test": "npx npm@6.14.8 test",
|
||||
"concourse-test-electron": "npx npm@6.14.8 test"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
@@ -46,44 +46,53 @@
|
||||
"author": "Balena Inc. <hello@etcher.io>",
|
||||
"license": "Apache-2.0",
|
||||
"platformSpecificDependencies": [
|
||||
"xmlbuilder",
|
||||
"xmldom",
|
||||
"@types/plist",
|
||||
"@types/verror",
|
||||
"crc",
|
||||
"iconv-corefoundation",
|
||||
"plist",
|
||||
"dmg-license",
|
||||
"fsevents",
|
||||
"winusb-driver-generator"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@balena/lint": "^5.0.4",
|
||||
"@balena/lint": "^5.3.0",
|
||||
"@fortawesome/fontawesome-free": "^5.13.1",
|
||||
"@svgr/webpack": "^5.4.0",
|
||||
"@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": "^0.9.1",
|
||||
"@types/mini-css-extract-plugin": "^1.2.2",
|
||||
"@types/mocha": "^8.0.3",
|
||||
"@types/node": "^12.12.39",
|
||||
"@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": "^4.1.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",
|
||||
"chai": "^4.2.0",
|
||||
"copy-webpack-plugin": "^6.0.1",
|
||||
"css-loader": "^4.2.1",
|
||||
"copy-webpack-plugin": "^7.0.0",
|
||||
"css-loader": "^5.0.1",
|
||||
"d3": "^4.13.0",
|
||||
"debug": "^4.2.0",
|
||||
"electron": "9.3.3",
|
||||
"electron-builder": "^22.9.1",
|
||||
"electron": "12.0.2",
|
||||
"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": "^5.1.5",
|
||||
"file-loader": "^6.0.0",
|
||||
"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": "^0.10.0",
|
||||
"mini-css-extract-plugin": "^1.3.3",
|
||||
"mocha": "^8.0.1",
|
||||
"native-addon-loader": "^2.0.1",
|
||||
"node-ipc": "^9.1.1",
|
||||
@@ -94,23 +103,25 @@
|
||||
"react": "^16.8.5",
|
||||
"react-dom": "^16.8.5",
|
||||
"redux": "^4.0.5",
|
||||
"rendition": "^18.8.3",
|
||||
"rendition": "^19.2.0",
|
||||
"resin-corvus": "^2.0.5",
|
||||
"semver": "^7.3.2",
|
||||
"simple-progress-webpack-plugin": "^1.1.2",
|
||||
"sinon": "^9.0.2",
|
||||
"spectron": "^11.0.0",
|
||||
"string-replace-loader": "^2.3.0",
|
||||
"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#workaround-windows-amperstand-in-username",
|
||||
"sys-class-rgb-led": "^2.1.0",
|
||||
"tmp": "^0.2.1",
|
||||
"ts-loader": "^8.0.0",
|
||||
"ts-node": "^9.0.0",
|
||||
"sudo-prompt": "github:zvin/sudo-prompt#7cdede2f0da28fbcc2db48402d7d935f3a825c91",
|
||||
"sys-class-rgb-led": "^3.0.0",
|
||||
"ts-loader": "^8.0.12",
|
||||
"ts-node": "^9.1.1",
|
||||
"tslib": "^2.0.0",
|
||||
"typescript": "^4.1.2",
|
||||
"typescript": "^4.2.2",
|
||||
"url-loader": "^4.1.1",
|
||||
"uuid": "^8.1.0",
|
||||
"webpack": "^4.40.2",
|
||||
"webpack-cli": "^3.3.9"
|
||||
"webpack": "^5.11.0",
|
||||
"webpack-cli": "^4.2.0",
|
||||
"webpack-dev-server": "^3.11.2"
|
||||
}
|
||||
}
|
||||
|
||||
11
repo.yml
11
repo.yml
@@ -8,4 +8,13 @@ sentry:
|
||||
triggerNotification:
|
||||
version: 1.5.81
|
||||
stagingPercentage: 100
|
||||
|
||||
upstream:
|
||||
- repo: etcher-sdk
|
||||
url: https://github.com/balena-io-modules/etcher-sdk
|
||||
module: 'etcher-sdk'
|
||||
- repo: sys-class-rgb-led
|
||||
url: https://github.com/balena-io-modules/sys-class-rgb-led
|
||||
module: sys-class-rgb-led
|
||||
- repo: rendition
|
||||
url: https://github.com/balena-io-modules/rendition
|
||||
module: rendition
|
||||
|
||||
@@ -29,6 +29,10 @@ const SHRINKWRAP_FILENAME = path.join(__dirname, '..', 'npm-shrinkwrap.json');
|
||||
async function main() {
|
||||
try {
|
||||
const cleaned = omit(shrinkwrap, packageInfo.platformSpecificDependencies);
|
||||
for (const item of Object.values(cleaned.dependencies)) {
|
||||
// @ts-ignore
|
||||
item.dev = true;
|
||||
}
|
||||
await writeFileAsync(
|
||||
SHRINKWRAP_FILENAME,
|
||||
JSON.stringify(cleaned, null, JSON_INDENT),
|
||||
|
||||
Submodule scripts/resin updated: 02c8c7ca1f...d1b05ad312
@@ -16,7 +16,6 @@
|
||||
|
||||
import { expect } from 'chai';
|
||||
|
||||
import * as settings from '../../../lib/gui/app/models/settings';
|
||||
import * as progressStatus from '../../../lib/gui/app/modules/progress-status';
|
||||
|
||||
describe('Browser: progressStatus', function () {
|
||||
@@ -30,8 +29,6 @@ describe('Browser: progressStatus', function () {
|
||||
eta: 15,
|
||||
speed: 100000000000000,
|
||||
};
|
||||
|
||||
settings.set('unmountOnSuccess', true);
|
||||
});
|
||||
|
||||
it('should report 0% if percentage == 0 but speed != 0', function () {
|
||||
@@ -40,22 +37,14 @@ describe('Browser: progressStatus', function () {
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle percentage == 0, flashing, unmountOnSuccess', function () {
|
||||
it('should handle percentage == 0, flashing', function () {
|
||||
this.state.speed = 0;
|
||||
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
|
||||
'0% Flashing...',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle percentage == 0, flashing, !unmountOnSuccess', function () {
|
||||
this.state.speed = 0;
|
||||
settings.set('unmountOnSuccess', false);
|
||||
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
|
||||
'0% Flashing...',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle percentage == 0, verifying, unmountOnSuccess', function () {
|
||||
it('should handle percentage == 0, verifying', function () {
|
||||
this.state.speed = 0;
|
||||
this.state.type = 'verifying';
|
||||
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
|
||||
@@ -63,31 +52,14 @@ describe('Browser: progressStatus', function () {
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle percentage == 0, verifying, !unmountOnSuccess', function () {
|
||||
this.state.speed = 0;
|
||||
this.state.type = 'verifying';
|
||||
settings.set('unmountOnSuccess', false);
|
||||
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
|
||||
'0% Validating...',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle percentage == 50, flashing, unmountOnSuccess', function () {
|
||||
it('should handle percentage == 50, flashing', function () {
|
||||
this.state.percentage = 50;
|
||||
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
|
||||
'50% Flashing...',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle percentage == 50, flashing, !unmountOnSuccess', function () {
|
||||
this.state.percentage = 50;
|
||||
settings.set('unmountOnSuccess', false);
|
||||
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
|
||||
'50% Flashing...',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle percentage == 50, verifying, unmountOnSuccess', function () {
|
||||
it('should handle percentage == 50, verifying', function () {
|
||||
this.state.percentage = 50;
|
||||
this.state.type = 'verifying';
|
||||
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
|
||||
@@ -95,31 +67,14 @@ describe('Browser: progressStatus', function () {
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle percentage == 50, verifying, !unmountOnSuccess', function () {
|
||||
this.state.percentage = 50;
|
||||
this.state.type = 'verifying';
|
||||
settings.set('unmountOnSuccess', false);
|
||||
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
|
||||
'50% Validating...',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle percentage == 100, flashing, unmountOnSuccess', function () {
|
||||
it('should handle percentage == 100, flashing', function () {
|
||||
this.state.percentage = 100;
|
||||
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
|
||||
'Finishing...',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle percentage == 100, flashing, !unmountOnSuccess', function () {
|
||||
this.state.percentage = 100;
|
||||
settings.set('unmountOnSuccess', false);
|
||||
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
|
||||
'Finishing...',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle percentage == 100, verifying, unmountOnSuccess', function () {
|
||||
it('should handle percentage == 100, verifying', function () {
|
||||
this.state.percentage = 100;
|
||||
this.state.type = 'verifying';
|
||||
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
|
||||
@@ -127,9 +82,8 @@ describe('Browser: progressStatus', function () {
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle percentage == 100, validatinf, !unmountOnSuccess', function () {
|
||||
it('should handle percentage == 100, validating', function () {
|
||||
this.state.percentage = 100;
|
||||
settings.set('unmountOnSuccess', false);
|
||||
expect(progressStatus.titleFromFlashState(this.state)).to.equal(
|
||||
'Finishing...',
|
||||
);
|
||||
|
||||
@@ -15,43 +15,52 @@
|
||||
*/
|
||||
|
||||
import { expect } from 'chai';
|
||||
import { platform } from 'os';
|
||||
import { Application } from 'spectron';
|
||||
import * as electronPath from 'electron';
|
||||
|
||||
describe('Spectron', function () {
|
||||
// Mainly for CI jobs
|
||||
this.timeout(40000);
|
||||
// TODO: spectron fails to start on the CI with:
|
||||
// Error: Failed to create session.
|
||||
// unknown error: Chrome failed to start: exited abnormally
|
||||
if (platform() !== 'darwin') {
|
||||
describe('Spectron', function () {
|
||||
// Mainly for CI jobs
|
||||
this.timeout(40000);
|
||||
|
||||
const app = new Application({
|
||||
path: (electronPath as unknown) as string,
|
||||
args: ['--no-sandbox', '.'],
|
||||
});
|
||||
|
||||
before('app:start', async () => {
|
||||
await app.start();
|
||||
});
|
||||
|
||||
after('app:stop', async () => {
|
||||
if (app && app.isRunning()) {
|
||||
await app.stop();
|
||||
}
|
||||
});
|
||||
|
||||
describe('Browser Window', () => {
|
||||
it('should open a browser window', async () => {
|
||||
// We can't use `isVisible()` here as it won't work inside
|
||||
// a Windows Docker container, but we can approximate it
|
||||
// with these set of checks:
|
||||
const bounds = await app.browserWindow.getBounds();
|
||||
expect(bounds.height).to.be.above(0);
|
||||
expect(bounds.width).to.be.above(0);
|
||||
expect(await app.browserWindow.isMinimized()).to.be.false;
|
||||
expect(await app.browserWindow.isVisible()).to.be.true;
|
||||
const app = new Application({
|
||||
path: (electronPath as unknown) as string,
|
||||
args: ['--no-sandbox', '.'],
|
||||
});
|
||||
|
||||
it('should set a proper title', async () => {
|
||||
// @ts-ignore (SpectronClient.getTitle exists)
|
||||
return expect(await app.client.getTitle()).to.equal('Etcher');
|
||||
before('app:start', async () => {
|
||||
await app.start();
|
||||
});
|
||||
|
||||
after('app:stop', async () => {
|
||||
if (app && app.isRunning()) {
|
||||
await app.stop();
|
||||
}
|
||||
});
|
||||
|
||||
describe('Browser Window', () => {
|
||||
it('should open a browser window', async () => {
|
||||
// We can't use `isVisible()` here as it won't work inside
|
||||
// a Windows Docker container, but we can approximate it
|
||||
// with these set of checks:
|
||||
const bounds = await app.browserWindow.getBounds();
|
||||
expect(bounds.height).to.be.above(0);
|
||||
expect(bounds.width).to.be.above(0);
|
||||
expect(await app.browserWindow.isMinimized()).to.be.false;
|
||||
expect(
|
||||
(await app.browserWindow.isVisible()) ||
|
||||
(await app.browserWindow.isFocused()),
|
||||
).to.be.true;
|
||||
});
|
||||
|
||||
it('should set a proper title', async () => {
|
||||
// @ts-ignore (SpectronClient.getTitle exists)
|
||||
return expect(await app.client.getTitle()).to.equal('Etcher');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
import * as CopyPlugin from 'copy-webpack-plugin';
|
||||
import { readdirSync } from 'fs';
|
||||
import * as _ from 'lodash';
|
||||
import * as MiniCssExtractPlugin from 'mini-css-extract-plugin';
|
||||
import * as os from 'os';
|
||||
import outdent from 'outdent';
|
||||
import * as path from 'path';
|
||||
@@ -32,8 +31,7 @@ import { BannerPlugin, NormalModuleReplacementPlugin } from 'webpack';
|
||||
*/
|
||||
function externalPackageJson(packageJsonPath: string) {
|
||||
return (
|
||||
_context: string,
|
||||
request: string,
|
||||
{ request }: { context: string; request: string },
|
||||
callback: (error?: Error | null, result?: string) => void,
|
||||
) => {
|
||||
if (_.endsWith(request, 'package.json')) {
|
||||
@@ -50,8 +48,7 @@ function platformSpecificModule(
|
||||
) {
|
||||
// Resolves module on platform, otherwise resolves the replacement
|
||||
return (
|
||||
_context: string,
|
||||
request: string,
|
||||
{ request }: { context: string; request: string },
|
||||
callback: (error?: Error, result?: string, type?: string) => void,
|
||||
) => {
|
||||
if (request === module && os.platform() !== platform) {
|
||||
@@ -70,6 +67,8 @@ function renameNodeModules(resourcePath: string) {
|
||||
path
|
||||
.relative(__dirname, resourcePath)
|
||||
.replace('node_modules', 'modules')
|
||||
// use the same name on all architectures so electron-builder can build a universal dmg on mac
|
||||
.replace(LZMA_BINDINGS_FOLDER, LZMA_BINDINGS_FOLDER_RENAMED)
|
||||
// file-loader expects posix paths, even on Windows
|
||||
.replace(/\\/g, '/')
|
||||
);
|
||||
@@ -89,6 +88,7 @@ function findLzmaNativeBindingsFolder(): string {
|
||||
}
|
||||
|
||||
const LZMA_BINDINGS_FOLDER = findLzmaNativeBindingsFolder();
|
||||
const LZMA_BINDINGS_FOLDER_RENAMED = 'binding';
|
||||
|
||||
interface ReplacementRule {
|
||||
search: string;
|
||||
@@ -119,7 +119,15 @@ function fetchWasm(...where: string[]) {
|
||||
} catch {
|
||||
}
|
||||
function appPath() {
|
||||
return Path.isAbsolute(__dirname) ? __dirname : Path.join(electron.remote.app.getAppPath(), 'generated');
|
||||
return Path.isAbsolute(__dirname) ?
|
||||
__dirname :
|
||||
Path.join(
|
||||
// With macOS universal builds, getAppPath() returns the path to an app.asar file containing an index.js file which will
|
||||
// include the app-x64 or app-arm64 folder depending on the arch.
|
||||
// We don't care about the app.asar file, we want the actual folder.
|
||||
electron.remote.app.getAppPath().replace(/\\.asar$/, () => process.platform === 'darwin' ? '-' + process.arch : ''),
|
||||
'generated'
|
||||
);
|
||||
}
|
||||
scriptDirectory = Path.join(appPath(), 'modules', ${whereStr}, '/');
|
||||
`;
|
||||
@@ -128,6 +136,7 @@ function fetchWasm(...where: string[]) {
|
||||
const commonConfig = {
|
||||
mode: 'production',
|
||||
optimization: {
|
||||
moduleIds: 'natural',
|
||||
minimize: true,
|
||||
minimizer: [
|
||||
new TerserPlugin({
|
||||
@@ -148,7 +157,12 @@ const commonConfig = {
|
||||
rules: [
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: 'css-loader',
|
||||
use: ['style-loader', 'css-loader'],
|
||||
},
|
||||
{
|
||||
test: /\.(woff|woff2|eot|ttf|otf)$/,
|
||||
loader: 'file-loader',
|
||||
options: { name: renameNodeModules },
|
||||
},
|
||||
{
|
||||
test: /\.svg$/,
|
||||
@@ -192,12 +206,7 @@ const commonConfig = {
|
||||
// remove node-pre-gyp magic from lzma-native
|
||||
{
|
||||
search: 'require(binding_path)',
|
||||
replace: () => {
|
||||
return `require('./${path.posix.join(
|
||||
LZMA_BINDINGS_FOLDER,
|
||||
'lzma_native.node',
|
||||
)}')`;
|
||||
},
|
||||
replace: `require('./${LZMA_BINDINGS_FOLDER}/lzma_native.node')`,
|
||||
},
|
||||
// use regular stream module instead of readable-stream
|
||||
{
|
||||
@@ -241,7 +250,19 @@ const commonConfig = {
|
||||
"return await readFile(Path.join(__dirname, '..', 'blobs', filename));",
|
||||
replace: outdent`
|
||||
const { app, remote } = require('electron');
|
||||
return await readFile(Path.join((app || remote.app).getAppPath(), 'generated', __dirname.replace('node_modules', 'modules'), '..', 'blobs', filename));
|
||||
return await readFile(
|
||||
Path.join(
|
||||
// With macOS universal builds, getAppPath() returns the path to an app.asar file containing an index.js file which will
|
||||
// include the app-x64 or app-arm64 folder depending on the arch.
|
||||
// We don't care about the app.asar file, we want the actual folder.
|
||||
(app || remote.app).getAppPath().replace(/\\.asar$/, () => process.platform === 'darwin' ? '-' + process.arch : ''),
|
||||
'generated',
|
||||
__dirname.replace('node_modules', 'modules'),
|
||||
'..',
|
||||
'blobs',
|
||||
filename
|
||||
)
|
||||
);
|
||||
`,
|
||||
}),
|
||||
// Use the libext2fs.wasm file in the generated folder
|
||||
@@ -276,7 +297,7 @@ const commonConfig = {
|
||||
format: process.env.WEBPACK_PROGRESS || 'verbose',
|
||||
}),
|
||||
// Force axios to use http.js, not xhr.js as we need stream support
|
||||
// (it's package.json file replaces http with xhr for browser targets).
|
||||
// (its package.json file replaces http with xhr for browser targets).
|
||||
new NormalModuleReplacementPlugin(
|
||||
slashOrAntislash(/node_modules\/axios\/lib\/adapters\/xhr\.js/),
|
||||
'./http.js',
|
||||
@@ -319,7 +340,7 @@ if (os.platform() === 'win32') {
|
||||
// liblzma.dll is required on Windows for lzma-native
|
||||
guiConfigCopyPatterns.push({
|
||||
from: `node_modules/lzma-native/${LZMA_BINDINGS_FOLDER}/liblzma.dll`,
|
||||
to: `modules/lzma-native/${LZMA_BINDINGS_FOLDER}/liblzma.dll`,
|
||||
to: `modules/lzma-native/${LZMA_BINDINGS_FOLDER_RENAMED}/liblzma.dll`,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -331,10 +352,18 @@ const guiConfig = {
|
||||
__filename: true,
|
||||
},
|
||||
entry: {
|
||||
gui: path.join(__dirname, 'lib', 'gui', 'app', 'app.ts'),
|
||||
gui: path.join(__dirname, 'lib', 'gui', 'app', 'renderer.ts'),
|
||||
},
|
||||
plugins: [
|
||||
...commonConfig.plugins,
|
||||
new CopyPlugin({
|
||||
patterns: [
|
||||
{ from: 'lib/gui/app/index.html', to: 'index.html' },
|
||||
// electron-builder doesn't bundle folders named "assets"
|
||||
// See https://github.com/electron-userland/electron-builder/issues/4545
|
||||
{ from: 'assets/icon.png', to: 'media/icon.png' },
|
||||
],
|
||||
}),
|
||||
// Remove "Download the React DevTools for a better development experience" message
|
||||
new BannerPlugin({
|
||||
banner: '__REACT_DEVTOOLS_GLOBAL_HOOK__ = { isDisabled: true };',
|
||||
@@ -373,41 +402,4 @@ const childWriterConfig = {
|
||||
},
|
||||
};
|
||||
|
||||
const cssConfig = {
|
||||
mode: 'production',
|
||||
optimization: {
|
||||
minimize: false,
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.css$/i,
|
||||
use: [MiniCssExtractPlugin.loader, 'css-loader'],
|
||||
},
|
||||
{
|
||||
test: /\.(woff|woff2|eot|ttf|otf|svg)$/,
|
||||
loader: 'file-loader',
|
||||
options: { name: renameNodeModules },
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new MiniCssExtractPlugin({ filename: '[name].css' }),
|
||||
new CopyPlugin({
|
||||
patterns: [
|
||||
{ from: 'lib/gui/app/index.html', to: 'index.html' },
|
||||
// electron-builder doesn't bundle folders named "assets"
|
||||
// See https://github.com/electron-userland/electron-builder/issues/4545
|
||||
{ from: 'assets/icon.png', to: 'media/icon.png' },
|
||||
],
|
||||
}),
|
||||
],
|
||||
entry: {
|
||||
index: path.join(__dirname, 'lib', 'gui', 'app', 'css', 'main.css'),
|
||||
},
|
||||
output: {
|
||||
path: path.join(__dirname, 'generated'),
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = [cssConfig, guiConfig, etcherConfig, childWriterConfig];
|
||||
export default [guiConfig, etcherConfig, childWriterConfig];
|
||||
|
||||
22
webpack.dev.config.ts
Normal file
22
webpack.dev.config.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import configs from './webpack.config';
|
||||
import { WebpackOptionsNormalized } from 'webpack';
|
||||
import * as fs from 'fs';
|
||||
|
||||
const [
|
||||
guiConfig,
|
||||
etcherConfig,
|
||||
childWriterConfig,
|
||||
] = (configs as unknown) as WebpackOptionsNormalized[];
|
||||
|
||||
configs.forEach((config) => {
|
||||
config.mode = 'development';
|
||||
});
|
||||
|
||||
guiConfig.devServer = {
|
||||
hot: true,
|
||||
port: 3030,
|
||||
};
|
||||
|
||||
fs.copyFileSync('./lib/gui/app/index.dev.html', './generated/index.html');
|
||||
|
||||
export default [guiConfig, etcherConfig, childWriterConfig];
|
||||
Reference in New Issue
Block a user