We remove the usage of Angular from 'FlashStateModel' and its usage
throughout the project comes under the new 'flashState' moniker.
Depends: https://github.com/resin-io/etcher/pull/1261
`image-stream` returns image objects that look like this:
```js
{
stream: <readable stream>,
transform: <transform stream>,
size: {
original: <number>,
final: {
value: <number>,
estimation: <boolean>
}
},
...
}
```
While the GUI handles image objects that look like this:
```sh
{
path: <string>,
size: <number>,
...
}
```
It looks like we should share a common structure between both, so we can
use `image-stream` images in `drive-constraints`, for example.
Turns out that we actually transform `image-stream` image objects to GUI
image objects when the user selects an image using the image selector
dialog, which is another indicator that we should normalise this
situation.
As a solution, this commit does the following:
- Add `path` to `image-stream` image object
- Reuse `image-stream` image objects in the GUI, given they are a
superset of GUI image objects
See: https://github.com/resin-io/etcher/pull/1223#discussion_r108165110
Fixes: https://github.com/resin-io/etcher/issues/1232
Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
* chore: get the CI tests working again
* an updated eslint-plugin-lodash was creating extra linter errors where we
were using built-in String methods instead of the lodash equivalents
* an updated codespell package now installs with a different executable-name
(no .py extension), and now supports multiple dictionaries
* while I was at it I replaced the messy double-quoting-string logic with the
command-join module
Changelog-type: patch
* chore: downgrade codespell to 1.9.2 because the latest version has issues running on Windows
(we'll hopefully be able to revert this commit when codespell works again properly!)
The current error handling logic is a mess. We have code that tries to
fetch information about errors in different places throughout the
application, and its incredibly hard to ensure certain types of error
get decent human friendly error messages.
This commit groups, improves, and tests all error related functions in
`lib/shared/errors.js`.
Here's a summary of the changes, in more detail:
- Move the `HUMAN_FRIENDLY` object to `shared/errors.js`
- Extend `HUMAN_FRIENDLY` with error descriptions
- Add `ENOMEM` to `shared/errors.js`
- Group CLI and `OSDialogService` mechanisms for getting an error title
and an error description
- Move error serialisation routines from `robot` to `shared/errors.js`
- Create and use `createError()` and `createUserError()` utility
functions
- Add user friendly descriptions to many errors
- Don't report user errors to TrackJS
Fixes: https://github.com/resin-io/etcher/issues/1098
Change-Type: minor
Changelog-Entry: Make errors more user friendly throughout the application.
Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
This is one step towards enforcing the linter until we barely have to
check for style issues on code reviews.
Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
There are a lot of new rules since the last time I revised the ESLint
rules documentation.
I've updated the main `.eslintrc.yml` to include some newer additions,
plus I added another ESLint configuration file inside `tests`, so we can
add some stricted rules to the production code while relaxing them for
the test suite (due to the fact that Mocha is not very ES6 friendly and
Angular tests require a bit of dark magic to setup).
This is a summary of the most important changes:
- Disallow "magic numbers"
These should now be extracted to constants, which forces us to think of
a good name for them, and thus make the code more self-documenting (I
had to Google up the meaning of some existing magic numbers, so I guess
this will be great for readability purposes).
- Require consistent `return` statements
Some functions relied on JavaScript relaxed casting mechanism to work,
which now have explicit return values. This flag also helped me detect
some promises that were not being returned, and therefore risked not
being caught by the exception handlers in case of errors.
- Disallow redefining function arguments
Immutability makes functions easier to reason about.
- Enforce JavaScript string templates instead of string concatenation
We were heavily mixing boths across the codebase.
There are some extra rules that I tweaked, however most of codebase
changes in this commit are related to the rules mentioned above.
Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
- The type in `@type` should be surrounded by curly braces
- We should use `@type` in some cases where we used `@property`
- `@module` descriptions should go above such tag
- Namespaces should not declare their access type
Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
Currently Etcher will auto-select a system drive if its the only
available one when the "unsafe mode" is enabled, which doesn't feel
right.
Change-Type: patch
Changelog-Entry: Don't auto select system drives in unsafe mode.
See: https://github.com/resin-io/etcher/pull/1061
Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
Currently Etcher will allow you to flash an image to the same drive that
contains the image. As a way to protect against that case we introduce
the concept of a "source drive", which means a drive that contains the
source image.
This commit adds the following logic around this new concept:
- Don't auto-select a source drive
- De-select an already selected drive if an image inside it is selected
- Disable the drive in the drive selector modal
- Add a "Source Drive" badge to the drive in the drive selector modal
Fixes: https://github.com/resin-io/etcher/issues/830
Change-Type: minor
Changelog-Entry: Prevent flashing the drive where the source image is located.
Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
We currently attempt to read extra metadata from ZIP files, such as a
logo, image name, image url, etc. In order to simplify the metadata
story, we decided that this metadata will not live on the image itself,
but rather on a centralised repo, which greatly simplified our custom
archive extraction logic and allows us to make use of these nice
features even when streaming the image directly from the internet.
We'll be working on bringing back this functionality from a centralised
repo in subsequent commits.
Change-Type: major
Changelog-Entry: Remove extended archives metadata extraction logic.
Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
This is a long lasting task. The `etcher-image-stream` project takes
care of converting any kind of image input into a NodeJS readable
stream, handling things like decompression in betwee, however its a
module that, except for weird cases, there is no benefit on having
separate from the main repository.
In order to validate the assumption above, we've left the module
separate for almost a year, and no use case has emerged to keep it
like that.
This commit joins the code and tests of that module in the main
repository.
Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
This commit extracts the byte-related conversions from the `byte-size`
AngularJS module and the `FlashStateModel` to a re-usable generic
CommonJS module at `lib/shared/units.js`, than can also be used by the
CLI.
Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
`SelectionStateModel`'s methods `isSystemDrive` and `isDriveLocked`
don't depend on application state. They have been extracted in a different
AngularJS service: `DriveConstraintsModel`. The new service's actual
implementation is in `lib/src`, in order to be reused by the CLI.
Miscellaneous changes:
- Rename `lib/src` to `lib/shared`
- Refactor `drive-constraints` to throw when image is undefined
The default behaviour was to pretend that we're all good if the if
the image is not specified. We're not using this "feature", and
it can be dangerous if we forget to pass in the image.
- Make `isSystemDrive` return `false` if `system` property is undefined
- Use `drive-constraints` in `store.js`
Change-Type: patch
System drives in the drive-list widget now have a red warning label
beneath. Also new is the `isSystemDrive()` method under
`SelectionStateModel` and its unit-tests.
See: https://github.com/resin-io/etcher/issues/888
Changelog-Entry: Label system drives in the drive-list widget
Recently, we've added support for a `recommendedDriveSize` property in
the `manifest.json` of extended image archives, which the image can use
to warn the user that his drive, even if it is large enough to hold the
image, might not be large enough to deliver a good usage experience
later on.
When this property is found, the GUI reacts in the following ways:
- Drives that are large enough to hold the image but don't meet the
recommended drive size are tagged with a warning label in the drive
selector component.
- Attempting to select a "labeled" drive opens a warning modal asking
for user confirmation.
- Drives that don't meet the recommended drive size declared in the
image won't get auto-selected.
- If there is a drive already selected, and the user picks an image
whose recommended drive size is greater than the drive size, the
currently selected drive gets auto-deselected.
Code-wise, the following significant changes have been introduced:
- Implement `SelectionStateModel.getImageRecommendedDriveSize()`.
- Implement `SelectionStateModel.isDriveSizeRecommended()`.
- Extract `WarningModal` out of the settings page (the dangerous setting
modal).
Change-Type: minor
Changelog-Entry: Allow images to declare a recommended minimum drive size.
See: https://github.com/resin-io-modules/etcher-image-stream/pull/36
Fixes: https://github.com/resin-io/etcher/issues/698
Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
This new version stops sending a `passedValidation` boolean property
upon completion and still throws an `EVALIDATION` error when validation
fails.
Such small chance allows us to get rid of lot of complexity related to
handling the `passedValidation` value in the application state.
Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
We've recently added support for a `supportUrl` property in an archive
image `_info/manifest.json`, which the publisher can use to define the
URL where it would like to redirect users facing problems.
This PR makes the "Need help?" link at the top right corner open the
configured `supportUrl` url, and fallback to the original Etcher's help
page if no `supportUrl` is found.
In order to accomplish this, we made the following changes:
- Implement `SelectionStateModel.getImageSupportUrl()`
- Implement `HeaderController` controller
Change-Type: minor
Changelog-Entry: Make the "Need help?" link dynamically open the image support url.
Fixes: https://github.com/resin-io/etcher/issues/662
Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
In order to get the bmap file contents to the Etcher CLI, we were
handling extraction, writing to a temporary file, then reading again,
and all sorts of other mumbo-jumbo, without realising that
`etcher-image-stream` already has this information right where we need
it (in the CLI's writer module) and in the way we need it (as plain
text).
Re-using from there hugely simplifies things.
Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
This PR integrates `.bmap` support recently added to
`etcher-image-write` into Etcher itself.
It does it in the following way:
- It adds a `--bmap` option to the Etcher CLI.
- It saves a potential `bmap` file contents to the
`SelectionStateModel`.
- In the GUI, at the time of writing, if there is a `bmap` file content
in `SelectionStateModel`, it gets written to a temporary file and such
path is passed as the `--bmap` option to the CLI.
Since validation checksums don't make sense anymore, the finish screen
doesn't show the checksum box in this case.
Change-Type: minor
Changelog-Entry: Add `.bmap` support.
Fixes: https://github.com/resin-io/etcher/issues/171
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
We were currently throwing a validation error if the error code was not
string when setting the flash results, however a number error code makes
sense in some cases, and its what its returned by a `ChildProcess`
object.
Change-Type: patch
See: https://github.com/resin-io/etcher/issues/609
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
Currently, the client application knows too much about how the flash
results are stored in the internal state, and relies on its structure to
perform its logic.
This PR introduces several getters to `FlashStateModel` and makes
`FlashStateModel.getFlashResults()` private, ensuring clients don't
depend on the flash results object.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
`ImageWriterService` currently has two responsibilities. It contains
logic to start and manage a flash process, and provides an API to
interact with the current flash state.
To honour the single responsibility principle, we extract
`FlashStateModel` from `ImageWriterService`.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
We recently introduced a feature where a single available drive would be
auto-selected even if the user didn't select an image.
This introduces a potential scenario that brings the state to an
incoherent state:
- Start Etcher.
- Let a drive get auto-selected.
- Select an image larger than the auto-selected drive.
In this case, the drive is known to not have enough space to hold the
image, but remains auto-selected.
This PR makes sure the drive is deselected automatically in the above
scenario.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
Currently, we intentionally prevent a drive from being auto-selected if
there was no selected image for UX purposes, however this way of
thinking has been challenged, and we didn't find a real UX problem by
doing this.
As a minor design improvement, we gray out the drive name in the main
window when an image hasn't been selected yet.
Change-Type: minor
Changelog-Entry: Perform drive auto-selection even when there is no selected image.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
Instead of storing the whole selected drive object, we barely store a
reference to the corresponding drive in the available drives array (the
reference being the drive device).
This greatly simplifies the application state in the following ways:
- The drive metadata (size, description, etc) is not duplicated in the
state, enforcing a single source of truth.
- If the selected drive stops being available (e.g: is unplugged), the
reference doesn't hold anymore, making this functionality very natural
to implement.
- Makes `SelectionStateModel.isCurrentDrive()` much more inuitive, since
we don't have to document that changes in the metadata of the drive
object, or extra keys such as `$$hashKey` don't change the result of
this function.
- Ensures the state never goes into a problematic state where we try to
to write to an unavailable drive.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
The following PRs add support for a custom `_info` directory containing
metadata such an image URL, display name, logo, etc in
`etcher-image-stream`:
- https://github.com/resin-io-modules/etcher-image-stream/pull/16
- https://github.com/resin-io-modules/etcher-image-stream/pull/14
- https://github.com/resin-io-modules/etcher-image-stream/pull/13
Now that this module supports such metadata, we make use of it in the
GUI as follows:
- The file name is replaced with the display name.
- The file name links to the image URL.
- The "Select Image" logo is replaced with the image logo.
Some miscellaneous changes introduces in this PR to support the changes
described above:
- Implement `SelectionStateModel.getImageUrl()`.
- Implement `SelectionStateModel.getImageLogo()`.
- Implement `SelectionStateModel.getImageName()`.
- Ignore the "logo" image property when displaying the "Select image"
event, in order to not fill the console with SVG contents.
- Make `svg-icon` understand SVG strings as paths.
- Make `svg-icon` react to changes to the `path` attribute.
- Extract the core functionality of `openExternal` into
`OSOpenExternalService`.
- Upgrade `etcher-image-stream` to v3.0.1.
Change-Type: minor
Changelog-Entry: Support rich image extensions.
Fixes: https://github.com/resin-io/etcher/issues/470
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
We try our best to check that the images the user select are too big for
the selected drive as early as possible, but this probes to be
problematic with certain compressed formats, like bzip2, which doesn't
store any information about the uncompressed size, requiring a ~50s
intensive computation as a minimum to find it out.
For these kinds of formats, we don't perform an early check, but instead
gracefully handle the case where the drive doesn't have any more space.
This PR handles an `ENOSPC` error by displaying the alert orange ribbon,
and prompting the user to retry with a larger drive. This is a huge
improvement over the cryptic `EIO` error what was thrown before, and
over having Etcher freeze at a certain percentage point.
Change-Type: minor
Changelog-Entry: Display a nice alert ribbon if drive runs out of space.
See: https://github.com/resin-io/etcher/issues/571
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
This setting makes Etcher not filter non-removable drives, allowing you to
arbitrarily write to your system drives.
This is a dangerous option, therefore we present it in a separate section of
the settings page, and show an informative confirmation dialog.
Change-Type: minor
Changelog-Entry: Add an "unsafe" option to bypass drive protection.
Fixes: https://github.com/resin-io/etcher/issues/480
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
Currently, we take the image extension casing into account when
determining if the extension is a recognised one (e.g: `img`, `iso`,
etc). This causes an "Invalid image" error to be thrown when selecting
an image with an uppercase extension, like `UBUNTU.ISO`.
Change-Type: patch
Changelog-Entry: Don't throw an "Invalid image" error if the extension is not in lowercase.
Fixes: https://github.com/resin-io/etcher/issues/567
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
There is a small flaw in the current state validation rules where a
speed that equals zero will be considered as if the speed was missing
giving that `!0 == true`.
Changelog-Entry: Fix state validation error when speed equals zero.
Change-Type: patch
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
JSCS has merged with ESLint. This is the perfect excuse to move to
ESLint and unify both JSHint and JSCS hints under ESLint.
This PR also deprecates `gulp lint` in favour of `npm run lint`.
See: https://medium.com/@markelog/jscs-end-of-the-line-bc9bf0b3fdb2#.zbuwvxa5y
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
* refactor: getter/setter interface for SettingsModel
This PR introduces a getter/setter interface for `SettingsModel`, which
replaces the old way of managing setting values by simply assigning
properties to an object.
This is the first step towards moving the settings functionality to the
Redux store.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
* refactor: store settings in redux store
The state data structure now contains a property called `settings`,
which is a map containing all setting values.
The list of supported settings can be calculated by retrieving the keys
from the `settings` object, which means that if we support a setting, we
must include a default.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
* feat: store settings in localStorage
This functionality was deleted by acb0de2 when moving the settings
object to the redux store, promising that the feature will be added back
in a future commit.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
This is a rather big PR that moves the flash results information to the
Redux store, which simplifies and improves a lot of things as throughly
described in the commits that introduced Redux.
Here's a summary of the changes:
- Add a `flashResults` property to the store.
- Validate the contents of `flashResults`, handling certain edge cases
that make the modal incoherent.
- Split `ImageWriterService.setFlashing()` to
`ImageWriterService.setFlashingFlag()` and
`ImageWriterService.unsetFlashingFlag()`.
- Require the flash results to be passed to
`ImageWriterService.unsetFlashingFlag()`.
- Stop resolving the flash results from `ImageWriterService.flash()`.
- Implement `ImageWriterService.getFlashResults()`.
- Make the `RESET_FLASH_STATE` action reset the flash results.
- Access the source checksum from the store in the "finish" page,
instead of requiring the controller to pass it as a state parameter.
- Implement `.wasLastFlashSuccessful()` function in the main controller
to replace the `.success` property.
- Completely remove the `.success` property in the main controller.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
The commit 0f8136f, which enforced validation for the state object,
introduced a subtle bug where `Missing state eta` would be thrown if
`eta === 0`, since `!0` evaluates to `true`.
This would cause the flashing to stop right at the end (when eta is
zero).
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
So the property is consistent from what we get from `etcher-image-write`
and we don't have to unnecessarily rename ourselves to pass it to the
model.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
ES6 fat arrows provide reasonable `this` behaviour, which protects us
from some subtle accidental bugs, and erradicates `const self = this`
from the codebase.
Far arrows were not applied in Mocha code and AngularJS
controllers/services constructors since these frameworks rely on
`.bind()` on those functions.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
Currently, the "flash" property of the application state containing both
the actual flashing state, and the flashing flag.
To simplify things, the following changes have been introduced:
- Move `flash.flashing` to `isFlashing`.
- Move `flash.state` to `flashState`.
- Rename `SET_FLASHING` to `SET_FLASHING_FLAG`.
- Extract `UNSET_FLASHING_FLAG` from `SET_FLASHING_FLAG`.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
Referencing actions as properties of an object is more reliable than
just hardcoding strings everywhere.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
Currently, we allow updating the flashing state independently on the
value of the `flashing` property.
In order to maintain the application state coherent, we deny updating
the flashing state if we're not currently flashing, which lets us safely
assume that the state will be in a reset state if we're not flashing.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
Currently, we were taking care of resetting the flashing state manually
across several controllers. Now that data mutations live in a single
place, we trigger a flash state reset whenever the flashing flag is set
to false, which reliably handles every case and allows us to forget
about it.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
Now that we have a central source of truth for state mutations, the
auto-select feature fits really well in the redux store, particularly
inside the `SET_AVAILABLE_DRIVES` action.
This also has the great benefit that we can unit test the auto-selection
logic, which was not particularly trivial before, when such code lived
in the controller instead.
The only downside of this approach is that we lose the nice "Auto-select
drive" analytics event, which will be re-added very soon in a future PR.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>