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>
The main page controller contained a lot of undocumented and untested
logic. As a first step towards cleaning up the whole thing, this PR
introduces the following changes:
- Implement `ImageSelectionController`, `DriveSelectionController`, and
`FlashController` as children of `MainController`. Each of them is
used by the appropriate main page "steps", and contains logic specific
to them. The `MainController` hosts functionality that applies to the
page as a whole.
- Add JSDoc annotations fo every controller function/property.
- Unit test several controller functions.
- Simplify template logic.
The "GUI fifty-thousand foot view" section in ARCHITECTURE.md has been
removed since there is no longer a single place where you can see all
the interactions between components.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
This PR fixes an uncaught error being thrown when
`OSOpenExternalService.open()` is called with an undefined value.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
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 subscribe to the store change events and manually update a
`.state` property in `ImageWriterService`. A much more elegant approach
is to provide a `.getFlashState()` function that fetches the flash state
directly from the store when needed.
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>
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>
* upgrade: etcher-image-stream to v2.3.0
This version contains support for `hddimg` files.
Changelog-Entry: Add support for `hddimg` images.
Change-Type: minor
Fixes: https://github.com/resin-io/etcher/issues/549
Link: https://github.com/resin-io-modules/etcher-image-stream/blob/master/CHANGELOG.md#v230---2016-07-01
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
* minifix(GUI): add a "many more" tooltip in the first step
The amount of image types we support is growing exponentially. Adding
the uncompressed extensions and the compressed ones in a tooltip gave us
room the breathe in the past, but its not enough anymore.
The current approach allows us to scale forever: we list the first three
extensions, and add a "many more" tooltip that shows all the rest.
Change-Type: patch
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
The code that performs an HTTP request to the S3 bucket where released
are stored and determines which is the latest available version was
extracted to a separate module called `etcher-latest-version`, mainly
for the website to be able to re-use this functionality.
See: https://github.com/resin-io-modules/etcher-latest-version
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>
The `NotifierService` was removed in 934d287, but for some reason, the
CI servers didn't report an error because we forgot to delete the
corresponding test file.
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>
If the user has a selected drive, but a new scan shows that such drive
is no longer available, then the selected drive should be de-selected.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
We shouldn't allow a write-protected drive from being selected, since
users will get confusing `EPERM` errors later on.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
Currently, Etcher's application state is stored among various models,
making it a bit hard to mentually visualise the application state at a
certain point of execution. This also means that some quirky bugs
appear, and we have to take non-elegant measures to mitigate them, for
example:
- The current drive selection might be invalid if the current available
drive list doesn't contain it anymore.
- The progress state might be modified while there is no flashing in
process.
- We have to rely on event emission to propagate the current state to
the application progress bar.
- The validity of a selected drive might depend on the currently
selected image.
While all these issues can be addressed with common programming
techniques, Redux introduces a new way of thinking about the application
state that make the above problems non-existent, or trivial to fix.
This PR creates a Redux store containing the logic used to mutate state
from:
- `SelectionStateModel`.
- `DrivesMode`.
- `ImageWriterService`.
We are also making extra effort to preserve the public APIs from the
models, which we will be simplifying in later commits.
There is still much to be done, but we're happy to be taking the first
steo towards a much cleaner architecture.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
The concept of a drive scanner lends itself very well to the concept of
"reactive programming", since the "available drives" make perfect sense
as an "Observable".
For this reason, the module was re-implemented using RxJS, which greatly
simplifies things and erradicates certain edge cases we were protecting
ourselves from automatically.
Other changes made in this PR:
- `DriveScannerService` no longer requires `DrivesModel`. The
`DriveScannerService` is the one in charge of populating the model.
- `DriveScannerService.scan()` no longer returns an `EventEmitter`. The
service itself is now an `EventEmitter`.
- `DriveScannerService` now emits a `drives`, not a `scan` event.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
Currently, we extract all the extensions from an image path and report
back that the image is invalid if *any* of the extensions is not a
valida extension, however this can cause trouble with images including
information between dots that are not strictly extensions.
For example:
```
elementaryos-0.3.2-stable-i386.20151209.iso
```
Etcher will consider `20151209` to be an invalid extension and therefore
will prevent such image from being selected.
As a way to allow these corner cases but will make use of our enforced
check controls, the validation routine will only consider extensions
starting from the first non compressed extension.
This PR also includes logic to show a nice GUI dialog saying that the
image is invalid in order to provide a much better experience than just
silently failing.
Fixes: https://github.com/resin-io/etcher/issues/492
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
We rely on a millisecond to determine the state of the update notifier
logic, which can fail at least once per 50 builds or so.
We increase the time difference to account for this little room for
error.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
* Implement SelectionStateModel.isDriveLocked()
Notice we also increase the maximum number of lines JSCS check, since
the `SelectionStateModel` test suite already met that limit.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
* Show a "Locked" label if the drive is write-protected
Fixes: https://github.com/resin-io/etcher/issues/458
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
Currently, we detect the extensions of an image path by finding the
first dot, and splitting everything afterwards, however this will fail
with image paths containing dots, like `foo.1.2.3-bar.iso.gz`.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
Currently, `DriveScannerService` is in charge of both scanning the
available drives and maintaining the state of the currently detected
drives.
To honour the single responsibility principle, we split this service
into a `DrivesModel`, which is incharge of maintaining the state without
caring how they are detected, and `DriveScannerService`, which only
scans the available drives and modified `DrivesModel` accordingly.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
* Fix lint warnings
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
* Run linter as part of `npm test`
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
There has been changes in the model regarding with how the image
information was stored, and the dropzone directive was never modified to
comply with those changes.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
* Make .selectImage() dialog return an object with a `path` property
This allows to return more than one value for the selected image,
like image size and other metadata for example.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
* Return image size from .selectImage() dialog
This property will be useful to perform some sanity checks, like
ensuring the selected drive is large enough to contain the image.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
* Store both the image path and the image size in the selection model
In order to simplify accessing such properties in an encapsulated
manner, `SelectionStateModel.getImage()` was replaced with the following
functions:
- `SelectionStateModel.getImagePath()`.
- `SelectionStateModel.getImageSize()`.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
* Increase SelectionStateModel setter validation
The model not providing any kind of validation was the source of some
bugs I encountered while implementing the previous commits that would
not have happened otherwise.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
* Prevent selection of drives that are not large enough
- The drive selector modal was modified such that drives that are not
large enough are crossed out, and the user is not able to click them.
- In the case of drive auto-selection, not large enough drives won't
attempt to get autoselected.
This commit introduces:
- The `SelectionStateModel.isDriveLargeEnough()` function.
Fixes: https://github.com/resin-io/etcher/issues/344
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
Auto-update functionality is not ready for usage. As a workaround to
prevent users staying with older versions, we now check for updates at
startup, and if the user is not running the latest version, we present a
modal informing the user of the availiblity of a new version, and
provide a call to action to open the Etcher website in his web browser.
Extra features:
- The user can skip the update, and tell the program to delay the
notification for 7 days.
Misc changes:
- Center modal with flexbox, to allow more flexibility on the modal height.
interacting with the S3 server.
- Implement `ManifestBindService`, which now serves as a backend for the
`manifest-bind` directive to allow the directive's functionality to be
re-used by other services.
- Namespace checkbox styles that are specific to the settings page.
Fixes: https://github.com/resin-io/etcher/issues/396
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
* Refactor SelectionStateModel.isCurrentDrive() to only check `.device`
This set of changes modify `SelectionStateModel.isCurrentDrive()` to
only return true if the `.device` property is equal, and ignore the
rest, since there can be tons of false positives if the drive doesn't
exactly matches otherwise.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
* Implement SelectionStateModel.toggleSetDrive()
This function is useful to toggle the current drive selected based on a
drive object.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
* Make use of SelectionStateModel directly in DriveSelectorModal
Currently, we were basically proxying (with some additions)
`SelectionStateModel` through a service called
`DriveSelectorStateService`, which was completely unnecessary.
Now, we make use of `SelectionStateModel`, after moving any custom logic
from `DriveSelectorStateService` to `SelectionStateModel`.
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
This PR introduces `etcher-image-stream`:
https://github.com/resin-io-modules/etcher-image-stream
a module that will handle support for decompression, URL streaming, and
any other way to get a source of data to write in Etcher.
Most of the changes in this PR are because XZ decompression includes a
native dependency (no pure JS implementations out there for now), so we
had to tweak various things for the dependency to work correctly on
Etcher/Electron.
See: https://github.com/resin-io/etcher/issues/325
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>