diff --git a/lib/index.js b/lib/index.js index e5019407..949c8d0e 100644 --- a/lib/index.js +++ b/lib/index.js @@ -229,7 +229,7 @@ module.exports = function nuts(opts) { // Change filename to use download proxy .map(function(entry) { - entry.filename = getBaseDownloadUrl(req) + entry.version+'/'+entry.filename; + entry.filename = getBaseDownloadUrl(req) + '/' + entry.version+'/'+entry.filename; return entry; }) diff --git a/lib/versions.js b/lib/versions.js index c6707735..3ccce76d 100644 --- a/lib/versions.js +++ b/lib/versions.js @@ -4,6 +4,8 @@ var semver = require('semver'); var platforms = require('./platforms'); +var channelRe = /^[a-z]*/ + // Creates filter function that checks for updates function newVersionFilter(opts) { return function(version) { @@ -41,6 +43,8 @@ function supportsPlatform(requestedPlatform, versionPlatforms) { platforms.satisfies(requestedPlatform, _.pluck(versionPlatforms, 'type'))); } + + module.exports = function(github, opts) { // Normalize tag name function normalizeTag(tag) { @@ -53,7 +57,7 @@ module.exports = function(github, opts) { var suffix = tag.split('-')[1]; if (!suffix) return 'stable'; - return suffix.split('.')[0]; + return suffix.match(channelRe)[0] || suffix.split('.')[0]; } // Normalize a release to a version diff --git a/lib/win-releases.js b/lib/win-releases.js index 29055c54..b5790bd1 100644 --- a/lib/win-releases.js +++ b/lib/win-releases.js @@ -2,43 +2,14 @@ var _ = require('lodash'); var semver = require('semver'); var stripBom = require('strip-bom'); -// Ordered list of supported channel -var CHANNEL_MAGINITUDE = 1000; -var CHANNELS = [ - 'alpha', 'beta', 'unstable', 'rc' -]; - // RELEASES parsing var releaseRe = /^([0-9a-fA-F]{40})\s+(\S+)\s+(\d+)[\r]*$/; - - -// Hash a prerelease -function hashPrerelease(s) { - if (_.isString(s[0])) { - return (_.indexOf(CHANNELS, s[0]) + 1) * CHANNEL_MAGINITUDE + (s[1] || 0); - } else { - return s[0]; - } -}; - -// Map a semver version to a windows version -function normVersion(tag) { - var parts = new semver.SemVer(tag); - var prerelease = ""; - - if (parts.prerelease && parts.prerelease.length > 0) { - prerelease = hashPrerelease(parts.prerelease); - } - - return [ - parts.major, - parts.minor, - parts.patch - ].join('.') + (prerelease? '.'+prerelease : ''); -} +var suffixRe = /(-full|-delta)?\.nupkg/; +var versionRe = /\d+(\.\d+){0,3}(-[a-z][0-9a-z-\.]*$)?$/; +var prereleaseRe = /-[a-z][0-9a-z-\.]*$/; // Parse RELEASES file -// https://github.com/Squirrel/Squirrel.Windows/blob/0d1250aa6f0c25fe22e92add78af327d1277d97d/src/Squirrel/ReleaseExtensions.cs#L19 +// https://github.com/Squirrel/Squirrel.Windows/blob/2cc6bfe3c51d0cd0a56511deb8bdab8172aed409/src/Squirrel/ReleaseExtensions.cs#L13-L14 function parseRELEASES(content) { return _.chain(stripBom(content)) .replace('\r\n', '\n') @@ -49,21 +20,22 @@ function parseRELEASES(content) { var filename = parts[2]; var isDelta = filename.indexOf('-full.nupkg') == -1; - - var filenameParts = filename - .replace(".nupkg", "") - .replace("-delta", "") - .replace("-full", "") - .split(/\.|-/) - .reverse(); - - var version = _.chain(filenameParts) - .filter(function(x) { - return /^\d+$/.exec(x); + var version = _.chain( + filename + .replace(suffixRe, '') + .match(versionRe) + ) + .thru(function(matchResult) { + return matchResult ? matchResult[0] : ''; + }) + .replace(prereleaseRe, function(prerelease) { + // NuGet doesn't support dots in prereleases + // https://docs.nuget.org/create/versioning#user-content-prerelease-versions + return prerelease.replace(/\./g, ''); }) - .reverse() - .value() - .join('.'); + .value(); + + if (!version) throw new Error('Release missing valid version: ' + filename); return { sha: parts[1], @@ -100,7 +72,6 @@ function generateRELEASES(entries) { } module.exports = { - normVersion: normVersion, parse: parseRELEASES, generate: generateRELEASES }; diff --git a/test/versions.js b/test/versions.js index b714ef8f..098ab1a6 100644 --- a/test/versions.js +++ b/test/versions.js @@ -1,5 +1,6 @@ var _ = require('lodash'), + Q = require('q'), should = require('should'), rewire = require('rewire'); @@ -7,183 +8,282 @@ describe('Versions', function() { var Versions = rewire('../lib/versions'); - var testVersions = [ - { tag: '0.0.8-alpha.1', - channel: 'alpha', - notes: 'More placeholder text.', - published_at: 'Mon Dec 14 2015 12:13:58 GMT-0800 (PST)', - platforms: [{ - type: 'osx_64' - }, { - type: 'windows_32' - }], - download_count: 3 }, - { tag: '0.0.7', - channel: 'stable', - notes: 'Kinda drifted away from the story thing and I\'m just writing random stuff now.', - published_at: 'Mon Dec 14 2015 11:41:30 GMT-0800 (PST)', - platforms: [{ - type: 'osx_64' - }, { - type: 'windows_32' - }], - download_count: 0 }, - { tag: '0.0.7-beta.1', - channel: 'beta', - notes: 'A beautiful brand new release', - published_at: 'Fri Dec 11 2015 17:01:43 GMT-0800 (PST)', - platforms: [{ - type: 'osx_64' - }, { - type: 'windows_32' - }], - download_count: 0 }, - { tag: '0.0.7-alpha.2', - channel: 'alpha', - notes: 'More excitement!!!', - published_at: 'Fri Dec 11 2015 17:21:08 GMT-0800 (PST)', - platforms: [{ - type: 'osx_64' - }, { - type: 'windows_32' - }], - download_count: 0 }, - { tag: '0.0.7-alpha.1', - channel: 'alpha', - notes: 'A beautiful brand new release', - published_at: 'Fri Dec 11 2015 17:01:43 GMT-0800 (PST)', - platforms: [{ - type: 'windows_32' - }], - download_count: 0 }, - { tag: '0.0.6', - channel: 'stable', - notes: 'A wonderful new release!', - published_at: 'Fri Dec 11 2015 15:55:45 GMT-0800 (PST)', - platforms: [{ - type: 'osx_64' - }], - download_count: 2 } - ]; + describe('when extracting a channel from a tag', function() { + + var mockGithubReleases = [ + { + expectations: { + channel: 'alpha' + }, + data: { + tag_name: '0.0.8-alpha.20151214.1', + name: '0.0.8-alpha.20151214.1', + assets: [ { + url: 'https://api.github.com/repos/skewart/hooktest/releases/assets/1120656', + name: 'AlphaTest-osx-0.0.6-alpha.20151211.1.zip', + content_type: 'application/x-msdownload', + size: 168493, + download_count: 0, + }, { + url: 'https://api.github.com/repos/skewart/hooktest/releases/assets/1120656', + name: 'AlphaTest-osx-0.0.6-alpha.20151211.1.dmg', + content_type: 'application/x-msdownload', + size: 168493, + download_count: 0, + }] + } + }, + { + expectations: { + channel: '3000' + }, + data: { + tag_name: '0.0.8-3000.20151214.1', + name: '0.0.8-3000.20151214.1', + assets: [ { + url: 'https://api.github.com/repos/skewart/hooktest/releases/assets/1120656', + name: 'AlphaTest-osx-0.0.6-3000.20151211.1.zip', + content_type: 'application/x-msdownload', + size: 168493, + download_count: 0, + }, { + url: 'https://api.github.com/repos/skewart/hooktest/releases/assets/1120656', + name: 'AlphaTest-osx-0.0.6-3000.20151211.1.dmg', + content_type: 'application/x-msdownload', + size: 168493, + download_count: 0, + }] + } + }, + { + expectations: { + channel: 'alpha' + }, + data: { + tag_name: '0.0.8-alpha201512101', + name: '0.0.8-alpha201512101', + assets: [ { + url: 'https://api.github.com/repos/skewart/hooktest/releases/assets/1120656', + name: 'AlphaTest-osx-0.0.6-alpha201512101.zip', + content_type: 'application/x-msdownload', + size: 168493, + download_count: 0, + }, { + url: 'https://api.github.com/repos/skewart/hooktest/releases/assets/1120656', + name: 'AlphaTest-osx-0.0.6-alpha201512101.dmg', + content_type: 'application/x-msdownload', + size: 168493, + download_count: 0, + }] + } + } + ] + + _.map(mockGithubReleases, function(release) { + + var versions = Versions({ + releases: function() { + return Q.fcall(function() { + return [release.data]; + }); + } + }); + + var expectedChannel = release.expectations.channel, + tag = release.data.tag_name; - function testVersionFilter(filterName, scenarios) { - describe(filterName, function() { - var filterFunction = Versions.__get__(filterName); - - scenarios.forEach(function(scenario) { - var opts = scenario.opts, - description = 'with opts = { tag: ' + opts.tag + ', channel: ' + opts.channel - + ', platform: ' + opts.platform + ' }, return -> [' + scenario.expectation.join(', ') + ']'; - - it(description, function() { - var result = _.chain(testVersions) - .filter(filterFunction(scenario.opts)) - .pluck('tag') - .value() - .join(', ') - .should.equal(scenario.expectation.join(', ')); + it('should get channel ' + expectedChannel + ' from tag ' + tag, function(done) { + versions.list().then(function(versions) { + versions[0].channel.should.be.exactly(expectedChannel); + done(); }); }); + }); - } - - testVersionFilter('newVersionFilter', [ - { - opts: { - tag: '0.0.7', - platform: 'osx', - channel: undefined - }, - expectation: ['0.0.7'] - }, - { - opts: { - tag: '0.0.6', - platform: 'osx', - channel: undefined + + }); + + describe('filtering', function() { + + function testVersionFilter(filterName, scenarios) { + describe('with ' + filterName, function() { + var filterFunction = Versions.__get__(filterName); + + scenarios.forEach(function(scenario) { + var opts = scenario.opts, + description = 'with opts = { tag: ' + opts.tag + ', channel: ' + opts.channel + + ', platform: ' + opts.platform + ' }, return -> [' + scenario.expectation.join(', ') + ']'; + + it(description, function() { + var result = _.chain(testVersions) + .filter(filterFunction(scenario.opts)) + .pluck('tag') + .value() + .join(', ') + .should.equal(scenario.expectation.join(', ')); + }); + }); + }); + } + + var testVersions = [ + { tag: '0.0.8-alpha.1', + channel: 'alpha', + notes: 'More placeholder text.', + published_at: 'Mon Dec 14 2015 12:13:58 GMT-0800 (PST)', + platforms: [{ + type: 'osx_64' + }, { + type: 'windows_32' + }], + download_count: 3 }, + { tag: '0.0.7', + channel: 'stable', + notes: 'Kinda drifted away from the story thing and I\'m just writing random stuff now.', + published_at: 'Mon Dec 14 2015 11:41:30 GMT-0800 (PST)', + platforms: [{ + type: 'osx_64' + }, { + type: 'windows_32' + }], + download_count: 0 }, + { tag: '0.0.7-beta.1', + channel: 'beta', + notes: 'A beautiful brand new release', + published_at: 'Fri Dec 11 2015 17:01:43 GMT-0800 (PST)', + platforms: [{ + type: 'osx_64' + }, { + type: 'windows_32' + }], + download_count: 0 }, + { tag: '0.0.7-alpha.2', + channel: 'alpha', + notes: 'More excitement!!!', + published_at: 'Fri Dec 11 2015 17:21:08 GMT-0800 (PST)', + platforms: [{ + type: 'osx_64' + }, { + type: 'windows_32' + }], + download_count: 0 }, + { tag: '0.0.7-alpha.1', + channel: 'alpha', + notes: 'A beautiful brand new release', + published_at: 'Fri Dec 11 2015 17:01:43 GMT-0800 (PST)', + platforms: [{ + type: 'windows_32' + }], + download_count: 0 }, + { tag: '0.0.6', + channel: 'stable', + notes: 'A wonderful new release!', + published_at: 'Fri Dec 11 2015 15:55:45 GMT-0800 (PST)', + platforms: [{ + type: 'osx_64' + }], + download_count: 2 } + ]; + + testVersionFilter('newVersionFilter', [ + { + opts: { + tag: '0.0.7', + platform: 'osx', + channel: undefined + }, + expectation: ['0.0.7'] }, - expectation: ['0.0.7', '0.0.6'] - }, - { - opts: { - tag: '0.0.7-beta.1', - platform: 'osx', - channel: undefined + { + opts: { + tag: '0.0.6', + platform: 'osx', + channel: undefined + }, + expectation: ['0.0.7', '0.0.6'] }, - expectation: ['0.0.7', '0.0.7-beta.1'] - }, - { - opts: { - tag: '0.0.7-alpha.2', - platform: 'osx', - channel: 'alpha' + { + opts: { + tag: '0.0.7-beta.1', + platform: 'osx', + channel: undefined + }, + expectation: ['0.0.7', '0.0.7-beta.1'] }, - expectation: ['0.0.8-alpha.1', '0.0.7-alpha.2'] - }, - { - opts: { - tag: '0.0.7-beta.1', - platform: 'osx', - channel: 'beta' + { + opts: { + tag: '0.0.7-alpha.2', + platform: 'osx', + channel: 'alpha' + }, + expectation: ['0.0.8-alpha.1', '0.0.7-alpha.2'] }, - expectation: ['0.0.7-beta.1'] - }, - { - opts: { - tag: '0.0.7-alpha.1', - platform: 'windows', - channel: 'alpha' + { + opts: { + tag: '0.0.7-beta.1', + platform: 'osx', + channel: 'beta' + }, + expectation: ['0.0.7-beta.1'] }, - expectation: ['0.0.8-alpha.1', '0.0.7-alpha.2', '0.0.7-alpha.1'] - } - ]); - - testVersionFilter('allVersionFilter', [ - { - opts: {}, - expectation: ['0.0.8-alpha.1', '0.0.7', '0.0.7-beta.1', '0.0.7-alpha.2', - '0.0.7-alpha.1', '0.0.6'] - }, - { - opts: { - platform: 'osx' + { + opts: { + tag: '0.0.7-alpha.1', + platform: 'windows', + channel: 'alpha' + }, + expectation: ['0.0.8-alpha.1', '0.0.7-alpha.2', '0.0.7-alpha.1'] + } + ]); + + testVersionFilter('allVersionFilter', [ + { + opts: {}, + expectation: ['0.0.8-alpha.1', '0.0.7', '0.0.7-beta.1', '0.0.7-alpha.2', + '0.0.7-alpha.1', '0.0.6'] }, - expectation: ['0.0.8-alpha.1', '0.0.7', '0.0.7-beta.1', '0.0.7-alpha.2', - '0.0.6'] - }, - { - opts: { - platform: 'windows' + { + opts: { + platform: 'osx' + }, + expectation: ['0.0.8-alpha.1', '0.0.7', '0.0.7-beta.1', '0.0.7-alpha.2', + '0.0.6'] }, - expectation: ['0.0.8-alpha.1', '0.0.7', '0.0.7-beta.1', '0.0.7-alpha.2', - '0.0.7-alpha.1'] - }, - { - opts: { - channel: 'alpha' + { + opts: { + platform: 'windows' + }, + expectation: ['0.0.8-alpha.1', '0.0.7', '0.0.7-beta.1', '0.0.7-alpha.2', + '0.0.7-alpha.1'] }, - expectation: ['0.0.8-alpha.1', '0.0.7-alpha.2', '0.0.7-alpha.1'] - }, - { - opts: { - channel: '*' + { + opts: { + channel: 'alpha' + }, + expectation: ['0.0.8-alpha.1', '0.0.7-alpha.2', '0.0.7-alpha.1'] }, - expectation: ['0.0.8-alpha.1', '0.0.7', '0.0.7-beta.1', '0.0.7-alpha.2', - '0.0.7-alpha.1', '0.0.6'] - }, - { - opts: { - channel: 'alpha', - platform: 'osx' + { + opts: { + channel: '*' + }, + expectation: ['0.0.8-alpha.1', '0.0.7', '0.0.7-beta.1', '0.0.7-alpha.2', + '0.0.7-alpha.1', '0.0.6'] }, - expectation: ['0.0.8-alpha.1', '0.0.7-alpha.2'] - }, - { - opts: { - tag: '0.0.7-alpha.2' + { + opts: { + channel: 'alpha', + platform: 'osx' + }, + expectation: ['0.0.8-alpha.1', '0.0.7-alpha.2'] }, - expectation: ['0.0.7-alpha.2'] - } - ]); + { + opts: { + tag: '0.0.7-alpha.2' + }, + expectation: ['0.0.7-alpha.2'] + } + ]); + + }); }); diff --git a/test/win-releases.js b/test/win-releases.js index b2a1a0fb..9ab2ec9a 100644 --- a/test/win-releases.js +++ b/test/win-releases.js @@ -1,26 +1,9 @@ var should = require('should'); +var assert = require('assert'); var winReleases = require('../lib/win-releases'); describe('Windows RELEASES', function() { - describe('Version Normalization', function() { - - it('should not changed version without pre-release', function() { - winReleases.normVersion('1.0.0').should.be.exactly('1.0.0'); - winReleases.normVersion('4.5.0').should.be.exactly('4.5.0'); - winReleases.normVersion('67.8.345').should.be.exactly('67.8.345'); - }); - - it('should normalize the pre-release', function() { - winReleases.normVersion('1.0.0-alpha.1').should.be.exactly('1.0.0.1001'); - winReleases.normVersion('1.0.0-beta.1').should.be.exactly('1.0.0.2001'); - winReleases.normVersion('1.0.0-unstable.1').should.be.exactly('1.0.0.3001'); - winReleases.normVersion('1.0.0-rc.1').should.be.exactly('1.0.0.4001'); - winReleases.normVersion('1.0.0-14').should.be.exactly('1.0.0.14'); - }); - - }); - describe('Parsing', function() { var releases = winReleases.parse( '62E8BF432F29E8E08240910B85EDBF2D1A41EDF2 atom-0.178.0-full.nupkg 81272434\n' + @@ -66,6 +49,23 @@ describe('Windows RELEASES', function() { }); + describe('Parsing pre-releases', function() { + var releases = winReleases.parse( + '62E8BF432F29E8E08240910B85EDBF2D1A41EDF2 atom-0.178.0-beta.20160120.1-full.nupkg 81272434\n' + ); + + it('should correctly parse versions', function() { + releases[0].version.should.be.exactly('0.178.0-beta201601201'); + }); + + it('should throw an appropriate error if a release has no version info', function() { + var releaseWithNoVersion = '62E8BF432F29E8E08240910B85EDBF2D1A41EDF2 atom-full.nupkg 81272434\n' + assert.throws(winReleases.parse.bind(winReleases, releaseWithNoVersion), + Error, 'Release missing valid version: atom-full.nupkg'); + }); + + }); + describe('Generations', function() { var input = '62E8BF432F29E8E08240910B85EDBF2D1A41EDF2 atom-0.178.0-full.nupkg 81272434\n' + '5D754139E89802E88984185D2276B54DB730CD5E atom-0.178.1-delta.nupkg 8938535\n' +