mirror of
https://github.com/balena-io/etcher.git
synced 2025-07-10 04:46:32 +00:00
fix: Support raw images without secondary file extension (#1724)
This allows selection of images without a secondary file extension (i.e. `example.gz`, compared to `example.img.gz`) by defaulting to `img` in the image-stream handlers, should no secondary extension be found. Further this adjusts `.getPenultimateFileExtension()` to return `null` if the detected penultimate extension is not a known file extension. Change-Type: patch
This commit is contained in:
parent
a3c54f22c8
commit
b5912eb9f6
@ -33,6 +33,13 @@ const fileExtensions = require('../shared/file-extensions')
|
|||||||
const path = require('path')
|
const path = require('path')
|
||||||
const errors = require('../shared/errors')
|
const errors = require('../shared/errors')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Default image extension to be assumed
|
||||||
|
* @type {String}
|
||||||
|
* @constant
|
||||||
|
*/
|
||||||
|
const DEFAULT_EXT = 'img'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary Image handlers
|
* @summary Image handlers
|
||||||
* @namespace handlers
|
* @namespace handlers
|
||||||
@ -57,7 +64,7 @@ module.exports = {
|
|||||||
return {
|
return {
|
||||||
path: imagePath,
|
path: imagePath,
|
||||||
archiveExtension: fileExtensions.getLastFileExtension(imagePath),
|
archiveExtension: fileExtensions.getLastFileExtension(imagePath),
|
||||||
extension: fileExtensions.getPenultimateFileExtension(imagePath),
|
extension: fileExtensions.getPenultimateFileExtension(imagePath) || DEFAULT_EXT,
|
||||||
stream: fs.createReadStream(imagePath),
|
stream: fs.createReadStream(imagePath),
|
||||||
size: {
|
size: {
|
||||||
original: options.size,
|
original: options.size,
|
||||||
@ -97,7 +104,7 @@ module.exports = {
|
|||||||
return {
|
return {
|
||||||
path: imagePath,
|
path: imagePath,
|
||||||
archiveExtension: fileExtensions.getLastFileExtension(imagePath),
|
archiveExtension: fileExtensions.getLastFileExtension(imagePath),
|
||||||
extension: fileExtensions.getPenultimateFileExtension(imagePath),
|
extension: fileExtensions.getPenultimateFileExtension(imagePath) || DEFAULT_EXT,
|
||||||
stream: fs.createReadStream(imagePath),
|
stream: fs.createReadStream(imagePath),
|
||||||
size: {
|
size: {
|
||||||
original: options.size,
|
original: options.size,
|
||||||
@ -138,7 +145,7 @@ module.exports = {
|
|||||||
return {
|
return {
|
||||||
path: imagePath,
|
path: imagePath,
|
||||||
archiveExtension: fileExtensions.getLastFileExtension(imagePath),
|
archiveExtension: fileExtensions.getLastFileExtension(imagePath),
|
||||||
extension: fileExtensions.getPenultimateFileExtension(imagePath),
|
extension: fileExtensions.getPenultimateFileExtension(imagePath) || DEFAULT_EXT,
|
||||||
stream: fs.createReadStream(imagePath),
|
stream: fs.createReadStream(imagePath),
|
||||||
size: {
|
size: {
|
||||||
original: options.size,
|
original: options.size,
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
|
const mime = require('mime-types')
|
||||||
const _ = require('lodash')
|
const _ = require('lodash')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -45,15 +46,15 @@ exports.getFileExtensions = _.memoize((filePath) => {
|
|||||||
* @public
|
* @public
|
||||||
*
|
*
|
||||||
* @param {String} filePath - file path
|
* @param {String} filePath - file path
|
||||||
* @returns {(String|Undefined)} last extension
|
* @returns {(String|Null)} last extension
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* const extension = fileExtensions.getLastFileExtension('path/to/foo.img.gz');
|
* const extension = fileExtensions.getLastFileExtension('path/to/foo.img.gz');
|
||||||
* console.log(extension);
|
* console.log(extension);
|
||||||
* > [ 'gz' ]
|
* > 'gz'
|
||||||
*/
|
*/
|
||||||
exports.getLastFileExtension = (filePath) => {
|
exports.getLastFileExtension = (filePath) => {
|
||||||
return _.last(exports.getFileExtensions(filePath))
|
return _.last(exports.getFileExtensions(filePath)) || null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -62,13 +63,14 @@ exports.getLastFileExtension = (filePath) => {
|
|||||||
* @public
|
* @public
|
||||||
*
|
*
|
||||||
* @param {String} filePath - file path
|
* @param {String} filePath - file path
|
||||||
* @returns {(String|Undefined)} penultimate extension
|
* @returns {(String|Null)} penultimate extension
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* const extension = fileExtensions.getPenultimateFileExtension('path/to/foo.img.gz');
|
* const extension = fileExtensions.getPenultimateFileExtension('path/to/foo.img.gz');
|
||||||
* console.log(extension);
|
* console.log(extension);
|
||||||
* > [ 'img' ]
|
* > 'img'
|
||||||
*/
|
*/
|
||||||
exports.getPenultimateFileExtension = (filePath) => {
|
exports.getPenultimateFileExtension = (filePath) => {
|
||||||
return _.last(_.initial(exports.getFileExtensions(filePath)))
|
const ext = _.last(_.initial(exports.getFileExtensions(filePath)))
|
||||||
|
return !_.isNil(ext) && mime.lookup(ext) ? ext : null
|
||||||
}
|
}
|
||||||
|
@ -311,6 +311,10 @@ const storeReducer = (state = DEFAULT_STATE, action) => {
|
|||||||
return state.setIn([ 'selection', 'drive' ], Immutable.fromJS(action.data))
|
return state.setIn([ 'selection', 'drive' ], Immutable.fromJS(action.data))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(jhermsmeier): Consolidate these assertions
|
||||||
|
// with image-stream / supported-formats, and have *one*
|
||||||
|
// place where all the image extension / format handling
|
||||||
|
// takes place, to avoid having to check 2+ locations with different logic
|
||||||
case ACTIONS.SELECT_IMAGE: {
|
case ACTIONS.SELECT_IMAGE: {
|
||||||
if (!action.data.path) {
|
if (!action.data.path) {
|
||||||
throw errors.createError({
|
throw errors.createError({
|
||||||
|
@ -122,10 +122,15 @@ exports.isSupportedImage = (imagePath) => {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return _.every([
|
if (_.every([
|
||||||
_.includes(exports.getCompressedExtensions(), lastExtension),
|
_.includes(exports.getCompressedExtensions(), lastExtension),
|
||||||
_.includes(exports.getNonCompressedExtensions(), penultimateExtension)
|
_.includes(exports.getNonCompressedExtensions(), penultimateExtension)
|
||||||
])
|
])) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return _.isNil(penultimateExtension) &&
|
||||||
|
_.includes(exports.getCompressedExtensions(), lastExtension)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -101,7 +101,7 @@ describe('Shared: fileExtensions', function () {
|
|||||||
|
|
||||||
describe('.getLastFileExtension()', function () {
|
describe('.getLastFileExtension()', function () {
|
||||||
it('should return undefined if the file path has no extension', function () {
|
it('should return undefined if the file path has no extension', function () {
|
||||||
m.chai.expect(fileExtensions.getLastFileExtension('foo')).to.be.undefined
|
m.chai.expect(fileExtensions.getLastFileExtension('foo')).to.equal(null)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return the extension if there is only one extension', function () {
|
it('should return the extension if there is only one extension', function () {
|
||||||
@ -119,11 +119,11 @@ describe('Shared: fileExtensions', function () {
|
|||||||
|
|
||||||
describe('.getPenultimateFileExtension()', function () {
|
describe('.getPenultimateFileExtension()', function () {
|
||||||
it('should return undefined in the file path has no extension', function () {
|
it('should return undefined in the file path has no extension', function () {
|
||||||
m.chai.expect(fileExtensions.getPenultimateFileExtension('foo')).to.be.undefined
|
m.chai.expect(fileExtensions.getPenultimateFileExtension('foo')).to.equal(null)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return undefined if there is only one extension', function () {
|
it('should return undefined if there is only one extension', function () {
|
||||||
m.chai.expect(fileExtensions.getPenultimateFileExtension('foo.img')).to.be.undefined
|
m.chai.expect(fileExtensions.getPenultimateFileExtension('foo.img')).to.equal(null)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return the penultimate extension if there are two extensions', function () {
|
it('should return the penultimate extension if there are two extensions', function () {
|
||||||
|
@ -370,6 +370,42 @@ describe('Model: selectionState', function () {
|
|||||||
m.chai.expect(imagePath).to.equal('foo.zip')
|
m.chai.expect(imagePath).to.equal('foo.zip')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should infer a compressed raw image if the penultimate extension is missing', function () {
|
||||||
|
selectionState.setImage({
|
||||||
|
path: 'foo.xz',
|
||||||
|
extension: 'img',
|
||||||
|
archiveExtension: 'xz',
|
||||||
|
size: {
|
||||||
|
original: 999999999,
|
||||||
|
final: {
|
||||||
|
estimation: false,
|
||||||
|
value: 999999999
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const imagePath = selectionState.getImagePath()
|
||||||
|
m.chai.expect(imagePath).to.equal('foo.xz')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should infer a compressed raw image if the penultimate extension is not a file extension', function () {
|
||||||
|
selectionState.setImage({
|
||||||
|
path: 'something.linux-x86-64.gz',
|
||||||
|
extension: 'img',
|
||||||
|
archiveExtension: 'gz',
|
||||||
|
size: {
|
||||||
|
original: 999999999,
|
||||||
|
final: {
|
||||||
|
estimation: false,
|
||||||
|
value: 999999999
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const imagePath = selectionState.getImagePath()
|
||||||
|
m.chai.expect(imagePath).to.equal('something.linux-x86-64.gz')
|
||||||
|
})
|
||||||
|
|
||||||
it('should throw if no path', function () {
|
it('should throw if no path', function () {
|
||||||
m.chai.expect(function () {
|
m.chai.expect(function () {
|
||||||
selectionState.setImage({
|
selectionState.setImage({
|
||||||
|
Loading…
x
Reference in New Issue
Block a user