diff --git a/dev-app-update.yml b/dev-app-update.yml new file mode 100644 index 00000000..41f67598 --- /dev/null +++ b/dev-app-update.yml @@ -0,0 +1,4 @@ +owner: balena-io +repo: etcher +provider: github +updaterCacheDirName: balena-etcher-updater diff --git a/lib/gui/app/app.js b/lib/gui/app/app.js index 303fbe0e..897f00c1 100644 --- a/lib/gui/app/app.js +++ b/lib/gui/app/app.js @@ -27,24 +27,18 @@ var angular = require('angular') /* eslint-enable no-var */ const electron = require('electron') -const Bluebird = require('bluebird') const sdk = require('etcher-sdk') const _ = require('lodash') -const semver = require('semver') const uuidV4 = require('uuid/v4') const EXIT_CODES = require('../../shared/exit-codes') const messages = require('../../shared/messages') -const s3Packages = require('../../shared/s3-packages') -const release = require('../../shared/release') const store = require('./models/store') -const errors = require('../../shared/errors') const packageJSON = require('../../../package.json') const flashState = require('./models/flash-state') const settings = require('./models/settings') const windowProgress = require('./os/window-progress') const analytics = require('./modules/analytics') -const updateNotifier = require('./components/update-notifier') const availableDrives = require('./models/available-drives') const selectionState = require('./models/selection-state') const driveScanner = require('./modules/drive-scanner') @@ -134,84 +128,6 @@ app.run(() => { version: currentVersion, applicationSessionUuid }) - - const shouldCheckForUpdates = updateNotifier.shouldCheckForUpdates({ - currentVersion, - lastSleptUpdateNotifier: settings.get('lastSleptUpdateNotifier'), - lastSleptUpdateNotifierVersion: settings.get('lastSleptUpdateNotifierVersion') - }) - - const isStableRelease = release.isStableRelease(currentVersion) - const updatesEnabled = settings.get('updatesEnabled') - - if (!shouldCheckForUpdates || !updatesEnabled) { - analytics.logEvent('Not checking for updates', { - shouldCheckForUpdates, - updatesEnabled, - stable: isStableRelease, - applicationSessionUuid - }) - - return Bluebird.resolve() - } - - const updateSemverRange = packageJSON.updates.semverRange - const includeUnstableChannel = settings.get('includeUnstableUpdateChannel') - - analytics.logEvent('Checking for updates', { - currentVersion, - stable: isStableRelease, - updateSemverRange, - includeUnstableChannel, - applicationSessionUuid - }) - - return s3Packages.getLatestVersion(release.getReleaseType(currentVersion), { - range: updateSemverRange, - includeUnstableChannel - }).then((latestVersion) => { - if (semver.gte(currentVersion, latestVersion || '0.0.0')) { - analytics.logEvent('Update notification skipped', { - reason: 'Latest version', - applicationSessionUuid - }) - return Bluebird.resolve() - } - - // In case the internet connection is not good and checking the - // latest published version takes too long, only show notify - // the user about the new version if he didn't start the flash - // process (e.g: selected an image), otherwise such interruption - // might be annoying. - if (selectionState.hasImage()) { - analytics.logEvent('Update notification skipped', { - reason: 'Image selected', - applicationSessionUuid - }) - return Bluebird.resolve() - } - - analytics.logEvent('Notifying update', { - latestVersion, - applicationSessionUuid - }) - - return updateNotifier.notify(latestVersion, { - allowSleepUpdateCheck: isStableRelease - }) - - // If the error is an update user error, then we don't want - // to bother users each time they open the app. - // See: https://github.com/resin-io/etcher/issues/1525 - }).catch((error) => { - return errors.isUserError(error) && error.code === 'UPDATE_USER_ERROR' - }, (error) => { - analytics.logEvent('Update check user error', { - title: errors.getTitle(error), - description: errors.getDescription(error), - applicationSessionUuid - }) - }).catch(exceptionReporter.report) }) app.run(() => { diff --git a/lib/gui/app/components/update-notifier.js b/lib/gui/app/components/update-notifier.js deleted file mode 100644 index 4911818a..00000000 --- a/lib/gui/app/components/update-notifier.js +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2016 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const electron = require('electron') -const Bluebird = require('bluebird') -const _ = require('lodash') -const store = require('../models/store') -const settings = require('../models/settings') -const analytics = require('../modules/analytics') -const units = require('../../../shared/units') -const release = require('../../../shared/release') -const packageJSON = require('../../../../package.json') - -/** - * @summary The number of days the update notifier can be put to sleep - * @constant - * @private - * @type {Number} - */ -exports.UPDATE_NOTIFIER_SLEEP_DAYS = packageJSON.updates.sleepDays - -/** - * @summary The current Electron browser window - * @constant - * @private - * @type {Object} - */ -const currentWindow = electron.remote.getCurrentWindow() - -/** - * @summary Determine if it's time to check for updates - * @function - * @public - * - * @param {Object} options - options - * @param {Number} [options.lastSleptUpdateNotifier] - last slept update notifier time - * @param {String} [options.lastSleptUpdateNotifierVersion] - last slept update notifier version - * @param {String} options.currentVersion - current version - * @returns {Boolean} should check for updates - * - * @example - * if (updateNotifier.shouldCheckForUpdates({ - * lastSleptUpdateNotifier: Date.now(), - * lastSleptUpdateNotifierVersion: '1.0.0', - * currentVersion: '1.0.0' - * })) { - * console.log('We should check for updates!'); - * } - */ -exports.shouldCheckForUpdates = (options) => { - if (settings.get('resinUpdateLock')) { - return false - } - - _.defaults(options, { - lastSleptUpdateNotifierVersion: options.currentVersion - }) - - if (_.some([ - !options.lastSleptUpdateNotifier, - !release.isStableRelease(options.currentVersion), - options.currentVersion !== options.lastSleptUpdateNotifierVersion - ])) { - return true - } - - return Date.now() - options.lastSleptUpdateNotifier > units.daysToMilliseconds(exports.UPDATE_NOTIFIER_SLEEP_DAYS) -} - -/** - * @summary Open the update notifier widget - * @function - * @public - * - * @param {String} version - version - * @param {Object} [options] - options - * @param {Boolean} [options.allowSleepUpdateCheck=true] - allow sleeping the update check - * @returns {Promise} - * - * @example - * updateNotifier.notify('1.0.0-beta.16', { - * allowSleepUpdateCheck: true - * }); - */ -exports.notify = (version, options = {}) => { - const BUTTONS = [ - 'Download', - 'Skip' - ] - - const BUTTON_CONFIRMATION_INDEX = _.indexOf(BUTTONS, _.first(BUTTONS)) - const BUTTON_REJECTION_INDEX = _.indexOf(BUTTONS, _.last(BUTTONS)) - - const dialogOptions = { - type: 'info', - buttons: BUTTONS, - defaultId: BUTTON_CONFIRMATION_INDEX, - cancelId: BUTTON_REJECTION_INDEX, - title: 'New Update Available!', - message: `Etcher ${version} is available for download` - } - - if (_.get(options, [ 'allowSleepUpdateCheck' ], true)) { - _.merge(dialogOptions, { - checkboxLabel: `Remind me again in ${exports.UPDATE_NOTIFIER_SLEEP_DAYS} days`, - checkboxChecked: false - }) - } - - return new Bluebird((resolve) => { - electron.remote.dialog.showMessageBox(currentWindow, dialogOptions, (response, checkboxChecked) => { - return resolve({ - agreed: response === BUTTON_CONFIRMATION_INDEX, - sleepUpdateCheck: checkboxChecked || false - }) - }) - }).tap((results) => { - // Only update the last slept update timestamp if the - // user ticked the "Remind me again in ..." checkbox, - // but didn't agree. - if (results.sleepUpdateCheck && !results.agreed) { - return Bluebird.all([ - settings.set('lastSleptUpdateNotifier', Date.now()), - settings.set('lastSleptUpdateNotifierVersion', packageJSON.version) - ]) - } - - return Bluebird.resolve() - }).then((results) => { - analytics.logEvent('Close update modal', { - sleepUpdateCheck: results.sleepUpdateCheck, - notifyVersion: version, - currentVersion: packageJSON.version, - agreed: results.agreed, - applicationSessionUuid: store.getState().toJS().applicationSessionUuid, - flashingWorkflowUuid: store.getState().toJS().flashingWorkflowUuid - }) - - if (results.agreed) { - electron.shell.openExternal('https://etcher.io?ref=etcher_update') - } - }) -} diff --git a/lib/gui/app/models/settings.js b/lib/gui/app/models/settings.js index 371922b2..5f3dcd89 100644 --- a/lib/gui/app/models/settings.js +++ b/lib/gui/app/models/settings.js @@ -24,7 +24,6 @@ const _ = require('lodash') const Bluebird = require('bluebird') const localSettings = require('./local-settings') const errors = require('../../../shared/errors') -const release = require('../../../shared/release') const packageJSON = require('../../../../package.json') const debug = require('debug')('etcher:models:settings') @@ -39,7 +38,6 @@ const DEFAULT_SETTINGS = { unmountOnSuccess: true, validateWriteOnSuccess: true, updatesEnabled: packageJSON.updates.enabled && !_.includes([ 'rpm', 'deb' ], packageJSON.packageType), - includeUnstableUpdateChannel: !release.isStableRelease(packageJSON.version), lastSleptUpdateNotifier: null, lastSleptUpdateNotifierVersion: null, desktopNotifications: true diff --git a/lib/gui/app/modules/analytics.js b/lib/gui/app/modules/analytics.js index 340fec55..7a8072f0 100644 --- a/lib/gui/app/modules/analytics.js +++ b/lib/gui/app/modules/analytics.js @@ -20,10 +20,8 @@ const _ = require('lodash') const resinCorvus = require('resin-corvus/browser') const packageJSON = require('../../../../package.json') const settings = require('../models/settings') -const { hasProps } = require('../../../shared/utils') +const { getConfig, hasProps } = require('../../../shared/utils') -const Bluebird = require('bluebird') -const request = Bluebird.promisifyAll(require('request')) const sentryToken = settings.get('analyticsSentryToken') || _.get(packageJSON, [ 'analytics', 'sentry', 'token' ]) const mixpanelToken = settings.get('analyticsMixpanelToken') || @@ -73,16 +71,6 @@ const initConfig = async () => { initConfig() -/** - * @summary Get etcher configs stored online - * @param {String} - url where config.json is stored - */ -// eslint-disable-next-line -function getConfig(url) { - return request.getAsync(url, { json: true }) - .get('body') -} - /** * @summary Check that the client is eligible for analytics * @param {Object} - config diff --git a/lib/gui/app/pages/settings/templates/settings.tpl.html b/lib/gui/app/pages/settings/templates/settings.tpl.html index 48b11f71..313c2cc2 100644 --- a/lib/gui/app/pages/settings/templates/settings.tpl.html +++ b/lib/gui/app/pages/settings/templates/settings.tpl.html @@ -43,14 +43,14 @@ -
+
diff --git a/lib/gui/etcher.js b/lib/gui/etcher.js index 374aa74c..db2350e8 100644 --- a/lib/gui/etcher.js +++ b/lib/gui/etcher.js @@ -18,14 +18,41 @@ const electron = require('electron') const path = require('path') +const _ = require('lodash') +const { autoUpdater } = require('electron-updater') +const Bluebird = require('bluebird') const EXIT_CODES = require('../shared/exit-codes') const buildWindowMenu = require('./menu') const settings = require('./app/models/settings') - +const analytics = require('./app/modules/analytics') +const { getConfig } = require('../shared/utils') /* eslint-disable lodash/prefer-lodash-method */ const config = settings.getDefaults() +const configUrl = settings.get('configUrl') || 'http://balena.io/etcher/static/config.json' + +/** + * + * @param {Number} interval - interval to wait to check for updates + * @example checkForUpdates() + */ +const checkForUpdates = async (interval) => { + // We use a while loop instead of a setInterval to preserve + // async execution time between each function call + while (true) { + try { + const release = await autoUpdater.checkForUpdates() + if (release.updateInfo.stagingPercentage) { + await autoUpdater.downloadUpdate() + } + } catch (err) { + analytics.logException(err) + } + await Bluebird.delay(interval) + } +} + /** * @summary Create Etcher's main window * @example @@ -83,6 +110,28 @@ const createMainWindow = () => { } else { mainWindow.loadURL(`file://${path.join(__dirname, 'app', 'index.html')}`) } + + const page = mainWindow.webContents + + page.once('did-frame-finish-load', async () => { + autoUpdater.on('error', (err) => { + analytics.logException(err) + }) + if (settings.get('updatesEnabled')) { + try { + const onlineConfig = await getConfig(configUrl) + const autoUpdaterConfig = _.get(onlineConfig, [ 'autoUpdates', 'autoUpdaterConfig' ], { + autoDownload: false + }) + _.merge(autoUpdater, autoUpdaterConfig) + // eslint-disable-next-line no-magic-numbers + const checkForUpdatesTimer = _.get(onlineConfig, [ 'autoUpdates', 'checkForUpdatesTimer' ], 300000) + checkForUpdates(checkForUpdatesTimer) + } catch (err) { + analytics.logException(err) + } + } + }) } electron.app.on('window-all-closed', electron.app.quit) diff --git a/lib/shared/release.js b/lib/shared/release.js deleted file mode 100644 index 39bcf2c1..00000000 --- a/lib/shared/release.js +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2017 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const _ = require('lodash') -const semver = require('semver') - -/** - * @summary Application release types - * @namespace RELEASE_TYPE - * @public - */ -exports.RELEASE_TYPE = { - - /** - * @property {String} PRODUCTION - * @memberof RELEASE_TYPE - * @description - * Production release type - */ - PRODUCTION: 'PRODUCTION', - - /** - * @property {String} SNAPSHOT - * @memberof RELEASE_TYPE - * @description - * Snapshot release type - */ - SNAPSHOT: 'SNAPSHOT', - - /** - * @property {String} UNKNOWN - * @memberof RELEASE_TYPE - * @description - * Unknown release type - */ - UNKNOWN: 'UNKNOWN' - -} - -/** - * @summary Get the release type from a version string - * @function - * @public - * - * @param {String} version - application version - * @returns {RELEASE_TYPE} release type - * - * @example - * const version = require('../../package.json').version; - * const releaseType = release.getReleaseType(version); - * - * if (releaseType === release.RELEASE_TYPE.PRODUCTION) { - * console.log('This is a production release!'); - * } - */ -exports.getReleaseType = (version) => { - const GIT_HASH_REGEX = /^[0-9a-f]{7,40}$/ - const buildNumber = _.get(semver.parse(version), [ 'build' ]) - - if (!_.isNil(buildNumber)) { - if (_.isEmpty(buildNumber)) { - return exports.RELEASE_TYPE.PRODUCTION - } - - if (GIT_HASH_REGEX.test(_.first(buildNumber))) { - return exports.RELEASE_TYPE.SNAPSHOT - } - } - - return exports.RELEASE_TYPE.UNKNOWN -} - -/** - * @summary Check if a version is a stable release - * @function - * @public - * - * @param {String} version - version - * @returns {Boolean} whether the version is a stable release - * - * @example - * if (release.isStableRelease('1.0.0')) { - * console.log('This is a stable release'); - * } - */ -exports.isStableRelease = (version) => { - const parsedVersion = semver.parse(version) - return _.every([ - _.isEmpty(_.get(parsedVersion, [ 'prerelease' ])), - _.isEmpty(_.get(parsedVersion, [ 'build' ])) - ]) -} diff --git a/lib/shared/s3-packages.js b/lib/shared/s3-packages.js deleted file mode 100644 index 949e4044..00000000 --- a/lib/shared/s3-packages.js +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright 2017 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const _ = require('lodash') -const semver = require('semver') -const Bluebird = require('bluebird') -const request = Bluebird.promisifyAll(require('request')) -const xml = Bluebird.promisifyAll(require('xml2js')) -const release = require('./release') -const errors = require('./errors') - -/** - * @summary Etcher S3 bucket URLs - * @namespace BUCKET_URL - * @public - */ -exports.BUCKET_URL = { - - /** - * @property {String} PRODUCTION - * @memberof BUCKET_URL - * @description - * Etcher production S3 bucket URL - */ - PRODUCTION: 'https://resin-production-downloads.s3.amazonaws.com', - - /** - * @property {String} SNAPSHOT - * @memberof BUCKET_URL - * @description - * Etcher snapshot S3 bucket URL - */ - SNAPSHOT: 'https://resin-nightly-downloads.s3.amazonaws.com' - -} - -/** - * @summary Etcher S3 package name - * @constant - * @private - * @type {String} - */ -const S3_PACKAGE_NAME = 'etcher' - -/** - * @summary Number of packages per Etcher version - * @constant - * @private - * @type {Number} - */ -const NUMBER_OF_PACKAGES = 8 - -/** - * @summary Get the correct S3 bucket url from a release type - * @function - * @private - * - * @param {RELEASE_TYPE} releaseType - release type - * @returns {?String} S3 bucket url - * - * @example - * const bucketUrl = s3Packages.getBucketUrlFromReleaseType(release.RELEASE_TYPE.PRODUCTION); - * - * if (bucketUrl) { - * console.log(bucketUrl); - * } - */ -exports.getBucketUrlFromReleaseType = (releaseType) => { - if (releaseType === release.RELEASE_TYPE.PRODUCTION) { - return exports.BUCKET_URL.PRODUCTION - } - - if (releaseType === release.RELEASE_TYPE.SNAPSHOT) { - return exports.BUCKET_URL.SNAPSHOT - } - - return null -} - -/** - * @summary Get all remote versions from an S3 bucket - * @function - * @private - * - * @description - * We memoize based on the assumption that the received latest version - * number will not increase while the application is running. - * - * @param {String} bucketUrl - s3 bucket url - * @fulfil {String[]} - remote versions - * @returns {Promise} - * - * @example - * s3Packages.getRemoteVersions(s3Packages.BUCKET_URL.PRODUCTION).then((versions) => { - * _.each(versions, (version) => { - * console.log(version); - * }); - * }); - */ -exports.getRemoteVersions = _.memoize((bucketUrl) => { - if (_.isNil(bucketUrl)) { - return Bluebird.reject(new Error(`Invalid bucket url: ${bucketUrl}`)) - } - - /* eslint-disable lodash/prefer-lodash-method */ - - return request.getAsync(bucketUrl) - - /* eslint-enable lodash/prefer-lodash-method */ - - .get('body') - .then(xml.parseStringAsync) - .get('ListBucketResult') - .then((bucketResult) => { - return _.get(bucketResult, [ 'Contents' ], []) - }) - .reduce((accumulator, entry) => { - const [ name, version ] = _.split(_.first(entry.Key), '/') - - if (name === S3_PACKAGE_NAME) { - if (_.isNil(accumulator[version])) { - accumulator[version] = 1 - } else { - accumulator[version] += 1 - } - } - - return accumulator - }, []) - .then((versions) => { - return _.keys(_.pickBy(versions, (occurrences) => { - return occurrences >= NUMBER_OF_PACKAGES - })) - }) - .catch({ - code: 'ENOTFOUND' - }, { - code: 'ECONNRESET' - }, { - code: 'ECONNREFUSED' - }, { - code: 'EACCES' - }, { - code: 'ETIMEDOUT' - }, { - code: 'EHOSTDOWN' - }, { - code: 'EAI_AGAIN' - }, { - - // May happen when behind a proxy - code: 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY' - - }, { - - // May happen when behind a proxy - code: 'UNABLE_TO_VERIFY_LEAF_SIGNATURE' - - }, (error) => { - throw errors.createUserError({ - title: 'Unable to check for updates', - description: `We got an ${error.code} error in response`, - code: 'UPDATE_USER_ERROR' - }) - }) -}) - -/** - * @summary Check if a version satisfies a semver range - * @function - * @private - * - * @description - * This function is a wrapper around `semver.satisfies` - * to make it work fine with pre-release versions. - * - * @param {String} version - semver version - * @param {String} range - semver range - * @returns {Boolean} whether the version satisfies the range - * - * @example - * if (semverSatisfies('1.0.0', '>=1.0.0')) { - * console.log('The version satisfies the range'); - * } - */ -const semverSatisfies = (version, range) => { - // The `semver` module refuses to apply ranges to prerelease versions - // As a workaround, we drop the prerelease tags, if any, apply the range - // on that, and keep using the prerelease tag from then on. - // See https://github.com/npm/node-semver#prerelease-tags - const strippedVersion = `${semver.major(version)}.${semver.minor(version)}.${semver.patch(version)}` - - return semver.satisfies(strippedVersion, range) -} - -/** - * @summary Get the latest available version for a given release type - * @function - * @public - * - * @param {String} releaseType - release type - * @param {Object} [options] - options - * @param {String} [options.range] - semver range - * @param {Boolean} [options.includeUnstableChannel=false] - include unstable channel - * @fulfil {(String|undefined)} - latest version - * @returns {Promise} - * - * @example - * s3Packages.getLatestVersion(release.RELEASE_TYPE.PRODUCTION, { - * range: '>=2.0.0', - * includeUnstableChannel: true - * }).then((latestVersion) => { - * console.log(`The latest version is: ${latestVersion}`); - * }); - */ -exports.getLatestVersion = (releaseType, options = {}) => { - // For manual testing purposes - const ETCHER_FAKE_S3_LATEST_VERSION = process.env.ETCHER_FAKE_S3_LATEST_VERSION - if (!_.isNil(ETCHER_FAKE_S3_LATEST_VERSION)) { - if (release.getReleaseType(ETCHER_FAKE_S3_LATEST_VERSION) === releaseType) { - return Bluebird.resolve(ETCHER_FAKE_S3_LATEST_VERSION) - } - - return Bluebird.resolve() - } - - const bucketUrl = exports.getBucketUrlFromReleaseType(releaseType) - if (_.isNil(bucketUrl)) { - return Bluebird.reject(new Error(`No bucket URL found for release type: ${releaseType}`)) - } - - /* eslint-disable lodash/prefer-lodash-method */ - return exports.getRemoteVersions(bucketUrl).filter((version) => { - /* eslint-enable lodash/prefer-lodash-method */ - if (_.some([ - - // This check allows us to ignore snapshot builds in production - // buckets, and viceversa, which could have been uploaded by mistake. - release.getReleaseType(version) !== releaseType, - - !release.isStableRelease(version) && !options.includeUnstableChannel - ])) { - return false - } - - return semverSatisfies(version, options.range || '*') - }).then((versions) => { - return _.last(versions.sort(semver.compare)) - }) -} diff --git a/lib/shared/utils.js b/lib/shared/utils.js index fd6688de..c356da5f 100644 --- a/lib/shared/utils.js +++ b/lib/shared/utils.js @@ -17,6 +17,8 @@ 'use strict' const _ = require('lodash') +const Bluebird = require('bluebird') +const request = Bluebird.promisifyAll(require('request')) const errors = require('./errors') /** @@ -156,3 +158,13 @@ exports.hasProps = (obj, props) => { return _.has(obj, prop) }) } + +/** +* @summary Get etcher configs stored online +* @param {String} - url where config.json is stored +*/ +// eslint-disable-next-line +exports.getConfig = (configUrl) => { + return request.getAsync(configUrl, { json: true }) + .get('body') +} diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index ec962006..0765f1eb 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1639,7 +1639,6 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, "requires": { "sprintf-js": "~1.0.2" }, @@ -1647,8 +1646,7 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" } } }, @@ -2455,8 +2453,7 @@ "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" }, "buffer-xor": { "version": "1.0.3", @@ -2577,7 +2574,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, "requires": { "ms": "^2.1.1" } @@ -4424,6 +4420,38 @@ "integrity": "sha512-De+lPAxEcpxvqPTyZAXELNpRZXABRxf+uL/rSykstQhzj/B0l1150G/ExIIxKc16lI89Hgz81J0BHAcbTqK49g==", "dev": true }, + "electron-updater": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-4.0.6.tgz", + "integrity": "sha512-JPGLME6fxJcHG8hX7HWFl6Aew6iVm0DkcrENreKa5SUJCHG+uUaAhxDGDt+YGcNkyx1uJ6eBGMvFxDTLUv67pg==", + "requires": { + "bluebird-lst": "^1.0.6", + "builder-util-runtime": "~8.1.0", + "fs-extra-p": "^7.0.0", + "js-yaml": "^3.12.0", + "lazy-val": "^1.0.3", + "lodash.isequal": "^4.5.0", + "pako": "^1.0.7", + "semver": "^5.6.0", + "source-map-support": "^0.5.9" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "source-map-support": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.10.tgz", + "integrity": "sha512-YfQ3tQFTK/yzlGJuX8pTwa4tifQj4QS2Mj7UegOu8jAz59MqIiMGPXxQhVQiIMNzayuUSF/jEuVnfFF5JqybmQ==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + } + } + }, "electron-window": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/electron-window/-/electron-window-0.8.1.tgz", @@ -5194,8 +5222,7 @@ "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, "esquery": { "version": "1.0.1", @@ -5891,7 +5918,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", - "dev": true, "requires": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", @@ -7070,7 +7096,6 @@ "version": "3.12.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.1.tgz", "integrity": "sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==", - "dev": true, "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -7142,7 +7167,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, "requires": { "graceful-fs": "^4.1.6" } @@ -7330,6 +7354,11 @@ "resolved": "https://registry.npmjs.org/lodash.frompairs/-/lodash.frompairs-4.0.1.tgz", "integrity": "sha1-vE5SB/onV8E25XNhTpZkUGsrG9I=" }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" + }, "lodash.kebabcase": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", @@ -8972,8 +9001,7 @@ "pako": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.8.tgz", - "integrity": "sha512-6i0HVbUfcKaTv+EG8ZTr75az7GFXcLYk9UyLEg7Notv/Ma+z/UG3TCoz6GiNeOrn1E/e63I0X/Hpw18jHOTUnA==", - "dev": true + "integrity": "sha512-6i0HVbUfcKaTv+EG8ZTr75az7GFXcLYk9UyLEg7Notv/Ma+z/UG3TCoz6GiNeOrn1E/e63I0X/Hpw18jHOTUnA==" }, "parallel-transform": { "version": "1.1.0", @@ -12576,8 +12604,7 @@ "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" }, "unpipe": { "version": "1.0.0", diff --git a/package.json b/package.json index 2c377c5e..dcc83cba 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ }, "scripts": { "test": "make lint test sanity-checks", - "start": "electron lib/start.js", + "start": "./node_modules/.bin/electron .", "configure": "node-gyp configure", "build": "node-gyp build", "install": "node-gyp rebuild", @@ -49,6 +49,7 @@ "debug": "^3.1.0", "electron-is-running-in-asar": "^1.0.0", "etcher-sdk": "^2.0.1", + "electron-updater": "4.0.6", "flexboxgrid": "^6.3.0", "immutable": "^3.8.1", "inactivity-timer": "^1.0.0", diff --git a/tests/gui/components/update-notifier.spec.js b/tests/gui/components/update-notifier.spec.js deleted file mode 100644 index a68af42d..00000000 --- a/tests/gui/components/update-notifier.spec.js +++ /dev/null @@ -1,450 +0,0 @@ -/* - * Copyright 2017 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const m = require('mochainon') -const _ = require('lodash') -const units = require('../../../lib/shared/units') -const updateNotifier = require('../../../lib/gui/app/components/update-notifier') - -describe('Browser: updateNotifier', function () { - describe('.UPDATE_NOTIFIER_SLEEP_DAYS', function () { - it('should be an integer', function () { - m.chai.expect(_.isInteger(updateNotifier.UPDATE_NOTIFIER_SLEEP_DAYS)).to.be.true - }) - - it('should be greater than 0', function () { - m.chai.expect(updateNotifier.UPDATE_NOTIFIER_SLEEP_DAYS > 0).to.be.true - }) - }) - - describe('.shouldCheckForUpdates()', function () { - const UPDATE_NOTIFIER_SLEEP_MS = units.daysToMilliseconds(updateNotifier.UPDATE_NOTIFIER_SLEEP_DAYS) - - _.each([ - - // Given the `lastSleptUpdateNotifier` was never updated - - { - options: { - lastSleptUpdateNotifier: undefined, - lastSleptUpdateNotifierVersion: undefined, - currentVersion: '1.0.0' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: undefined, - lastSleptUpdateNotifierVersion: undefined, - currentVersion: '1.0.0+6374412' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: undefined, - lastSleptUpdateNotifierVersion: undefined, - currentVersion: '1.0.0+foo' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: undefined, - lastSleptUpdateNotifierVersion: '1.0.0', - currentVersion: '1.0.0' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: undefined, - lastSleptUpdateNotifierVersion: '1.0.0+6374412', - currentVersion: '1.0.0+6374412' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: undefined, - lastSleptUpdateNotifierVersion: '1.0.0+foo', - currentVersion: '1.0.0+foo' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: undefined, - lastSleptUpdateNotifierVersion: '0.0.0', - currentVersion: '1.0.0' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: undefined, - lastSleptUpdateNotifierVersion: '0.0.0', - currentVersion: '1.0.0+6374412' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: undefined, - lastSleptUpdateNotifierVersion: '0.0.0', - currentVersion: '1.0.0+foo' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: undefined, - lastSleptUpdateNotifierVersion: '99.9.9', - currentVersion: '1.0.0' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: undefined, - lastSleptUpdateNotifierVersion: '99.9.9', - currentVersion: '1.0.0+6374412' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: undefined, - lastSleptUpdateNotifierVersion: '99.9.9', - currentVersion: '1.0.0+foo' - }, - expected: true - }, - - // Given the `lastSleptUpdateNotifier` was very recently updated - - { - options: { - lastSleptUpdateNotifier: Date.now() - 1000, - lastSleptUpdateNotifierVersion: '1.0.0', - currentVersion: '1.0.0' - }, - expected: false - }, - { - options: { - lastSleptUpdateNotifier: Date.now() - 1000, - lastSleptUpdateNotifierVersion: '1.0.0+6374412', - currentVersion: '1.0.0+6374412' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: Date.now() - 1000, - lastSleptUpdateNotifierVersion: '1.0.0+foo', - currentVersion: '1.0.0+foo' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: Date.now() - 1000, - lastSleptUpdateNotifierVersion: '0.0.0', - currentVersion: '1.0.0' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: Date.now() - 1000, - lastSleptUpdateNotifierVersion: '0.0.0', - currentVersion: '1.0.0+6374412' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: Date.now() - 1000, - lastSleptUpdateNotifierVersion: '0.0.0', - currentVersion: '1.0.0+foo' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: Date.now() - 1000, - lastSleptUpdateNotifierVersion: '99.9.9', - currentVersion: '1.0.0' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: Date.now() - 1000, - lastSleptUpdateNotifierVersion: '99.9.9', - currentVersion: '1.0.0+6374412' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: Date.now() - 1000, - lastSleptUpdateNotifierVersion: '99.9.9', - currentVersion: '1.0.0+foo' - }, - expected: true - }, - - // Given the `lastSleptUpdateNotifier` was updated in the future - - { - options: { - lastSleptUpdateNotifier: Date.now() + 1000, - lastSleptUpdateNotifierVersion: '1.0.0', - currentVersion: '1.0.0' - }, - expected: false - }, - { - options: { - lastSleptUpdateNotifier: Date.now() + 1000, - lastSleptUpdateNotifierVersion: '1.0.0+6374412', - currentVersion: '1.0.0+6374412' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: Date.now() + 1000, - lastSleptUpdateNotifierVersion: '1.0.0+foo', - currentVersion: '1.0.0+foo' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: Date.now() + 1000, - lastSleptUpdateNotifierVersion: '0.0.0', - currentVersion: '1.0.0' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: Date.now() + 1000, - lastSleptUpdateNotifierVersion: '0.0.0', - currentVersion: '1.0.0+6374412' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: Date.now() + 1000, - lastSleptUpdateNotifierVersion: '0.0.0', - currentVersion: '1.0.0+foo' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: Date.now() + 1000, - lastSleptUpdateNotifierVersion: '99.9.9', - currentVersion: '1.0.0' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: Date.now() + 1000, - lastSleptUpdateNotifierVersion: '99.9.9', - currentVersion: '1.0.0+6374412' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: Date.now() + 1000, - lastSleptUpdateNotifierVersion: '99.9.9', - currentVersion: '1.0.0+foo' - }, - expected: true - }, - - // Given the `lastSleptUpdateNotifier` was updated far in the future - - { - options: { - lastSleptUpdateNotifier: Date.now() + UPDATE_NOTIFIER_SLEEP_MS + 1000, - lastSleptUpdateNotifierVersion: '1.0.0', - currentVersion: '1.0.0' - }, - expected: false - }, - { - options: { - lastSleptUpdateNotifier: Date.now() + UPDATE_NOTIFIER_SLEEP_MS + 1000, - lastSleptUpdateNotifierVersion: '1.0.0+6374412', - currentVersion: '1.0.0+6374412' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: Date.now() + UPDATE_NOTIFIER_SLEEP_MS + 1000, - lastSleptUpdateNotifierVersion: '1.0.0+foo', - currentVersion: '1.0.0+foo' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: Date.now() + UPDATE_NOTIFIER_SLEEP_MS + 1000, - lastSleptUpdateNotifierVersion: '0.0.0', - currentVersion: '1.0.0' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: Date.now() + UPDATE_NOTIFIER_SLEEP_MS + 1000, - lastSleptUpdateNotifierVersion: '0.0.0', - currentVersion: '1.0.0+6374412' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: Date.now() + UPDATE_NOTIFIER_SLEEP_MS + 1000, - lastSleptUpdateNotifierVersion: '0.0.0', - currentVersion: '1.0.0+foo' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: Date.now() + UPDATE_NOTIFIER_SLEEP_MS + 1000, - lastSleptUpdateNotifierVersion: '99.9.9', - currentVersion: '1.0.0' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: Date.now() + UPDATE_NOTIFIER_SLEEP_MS + 1000, - lastSleptUpdateNotifierVersion: '99.9.9', - currentVersion: '1.0.0+6374412' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: Date.now() + UPDATE_NOTIFIER_SLEEP_MS + 1000, - lastSleptUpdateNotifierVersion: '99.9.9', - currentVersion: '1.0.0+foo' - }, - expected: true - }, - - // Given the `lastSleptUpdateNotifier` was updated long ago - - { - options: { - lastSleptUpdateNotifier: Date.now() - UPDATE_NOTIFIER_SLEEP_MS - 1000, - lastSleptUpdateNotifierVersion: '1.0.0', - currentVersion: '1.0.0' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: Date.now() - UPDATE_NOTIFIER_SLEEP_MS - 1000, - lastSleptUpdateNotifierVersion: '1.0.0+6374412', - currentVersion: '1.0.0+6374412' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: Date.now() - UPDATE_NOTIFIER_SLEEP_MS - 1000, - lastSleptUpdateNotifierVersion: '1.0.0+foo', - currentVersion: '1.0.0+foo' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: Date.now() - UPDATE_NOTIFIER_SLEEP_MS - 1000, - lastSleptUpdateNotifierVersion: '0.0.0', - currentVersion: '1.0.0' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: Date.now() - UPDATE_NOTIFIER_SLEEP_MS - 1000, - lastSleptUpdateNotifierVersion: '0.0.0', - currentVersion: '1.0.0+6374412' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: Date.now() - UPDATE_NOTIFIER_SLEEP_MS - 1000, - lastSleptUpdateNotifierVersion: '0.0.0', - currentVersion: '1.0.0+foo' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: Date.now() - UPDATE_NOTIFIER_SLEEP_MS - 1000, - lastSleptUpdateNotifierVersion: '99.9.9', - currentVersion: '1.0.0' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: Date.now() - UPDATE_NOTIFIER_SLEEP_MS - 1000, - lastSleptUpdateNotifierVersion: '99.9.9', - currentVersion: '1.0.0+6374412' - }, - expected: true - }, - { - options: { - lastSleptUpdateNotifier: Date.now() - UPDATE_NOTIFIER_SLEEP_MS - 1000, - lastSleptUpdateNotifierVersion: '99.9.9', - currentVersion: '1.0.0+foo' - }, - expected: true - } - - ], (testCase) => { - it(_.join([ - `should return ${testCase.expected} if`, - `lastSleptUpdateNotifier=${testCase.options.lastSleptUpdateNotifier},`, - `lastSleptUpdateNotifierVersion=${testCase.options.lastSleptUpdateNotifierVersion}, and`, - `currentVersion=${testCase.options.currentVersion}` - ], ' '), function () { - m.chai.expect(updateNotifier.shouldCheckForUpdates(testCase.options)).to.equal(testCase.expected) - }) - }) - }) -}) diff --git a/tests/shared/release.spec.js b/tests/shared/release.spec.js deleted file mode 100644 index 5c38fdbf..00000000 --- a/tests/shared/release.spec.js +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2017 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const m = require('mochainon') -const _ = require('lodash') -const release = require('../../lib/shared/release') - -describe('Shared: Release', function () { - describe('.RELEASE_TYPE', function () { - it('should be a plain object', function () { - m.chai.expect(_.isPlainObject(release.RELEASE_TYPE)).to.be.true - }) - - it('should contain keys with different values', function () { - const keys = _.keys(release.RELEASE_TYPE) - const uniqueValues = _.uniq(_.values(release.RELEASE_TYPE)) - m.chai.expect(_.size(keys)).to.equal(_.size(uniqueValues)) - }) - }) - - describe('.getReleaseType()', function () { - it('should return the unknown release type if the version is not valid semver', function () { - const releaseType = release.getReleaseType('foo.bar') - m.chai.expect(releaseType).to.equal(release.RELEASE_TYPE.UNKNOWN) - }) - - describe('given the version has a short git commit hash build number', function () { - it('should return the snapshot release type', function () { - const releaseType = release.getReleaseType('1.0.0+6374412') - m.chai.expect(releaseType).to.equal(release.RELEASE_TYPE.SNAPSHOT) - }) - - it('should return the snapshot release type if the version has a pre release tag', function () { - const releaseType = release.getReleaseType('1.0.0-beta.19+6374412') - m.chai.expect(releaseType).to.equal(release.RELEASE_TYPE.SNAPSHOT) - }) - }) - - describe('given the version has a long git commit hash build number', function () { - it('should return the snapshot release type', function () { - const releaseType = release.getReleaseType('1.0.0+6374412554b034799bfc6e13b4e39c3f5e6386e6') - m.chai.expect(releaseType).to.equal(release.RELEASE_TYPE.SNAPSHOT) - }) - - it('should return the snapshot release type if the version has a pre release tag', function () { - const releaseType = release.getReleaseType('1.0.0-beta.19+6374412554b034799bfc6e13b4e39c3f5e6386e6') - m.chai.expect(releaseType).to.equal(release.RELEASE_TYPE.SNAPSHOT) - }) - }) - - describe('given the version has no build number', function () { - it('should return the production release type', function () { - const releaseType = release.getReleaseType('1.0.0') - m.chai.expect(releaseType).to.equal(release.RELEASE_TYPE.PRODUCTION) - }) - - it('should return the production release type if the version has a pre release tag', function () { - const releaseType = release.getReleaseType('1.0.0-beta.19') - m.chai.expect(releaseType).to.equal(release.RELEASE_TYPE.PRODUCTION) - }) - }) - - describe('given a build number that is not a git commit hash', function () { - it('should return the unknown release type', function () { - const releaseType = release.getReleaseType('1.0.0+foo') - m.chai.expect(releaseType).to.equal(release.RELEASE_TYPE.UNKNOWN) - }) - - it('should return the unknown release type if the version has a pre release tag', function () { - const releaseType = release.getReleaseType('1.0.0-beta.19+foo') - m.chai.expect(releaseType).to.equal(release.RELEASE_TYPE.UNKNOWN) - }) - }) - }) - - describe('.isStableRelease()', function () { - it('should return true if given a production stable version', function () { - m.chai.expect(release.isStableRelease('1.0.0')).to.be.true - }) - - it('should return false if given a production release candidate version', function () { - m.chai.expect(release.isStableRelease('1.0.0-rc.2')).to.be.false - }) - - it('should return false if given a production beta version', function () { - m.chai.expect(release.isStableRelease('1.0.0-beta.1')).to.be.false - }) - - it('should return false if given a snapshot stable version', function () { - m.chai.expect(release.isStableRelease('1.0.0+6374412')).to.be.false - }) - - it('should return false if given a snapshot release candidate version', function () { - m.chai.expect(release.isStableRelease('1.0.0-rc.2+6374412')).to.be.false - }) - - it('should return false if given a snapshot beta version', function () { - m.chai.expect(release.isStableRelease('1.0.0-beta.1+6374412')).to.be.false - }) - }) -}) diff --git a/tests/shared/s3-packages.spec.js b/tests/shared/s3-packages.spec.js deleted file mode 100644 index 1b00e500..00000000 --- a/tests/shared/s3-packages.spec.js +++ /dev/null @@ -1,1199 +0,0 @@ -/* - * Copyright 2017 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const m = require('mochainon') -const Bluebird = require('bluebird') -const request = Bluebird.promisifyAll(require('request')) -const nock = require('nock') -const s3Packages = require('../../lib/shared/s3-packages') -const release = require('../../lib/shared/release') -const errors = require('../../lib/shared/errors') - -describe('Shared: s3Packages', function () { - describe('.getBucketUrlFromReleaseType()', function () { - it('should return the production URL if given a production release type', function () { - const bucketUrl = s3Packages.getBucketUrlFromReleaseType(release.RELEASE_TYPE.PRODUCTION) - m.chai.expect(bucketUrl).to.equal(s3Packages.BUCKET_URL.PRODUCTION) - }) - - it('should return the snapshot URL if given a snapshot release type', function () { - const bucketUrl = s3Packages.getBucketUrlFromReleaseType(release.RELEASE_TYPE.SNAPSHOT) - m.chai.expect(bucketUrl).to.equal(s3Packages.BUCKET_URL.SNAPSHOT) - }) - - it('should return null if given an unknown release type', function () { - const bucketUrl = s3Packages.getBucketUrlFromReleaseType(release.RELEASE_TYPE.UNKNOWN) - m.chai.expect(bucketUrl).to.be.null - }) - }) - - describe('.getRemoteVersions()', function () { - beforeEach(function () { - s3Packages.getRemoteVersions.cache.clear() - }) - - it('should be rejected if url is null', function (done) { - s3Packages.getRemoteVersions(null).catch((error) => { - m.chai.expect(error).to.be.an.instanceof(Error) - m.chai.expect(error.message).to.equal('Invalid bucket url: null') - done() - }) - }) - - describe('given an empty bucket', function () { - beforeEach(function () { - nock(s3Packages.BUCKET_URL.PRODUCTION).get('/').reply(200, ` - - resin-production-downloads - - - 1000 - false - - `) - }) - - afterEach(function () { - nock.cleanAll() - }) - - it('should resolve an empty array', function (done) { - s3Packages.getRemoteVersions(s3Packages.BUCKET_URL.PRODUCTION).then((versions) => { - m.chai.expect(versions).to.deep.equal([]) - done() - }).catch(done) - }) - }) - - describe('given many versions', function () { - beforeEach(function () { - nock(s3Packages.BUCKET_URL.PRODUCTION).get('/').reply(200, ` - - resin-production-downloads - - - 1000 - false - - etcher/1.0.0-beta.17/Etcher-1.0.0-beta.17-darwin-x64.dmg - 2016-11-28T16:12:28.000Z - "5818b791238e7a03c2128149cbcabfd6" - 73532901 - STANDARD - - - etcher/1.0.0-beta.17/Etcher-1.0.0-beta.17-darwin-x64.zip - 2016-11-28T16:52:26.000Z - "e9b4e7350e352298de293bb44aa72e9c" - 154896510 - STANDARD - - - etcher/1.0.0-beta.17/Etcher-1.0.0-beta.17-linux-x64.zip - 2016-11-28T18:27:10.000Z - "40e2b620d2aecb87e44c8675f2028d03" - 71186664 - STANDARD - - - etcher/1.0.0-beta.17/Etcher-1.0.0-beta.17-linux-x86.zip - 2016-11-28T17:56:56.000Z - "e585bd96708d79845015cc57d86a3f60" - 72576097 - STANDARD - - - etcher/1.0.0-beta.17/Etcher-1.0.0-beta.17-win32-x64.exe - 2016-11-28T20:01:43.000Z - "f6134fedb835af59db063810fb511ef0" - 84717856 - STANDARD - - - etcher/1.0.0-beta.17/Etcher-1.0.0-beta.17-win32-x64.zip - 2016-11-28T20:21:55.000Z - "8c6db54d2210355563519c67c1618664" - 82056508 - STANDARD - - - etcher/1.0.0-beta.17/Etcher-1.0.0-beta.17-win32-x86.exe - 2016-11-28T20:39:43.000Z - "fdcc21ec9a7312b781c03b8d469e843d" - 74151760 - STANDARD - - - etcher/1.0.0-beta.17/Etcher-1.0.0-beta.17-win32-x86.zip - 2016-11-28T20:57:31.000Z - "992c2c021575d5909dbe77a759b67464" - 71846504 - STANDARD - - - etcher/1.0.0-beta.18/Etcher-1.0.0-beta.18-darwin-x64.dmg - 2017-01-17T00:58:49.000Z - "81a1b5a330a230ca6d89db97b3399420" - 58442097 - STANDARD - - - etcher/1.0.0-beta.18/Etcher-1.0.0-beta.18-darwin-x64.zip - 2017-01-17T01:18:56.000Z - "81b438a9f7b2d4c871cfbd5aedd96975" - 139834277 - STANDARD - - - etcher/1.0.0-beta.18/Etcher-1.0.0-beta.18-linux-x64.zip - 2017-01-17T02:01:01.000Z - "35660b65233082a10c00828ea1e50c38" - 55960697 - STANDARD - - - etcher/1.0.0-beta.18/Etcher-1.0.0-beta.18-linux-x86.zip - 2017-01-17T02:18:20.000Z - "1bafcc0d5d2c8d43bd2ce8948bb51a8b" - 57331229 - STANDARD - - - etcher/1.0.0-beta.18/Etcher-1.0.0-beta.18-win32-x64.exe - 2017-01-17T05:48:02.000Z - "b0a6154ec79d17618632ac62eb30b44e" - 84802632 - STANDARD - - - etcher/1.0.0-beta.18/Etcher-1.0.0-beta.18-win32-x64.zip - 2017-01-17T06:14:46.000Z - "f25c5dfa8378e608c25fafbe65e90162" - 82235087 - STANDARD - - - etcher/1.0.0-beta.18/Etcher-1.0.0-beta.18-win32-x86.exe - 2017-01-17T06:42:58.000Z - "183b6eb648b0a78a1e99c9182f90194e" - 74264232 - STANDARD - - - etcher/1.0.0-beta.18/Etcher-1.0.0-beta.18-win32-x86.zip - 2017-01-17T07:05:55.000Z - "d8d860013f038bed3f52534053b08382" - 72042525 - STANDARD - - - `) - }) - - afterEach(function () { - nock.cleanAll() - }) - - it('should resolve all the available versions', function (done) { - s3Packages.getRemoteVersions(s3Packages.BUCKET_URL.PRODUCTION).then((versions) => { - m.chai.expect(versions).to.deep.equal([ - '1.0.0-beta.17', - '1.0.0-beta.18' - ]) - done() - }).catch(done) - }) - }) - - describe('given a version is being uploaded', function () { - beforeEach(function () { - nock(s3Packages.BUCKET_URL.PRODUCTION).get('/').reply(200, ` - - resin-production-downloads - - - 1000 - false - - etcher/1.0.0-beta.17/Etcher-1.0.0-beta.17-darwin-x64.dmg - 2016-11-28T16:12:28.000Z - "5818b791238e7a03c2128149cbcabfd6" - 73532901 - STANDARD - - - etcher/1.0.0-beta.17/Etcher-1.0.0-beta.17-darwin-x64.zip - 2016-11-28T16:52:26.000Z - "e9b4e7350e352298de293bb44aa72e9c" - 154896510 - STANDARD - - - etcher/1.0.0-beta.17/Etcher-1.0.0-beta.17-linux-x64.zip - 2016-11-28T18:27:10.000Z - "40e2b620d2aecb87e44c8675f2028d03" - 71186664 - STANDARD - - - etcher/1.0.0-beta.17/Etcher-1.0.0-beta.17-linux-x86.zip - 2016-11-28T17:56:56.000Z - "e585bd96708d79845015cc57d86a3f60" - 72576097 - STANDARD - - - etcher/1.0.0-beta.17/Etcher-1.0.0-beta.17-win32-x64.exe - 2016-11-28T20:01:43.000Z - "f6134fedb835af59db063810fb511ef0" - 84717856 - STANDARD - - - etcher/1.0.0-beta.17/Etcher-1.0.0-beta.17-win32-x64.zip - 2016-11-28T20:21:55.000Z - "8c6db54d2210355563519c67c1618664" - 82056508 - STANDARD - - - etcher/1.0.0-beta.17/Etcher-1.0.0-beta.17-win32-x86.exe - 2016-11-28T20:39:43.000Z - "fdcc21ec9a7312b781c03b8d469e843d" - 74151760 - STANDARD - - - etcher/1.0.0-beta.17/Etcher-1.0.0-beta.17-win32-x86.zip - 2016-11-28T20:57:31.000Z - "992c2c021575d5909dbe77a759b67464" - 71846504 - STANDARD - - - etcher/1.0.0-beta.18/Etcher-1.0.0-beta.18-darwin-x64.dmg - 2017-01-17T00:58:49.000Z - "81a1b5a330a230ca6d89db97b3399420" - 58442097 - STANDARD - - - etcher/1.0.0-beta.18/Etcher-1.0.0-beta.18-darwin-x64.zip - 2017-01-17T01:18:56.000Z - "81b438a9f7b2d4c871cfbd5aedd96975" - 139834277 - STANDARD - - - etcher/1.0.0-beta.18/Etcher-1.0.0-beta.18-linux-x64.zip - 2017-01-17T02:01:01.000Z - "35660b65233082a10c00828ea1e50c38" - 55960697 - STANDARD - - - etcher/1.0.0-beta.18/Etcher-1.0.0-beta.18-linux-x86.zip - 2017-01-17T02:18:20.000Z - "1bafcc0d5d2c8d43bd2ce8948bb51a8b" - 57331229 - STANDARD - - - etcher/1.0.0-beta.18/Etcher-1.0.0-beta.18-win32-x64.exe - 2017-01-17T05:48:02.000Z - "b0a6154ec79d17618632ac62eb30b44e" - 84802632 - STANDARD - - - etcher/1.0.0-beta.18/Etcher-1.0.0-beta.18-win32-x64.zip - 2017-01-17T06:14:46.000Z - "f25c5dfa8378e608c25fafbe65e90162" - 82235087 - STANDARD - - - etcher/1.0.0-beta.18/Etcher-1.0.0-beta.18-win32-x86.exe - 2017-01-17T06:42:58.000Z - "183b6eb648b0a78a1e99c9182f90194e" - 74264232 - STANDARD - - - `) - }) - - afterEach(function () { - nock.cleanAll() - }) - - it('should resolve all the entirely available versions', function (done) { - s3Packages.getRemoteVersions(s3Packages.BUCKET_URL.PRODUCTION).then((versions) => { - m.chai.expect(versions).to.deep.equal([ - '1.0.0-beta.17' - ]) - done() - }).catch(done) - }) - }) - - describe('given other programs in the bucket', function () { - beforeEach(function () { - nock(s3Packages.BUCKET_URL.PRODUCTION).get('/').reply(200, ` - - resin-production-downloads - - - 1000 - false - - etcher/1.0.0-beta.17/Etcher-1.0.0-beta.17-darwin-x64.dmg - 2016-11-28T16:12:28.000Z - "5818b791238e7a03c2128149cbcabfd6" - 73532901 - STANDARD - - - etcher/1.0.0-beta.17/Etcher-1.0.0-beta.17-darwin-x64.zip - 2016-11-28T16:52:26.000Z - "e9b4e7350e352298de293bb44aa72e9c" - 154896510 - STANDARD - - - etcher/1.0.0-beta.17/Etcher-1.0.0-beta.17-linux-x64.zip - 2016-11-28T18:27:10.000Z - "40e2b620d2aecb87e44c8675f2028d03" - 71186664 - STANDARD - - - etcher/1.0.0-beta.17/Etcher-1.0.0-beta.17-linux-x86.zip - 2016-11-28T17:56:56.000Z - "e585bd96708d79845015cc57d86a3f60" - 72576097 - STANDARD - - - etcher/1.0.0-beta.17/Etcher-1.0.0-beta.17-win32-x64.exe - 2016-11-28T20:01:43.000Z - "f6134fedb835af59db063810fb511ef0" - 84717856 - STANDARD - - - etcher/1.0.0-beta.17/Etcher-1.0.0-beta.17-win32-x64.zip - 2016-11-28T20:21:55.000Z - "8c6db54d2210355563519c67c1618664" - 82056508 - STANDARD - - - etcher/1.0.0-beta.17/Etcher-1.0.0-beta.17-win32-x86.exe - 2016-11-28T20:39:43.000Z - "fdcc21ec9a7312b781c03b8d469e843d" - 74151760 - STANDARD - - - etcher/1.0.0-beta.17/Etcher-1.0.0-beta.17-win32-x86.zip - 2016-11-28T20:57:31.000Z - "992c2c021575d5909dbe77a759b67464" - 71846504 - STANDARD - - - notEtcher/1.0.0-beta.18/Etcher-1.0.0-beta.18-darwin-x64.dmg - 2017-01-17T00:58:49.000Z - "81a1b5a330a230ca6d89db97b3399420" - 58442097 - STANDARD - - - notEtcher/1.0.0-beta.18/Etcher-1.0.0-beta.18-darwin-x64.zip - 2017-01-17T01:18:56.000Z - "81b438a9f7b2d4c871cfbd5aedd96975" - 139834277 - STANDARD - - - notEtcher/1.0.0-beta.18/Etcher-1.0.0-beta.18-linux-x64.zip - 2017-01-17T02:01:01.000Z - "35660b65233082a10c00828ea1e50c38" - 55960697 - STANDARD - - - notEtcher/1.0.0-beta.18/Etcher-1.0.0-beta.18-linux-x86.zip - 2017-01-17T02:18:20.000Z - "1bafcc0d5d2c8d43bd2ce8948bb51a8b" - 57331229 - STANDARD - - - notEtcher/1.0.0-beta.18/Etcher-1.0.0-beta.18-win32-x64.exe - 2017-01-17T05:48:02.000Z - "b0a6154ec79d17618632ac62eb30b44e" - 84802632 - STANDARD - - - notEtcher/1.0.0-beta.18/Etcher-1.0.0-beta.18-win32-x64.zip - 2017-01-17T06:14:46.000Z - "f25c5dfa8378e608c25fafbe65e90162" - 82235087 - STANDARD - - - notEtcher/1.0.0-beta.18/Etcher-1.0.0-beta.18-win32-x86.exe - 2017-01-17T06:42:58.000Z - "183b6eb648b0a78a1e99c9182f90194e" - 74264232 - STANDARD - - - notEtcher/1.0.0-beta.18/Etcher-1.0.0-beta.18-win32-x86.zip - 2017-01-17T07:05:55.000Z - "d8d860013f038bed3f52534053b08382" - 72042525 - STANDARD - - - `) - }) - - afterEach(function () { - nock.cleanAll() - }) - - it('should not consider the other packages', function (done) { - s3Packages.getRemoteVersions(s3Packages.BUCKET_URL.PRODUCTION).then((versions) => { - m.chai.expect(versions).to.deep.equal([ - '1.0.0-beta.17' - ]) - done() - }).catch(done) - }) - }) - - describe('given only other programs in the bucket', function () { - beforeEach(function () { - nock(s3Packages.BUCKET_URL.PRODUCTION).get('/').reply(200, ` - - resin-production-downloads - - - 1000 - false - - notEtcher/1.0.0-beta.18/Etcher-1.0.0-beta.18-darwin-x64.zip - 2017-01-17T01:18:56.000Z - "81b438a9f7b2d4c871cfbd5aedd96975" - 139834277 - STANDARD - - - notEtcher/1.0.0-beta.18/Etcher-1.0.0-beta.18-linux-x64.zip - 2017-01-17T02:01:01.000Z - "35660b65233082a10c00828ea1e50c38" - 55960697 - STANDARD - - - notEtcher/1.0.0-beta.18/Etcher-1.0.0-beta.18-linux-x86.zip - 2017-01-17T02:18:20.000Z - "1bafcc0d5d2c8d43bd2ce8948bb51a8b" - 57331229 - STANDARD - - - notEtcher/1.0.0-beta.18/Etcher-1.0.0-beta.18-win32-x64.exe - 2017-01-17T05:48:02.000Z - "b0a6154ec79d17618632ac62eb30b44e" - 84802632 - STANDARD - - - notEtcher/1.0.0-beta.18/Etcher-1.0.0-beta.18-win32-x64.zip - 2017-01-17T06:14:46.000Z - "f25c5dfa8378e608c25fafbe65e90162" - 82235087 - STANDARD - - - notEtcher/1.0.0-beta.18/Etcher-1.0.0-beta.18-win32-x86.exe - 2017-01-17T06:42:58.000Z - "183b6eb648b0a78a1e99c9182f90194e" - 74264232 - STANDARD - - - notEtcher/1.0.0-beta.18/Etcher-1.0.0-beta.18-win32-x86.zip - 2017-01-17T07:05:55.000Z - "d8d860013f038bed3f52534053b08382" - 72042525 - STANDARD - - - `) - }) - - afterEach(function () { - nock.cleanAll() - }) - - it('should resolve an empty array', function (done) { - s3Packages.getRemoteVersions(s3Packages.BUCKET_URL.PRODUCTION).then((versions) => { - m.chai.expect(versions).to.deep.equal([]) - done() - }).catch(done) - }) - }) - - describe('given an unsuccessful request', function () { - beforeEach(function () { - nock(s3Packages.BUCKET_URL.PRODUCTION).get('/').reply(500) - }) - - afterEach(function () { - nock.cleanAll() - }) - - it('should be rejected with a non-user error', function (done) { - s3Packages.getRemoteVersions(s3Packages.BUCKET_URL.PRODUCTION).catch((error) => { - m.chai.expect(error).to.be.an.instanceof(Error) - m.chai.expect(errors.isUserError(error)).to.be.false - done() - }) - }) - }) - - describe('given ENOTFOUND', function () { - beforeEach(function () { - const error = new Error('ENOTFOUND') - error.code = 'ENOTFOUND' - - this.requestGetAsyncStub = m.sinon.stub(request, 'getAsync') - this.requestGetAsyncStub.returns(Bluebird.reject(error)) - }) - - afterEach(function () { - this.requestGetAsyncStub.restore() - }) - - it('should be rejected with a user error with code UPDATE_USER_ERROR', function (done) { - s3Packages.getRemoteVersions(s3Packages.BUCKET_URL.PRODUCTION).catch((error) => { - m.chai.expect(errors.isUserError(error)).to.be.true - m.chai.expect(error.code).to.equal('UPDATE_USER_ERROR') - done() - }) - }) - }) - - describe('given ETIMEDOUT', function () { - beforeEach(function () { - const error = new Error('ETIMEDOUT') - error.code = 'ETIMEDOUT' - - this.requestGetAsyncStub = m.sinon.stub(request, 'getAsync') - this.requestGetAsyncStub.returns(Bluebird.reject(error)) - }) - - afterEach(function () { - this.requestGetAsyncStub.restore() - }) - - it('should be rejected with a user error with code UPDATE_USER_ERROR', function (done) { - s3Packages.getRemoteVersions(s3Packages.BUCKET_URL.PRODUCTION).catch((error) => { - m.chai.expect(errors.isUserError(error)).to.be.true - m.chai.expect(error.code).to.equal('UPDATE_USER_ERROR') - done() - }) - }) - }) - - describe('given EHOSTDOWN', function () { - beforeEach(function () { - const error = new Error('EHOSTDOWN') - error.code = 'EHOSTDOWN' - - this.requestGetAsyncStub = m.sinon.stub(request, 'getAsync') - this.requestGetAsyncStub.returns(Bluebird.reject(error)) - }) - - afterEach(function () { - this.requestGetAsyncStub.restore() - }) - - it('should be rejected with a user error with code UPDATE_USER_ERROR', function (done) { - s3Packages.getRemoteVersions(s3Packages.BUCKET_URL.PRODUCTION).catch((error) => { - m.chai.expect(errors.isUserError(error)).to.be.true - m.chai.expect(error.code).to.equal('UPDATE_USER_ERROR') - done() - }) - }) - }) - - describe('given EAI_AGAIN', function () { - beforeEach(function () { - const error = new Error('EAI_AGAIN') - error.code = 'EAI_AGAIN' - - this.requestGetAsyncStub = m.sinon.stub(request, 'getAsync') - this.requestGetAsyncStub.returns(Bluebird.reject(error)) - }) - - afterEach(function () { - this.requestGetAsyncStub.restore() - }) - - it('should be rejected with a user error with code UPDATE_USER_ERROR', function (done) { - s3Packages.getRemoteVersions(s3Packages.BUCKET_URL.PRODUCTION).catch((error) => { - m.chai.expect(errors.isUserError(error)).to.be.true - m.chai.expect(error.code).to.equal('UPDATE_USER_ERROR') - done() - }) - }) - }) - - describe('given ECONNRESET', function () { - beforeEach(function () { - const error = new Error('ECONNRESET') - error.code = 'ECONNRESET' - - this.requestGetAsyncStub = m.sinon.stub(request, 'getAsync') - this.requestGetAsyncStub.returns(Bluebird.reject(error)) - }) - - afterEach(function () { - this.requestGetAsyncStub.restore() - }) - - it('should be rejected with a user error with code UPDATE_USER_ERROR', function (done) { - s3Packages.getRemoteVersions(s3Packages.BUCKET_URL.PRODUCTION).catch((error) => { - m.chai.expect(errors.isUserError(error)).to.be.true - m.chai.expect(error.code).to.equal('UPDATE_USER_ERROR') - done() - }) - }) - }) - - describe('given ECONNREFUSED', function () { - beforeEach(function () { - const error = new Error('ECONNREFUSED') - error.code = 'ECONNREFUSED' - - this.requestGetAsyncStub = m.sinon.stub(request, 'getAsync') - this.requestGetAsyncStub.returns(Bluebird.reject(error)) - }) - - afterEach(function () { - this.requestGetAsyncStub.restore() - }) - - it('should be rejected with a user error with code UPDATE_USER_ERROR', function (done) { - s3Packages.getRemoteVersions(s3Packages.BUCKET_URL.PRODUCTION).catch((error) => { - m.chai.expect(errors.isUserError(error)).to.be.true - m.chai.expect(error.code).to.equal('UPDATE_USER_ERROR') - done() - }) - }) - }) - - describe('given EACCES', function () { - beforeEach(function () { - const error = new Error('EACCES') - error.code = 'EACCES' - - this.requestGetAsyncStub = m.sinon.stub(request, 'getAsync') - this.requestGetAsyncStub.returns(Bluebird.reject(error)) - }) - - afterEach(function () { - this.requestGetAsyncStub.restore() - }) - - it('should be rejected with a user error with code UPDATE_USER_ERROR', function (done) { - s3Packages.getRemoteVersions(s3Packages.BUCKET_URL.PRODUCTION).catch((error) => { - m.chai.expect(errors.isUserError(error)).to.be.true - m.chai.expect(error.code).to.equal('UPDATE_USER_ERROR') - done() - }) - }) - }) - - describe('given UNABLE_TO_GET_ISSUER_CERT_LOCALLY', function () { - beforeEach(function () { - const error = new Error('UNABLE_TO_GET_ISSUER_CERT_LOCALLY') - error.code = 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY' - - this.requestGetAsyncStub = m.sinon.stub(request, 'getAsync') - this.requestGetAsyncStub.returns(Bluebird.reject(error)) - }) - - afterEach(function () { - this.requestGetAsyncStub.restore() - }) - - it('should be rejected with a user error with code UPDATE_USER_ERROR', function (done) { - s3Packages.getRemoteVersions(s3Packages.BUCKET_URL.PRODUCTION).catch((error) => { - m.chai.expect(errors.isUserError(error)).to.be.true - m.chai.expect(error.code).to.equal('UPDATE_USER_ERROR') - done() - }) - }) - }) - - describe('given UNABLE_TO_VERIFY_LEAF_SIGNATURE', function () { - beforeEach(function () { - const error = new Error('UNABLE_TO_VERIFY_LEAF_SIGNATURE') - error.code = 'UNABLE_TO_VERIFY_LEAF_SIGNATURE' - - this.requestGetAsyncStub = m.sinon.stub(request, 'getAsync') - this.requestGetAsyncStub.returns(Bluebird.reject(error)) - }) - - afterEach(function () { - this.requestGetAsyncStub.restore() - }) - - it('should be rejected with a user error with code UPDATE_USER_ERROR', function (done) { - s3Packages.getRemoteVersions(s3Packages.BUCKET_URL.PRODUCTION).catch((error) => { - m.chai.expect(errors.isUserError(error)).to.be.true - m.chai.expect(error.code).to.equal('UPDATE_USER_ERROR') - done() - }) - }) - }) - }) - - describe('.getLatestVersion()', function () { - describe('given a valid production ETCHER_FAKE_S3_LATEST_VERSION environment variable', function () { - beforeEach(function () { - process.env.ETCHER_FAKE_S3_LATEST_VERSION = '9.9.9' - }) - - afterEach(function () { - Reflect.deleteProperty(process.env, 'ETCHER_FAKE_S3_LATEST_VERSION') - }) - - describe('given a production release type', function () { - it('should resolve the variable', function (done) { - s3Packages.getLatestVersion(release.RELEASE_TYPE.PRODUCTION).then((latestVersion) => { - m.chai.expect(latestVersion).to.equal('9.9.9') - done() - }).catch(done) - }) - }) - - describe('given a snapshot release type', function () { - it('should resolve undefined', function (done) { - s3Packages.getLatestVersion(release.RELEASE_TYPE.SNAPSHOT).then((latestVersion) => { - m.chai.expect(latestVersion).to.be.undefined - done() - }).catch(done) - }) - }) - }) - - describe('given a valid snapshot ETCHER_FAKE_S3_LATEST_VERSION environment variable', function () { - beforeEach(function () { - process.env.ETCHER_FAKE_S3_LATEST_VERSION = '9.9.9+7b47334' - }) - - afterEach(function () { - Reflect.deleteProperty(process.env, 'ETCHER_FAKE_S3_LATEST_VERSION') - }) - - describe('given a snapshot release type', function () { - it('should resolve the variable', function (done) { - s3Packages.getLatestVersion(release.RELEASE_TYPE.SNAPSHOT).then((latestVersion) => { - m.chai.expect(latestVersion).to.equal('9.9.9+7b47334') - done() - }).catch(done) - }) - }) - - describe('given a production release type', function () { - it('should resolve undefined', function (done) { - s3Packages.getLatestVersion(release.RELEASE_TYPE.PRODUCTION).then((latestVersion) => { - m.chai.expect(latestVersion).to.be.undefined - done() - }).catch(done) - }) - }) - }) - - describe('given an invalid ETCHER_FAKE_S3_LATEST_VERSION environment variable', function () { - beforeEach(function () { - process.env.ETCHER_FAKE_S3_LATEST_VERSION = 'foo' - }) - - afterEach(function () { - Reflect.deleteProperty(process.env, 'ETCHER_FAKE_S3_LATEST_VERSION') - }) - - it('should resolve undefined', function (done) { - s3Packages.getLatestVersion(release.RELEASE_TYPE.PRODUCTION).then((latestVersion) => { - m.chai.expect(latestVersion).to.be.undefined - done() - }).catch(done) - }) - }) - - describe('given an invalid release type', function () { - it('should be rejected with an error', function (done) { - s3Packages.getLatestVersion('foobar').catch((error) => { - m.chai.expect(error).to.be.an.instanceof(Error) - m.chai.expect(error.message).to.equal('No bucket URL found for release type: foobar') - done() - }) - }) - }) - - describe('given no remote versions', function () { - beforeEach(function () { - this.getRemoteVersionsStub = m.sinon.stub(s3Packages, 'getRemoteVersions') - this.getRemoteVersionsStub.returns(Bluebird.resolve([])) - }) - - afterEach(function () { - this.getRemoteVersionsStub.restore() - }) - - it('should resolve undefined', function (done) { - s3Packages.getLatestVersion(release.RELEASE_TYPE.PRODUCTION).then((latestVersion) => { - m.chai.expect(latestVersion).to.be.undefined - done() - }).catch(done) - }) - }) - - describe('given a single version', function () { - beforeEach(function () { - this.getRemoteVersionsStub = m.sinon.stub(s3Packages, 'getRemoteVersions') - this.getRemoteVersionsStub.returns(Bluebird.resolve([ '0.5.0' ])) - }) - - afterEach(function () { - this.getRemoteVersionsStub.restore() - }) - - it('should resolve the version', function (done) { - s3Packages.getLatestVersion(release.RELEASE_TYPE.PRODUCTION).then((latestVersion) => { - m.chai.expect(latestVersion).to.equal('0.5.0') - done() - }).catch(done) - }) - }) - - describe('given multiple versions', function () { - beforeEach(function () { - this.getRemoteVersionsStub = m.sinon.stub(s3Packages, 'getRemoteVersions') - this.getRemoteVersionsStub.returns(Bluebird.resolve([ - '2.1.0', - '1.0.0', - '0.5.0', - '0.4.0' - ])) - }) - - afterEach(function () { - this.getRemoteVersionsStub.restore() - }) - - it('should resolve the latest version', function (done) { - s3Packages.getLatestVersion(release.RELEASE_TYPE.PRODUCTION).then((latestVersion) => { - m.chai.expect(latestVersion).to.equal('2.1.0') - done() - }).catch(done) - }) - }) - - describe('given a greater production version in a snapshot bucket', function () { - beforeEach(function () { - this.getRemoteVersionsStub = m.sinon.stub(s3Packages, 'getRemoteVersions') - this.getRemoteVersionsStub.returns(Bluebird.resolve([ - '1.0.0+abb6139', - '2.0.0' - ])) - }) - - afterEach(function () { - this.getRemoteVersionsStub.restore() - }) - - it('should ignore production versions if includeUnstableChannel is true', function (done) { - s3Packages.getLatestVersion(release.RELEASE_TYPE.SNAPSHOT, { - includeUnstableChannel: true - }).then((latestVersion) => { - m.chai.expect(latestVersion).to.equal('1.0.0+abb6139') - done() - }).catch(done) - }) - - it('should return undefined if includeUnstableChannel is false', function (done) { - s3Packages.getLatestVersion(release.RELEASE_TYPE.SNAPSHOT, { - includeUnstableChannel: false - }).then((latestVersion) => { - m.chai.expect(latestVersion).to.be.undefined - done() - }).catch(done) - }) - }) - - describe('given a greater snapshot version in a production bucket', function () { - beforeEach(function () { - this.getRemoteVersionsStub = m.sinon.stub(s3Packages, 'getRemoteVersions') - this.getRemoteVersionsStub.returns(Bluebird.resolve([ - '1.0.0', - '2.0.0+abb6139' - ])) - }) - - afterEach(function () { - this.getRemoteVersionsStub.restore() - }) - - it('should ignore snapshot versions', function (done) { - s3Packages.getLatestVersion(release.RELEASE_TYPE.PRODUCTION).then((latestVersion) => { - m.chai.expect(latestVersion).to.equal('1.0.0') - done() - }).catch(done) - }) - }) - - describe('given production v1, v2, and v3 remote versions', function () { - beforeEach(function () { - this.getRemoteVersionsStub = m.sinon.stub(s3Packages, 'getRemoteVersions') - this.getRemoteVersionsStub.returns(Bluebird.resolve([ - '3.0.1', - '3.0.0', - '2.1.1', - '2.1.0', - '2.0.0', - '1.2.0', - '1.1.0', - '1.0.2', - '1.0.1', - '1.0.0' - ])) - }) - - afterEach(function () { - this.getRemoteVersionsStub.restore() - }) - - it('should be able to resolve the latest v1 version with a semver range', function (done) { - s3Packages.getLatestVersion(release.RELEASE_TYPE.PRODUCTION, { - range: '<2.0.0' - }).then((latestVersion) => { - m.chai.expect(latestVersion).to.equal('1.2.0') - done() - }).catch(done) - }) - - it('should be able to resolve the latest v2 version with a semver range', function (done) { - s3Packages.getLatestVersion(release.RELEASE_TYPE.PRODUCTION, { - range: '>=2.0.0 <3.0.0' - }).then((latestVersion) => { - m.chai.expect(latestVersion).to.equal('2.1.1') - done() - }).catch(done) - }) - - it('should be able to resolve the latest v3 version with a semver range', function (done) { - s3Packages.getLatestVersion(release.RELEASE_TYPE.PRODUCTION, { - range: '>=3.0.0' - }).then((latestVersion) => { - m.chai.expect(latestVersion).to.equal('3.0.1') - done() - }).catch(done) - }) - - it('should resolve the latest version if includeUnstableChannel is true', function (done) { - s3Packages.getLatestVersion(release.RELEASE_TYPE.PRODUCTION, { - includeUnstableChannel: true - }).then((latestVersion) => { - m.chai.expect(latestVersion).to.equal('3.0.1') - done() - }).catch(done) - }) - - it('should resolve the latest version if includeUnstableChannel is false', function (done) { - s3Packages.getLatestVersion(release.RELEASE_TYPE.PRODUCTION, { - includeUnstableChannel: false - }).then((latestVersion) => { - m.chai.expect(latestVersion).to.equal('3.0.1') - done() - }).catch(done) - }) - }) - - describe('given unstable and stable versions where the last version is stable', function () { - beforeEach(function () { - this.getRemoteVersionsStub = m.sinon.stub(s3Packages, 'getRemoteVersions') - this.getRemoteVersionsStub.returns(Bluebird.resolve([ - '3.0.1', - '3.0.0-beta.2', - '3.0.0-beta.1', - '2.1.1', - '2.1.0-beta.15', - '1.0.0' - ])) - }) - - afterEach(function () { - this.getRemoteVersionsStub.restore() - }) - - it('should resolve the latest stable version if includeUnstableChannel is false', function (done) { - s3Packages.getLatestVersion(release.RELEASE_TYPE.PRODUCTION, { - includeUnstableChannel: false - }).then((latestVersion) => { - m.chai.expect(latestVersion).to.equal('3.0.1') - done() - }).catch(done) - }) - - it('should resolve the latest stable version if includeUnstableChannel is true', function (done) { - s3Packages.getLatestVersion(release.RELEASE_TYPE.PRODUCTION, { - includeUnstableChannel: true - }).then((latestVersion) => { - m.chai.expect(latestVersion).to.equal('3.0.1') - done() - }).catch(done) - }) - }) - - describe('given unstable and stable versions where the last version is unstable', function () { - beforeEach(function () { - this.getRemoteVersionsStub = m.sinon.stub(s3Packages, 'getRemoteVersions') - this.getRemoteVersionsStub.returns(Bluebird.resolve([ - '3.0.2-beta.1', - '3.0.1', - '3.0.0-beta.2', - '3.0.0-beta.1', - '2.1.1', - '2.1.0-beta.15', - '1.0.0' - ])) - }) - - afterEach(function () { - this.getRemoteVersionsStub.restore() - }) - - it('should resolve the latest stable version if includeUnstableChannel is false', function (done) { - s3Packages.getLatestVersion(release.RELEASE_TYPE.PRODUCTION, { - includeUnstableChannel: false - }).then((latestVersion) => { - m.chai.expect(latestVersion).to.equal('3.0.1') - done() - }).catch(done) - }) - - it('should resolve the latest unstable version if includeUnstableChannel is true', function (done) { - s3Packages.getLatestVersion(release.RELEASE_TYPE.PRODUCTION, { - includeUnstableChannel: true - }).then((latestVersion) => { - m.chai.expect(latestVersion).to.equal('3.0.2-beta.1') - done() - }).catch(done) - }) - }) - - describe('given pre-release production remote versions', function () { - beforeEach(function () { - this.getRemoteVersionsStub = m.sinon.stub(s3Packages, 'getRemoteVersions') - this.getRemoteVersionsStub.returns(Bluebird.resolve([ - '2.0.0-beta.3', - '2.0.0-beta.2', - '2.0.0-beta.1', - '2.0.0-beta.0', - '1.0.0-beta.19', - '1.0.0-beta.18', - '1.0.0-beta.17', - '1.0.0-beta.16' - ])) - }) - - afterEach(function () { - this.getRemoteVersionsStub.restore() - }) - - it('should be able to resolve the latest v2 pre-release version with a non pre-release semver range', function (done) { - s3Packages.getLatestVersion(release.RELEASE_TYPE.PRODUCTION, { - range: '>=2.0.0', - includeUnstableChannel: true - }).then((latestVersion) => { - m.chai.expect(latestVersion).to.equal('2.0.0-beta.3') - done() - }).catch(done) - }) - - it('should resolve undefined if includeUnstableChannel is false', function (done) { - s3Packages.getLatestVersion(release.RELEASE_TYPE.PRODUCTION, { - includeUnstableChannel: false - }).then((latestVersion) => { - m.chai.expect(latestVersion).to.be.undefined - done() - }).catch(done) - }) - }) - - describe('given pre-release snapshot remote versions', function () { - beforeEach(function () { - this.getRemoteVersionsStub = m.sinon.stub(s3Packages, 'getRemoteVersions') - this.getRemoteVersionsStub.returns(Bluebird.resolve([ - '2.0.0-beta.3+5370ef2', - '2.0.0-beta.2+ff495a4', - '2.0.0-beta.1+07b6dd2', - '2.0.0-beta.0+4cd8776', - '1.0.0-beta.19+7b47334', - '1.0.0-beta.18+7fe503c', - '1.0.0-beta.17+76aa05f', - '1.0.0-beta.16+802d9ab' - ])) - }) - - afterEach(function () { - this.getRemoteVersionsStub.restore() - }) - - it('should be able to resolve the latest v2 pre-release version with a non pre-release semver range', function (done) { - s3Packages.getLatestVersion(release.RELEASE_TYPE.SNAPSHOT, { - range: '>=2.0.0', - includeUnstableChannel: true - }).then((latestVersion) => { - m.chai.expect(latestVersion).to.equal('2.0.0-beta.3+5370ef2') - done() - }).catch(done) - }) - - it('should resolve undefined if includeUnstableChannel is false', function (done) { - s3Packages.getLatestVersion(release.RELEASE_TYPE.SNAPSHOT, { - includeUnstableChannel: false - }).then((latestVersion) => { - m.chai.expect(latestVersion).to.be.undefined - done() - }).catch(done) - }) - }) - }) -})