From fe41cef2dceff63e898244dfa672ea8eb40ab1f9 Mon Sep 17 00:00:00 2001 From: Thom Nichols Date: Mon, 16 Jan 2017 12:28:18 -0500 Subject: [PATCH] Add "binary.tag" option to specify the tag name separate from the remote path Fixes #18 --- README.md | 13 +++++++------ index.js | 54 ++++++++++++++++++++++++++++++++++------------------ test/test.js | 46 ++++++++++++++++++++++---------------------- 3 files changed, 65 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index b192b13..6973060 100644 --- a/README.md +++ b/README.md @@ -19,17 +19,18 @@ npm install -g node-pre-gyp-github ``` ## Configuration -This module is intended to be used with node-pre-gyp. Therefore, be sure to configure and install node-pre-gyp first. After having done that, within **```package.json```** update the ```binary``` properties ```host``` and ```remote_path``` so it matches the following format: +This module is intended to be used with node-pre-gyp. Therefore, be sure to configure and install node-pre-gyp first. After having done that, within **```package.json```** update the `binary` section properties so they match the following format: ``` - "host": "https://github.com/[owner]/[repo]/releases/download/", - "remote_path": "{version}" + "host": "https://github.com", + "remote_path": "/[owner]/[repo]/releases/download/", + "tag": "{version}" ``` -Be sure to replace ```[owner]```, ```[repo]```, with actual values, -but DO NOT replace ```{version}``` with actual version. +Be sure to replace `[owner]`, `[repo]`, with actual values, +but DO NOT replace `{version}` with actual version. -***WARNING: Variable substitutions are not supported on the ```host``` property and on the ```remote_path``` only ```{version}``` placeholder is supported. The value of ```remote_path``` after substitution will become a release tag name. Do not use [forbidden git tag characters](https://git-scm.com/docs/git-check-ref-format) for ```version``` and ```remote_path``` properties.*** +***WARNING: Variable substitutions are not supported on the `host` or `remote_path` property. Only `{version}` placeholder is supported for the `tag` property. The value of `tag` after substitution will become a release tag name. Do not use [forbidden git tag characters](https://git-scm.com/docs/git-check-ref-format) in your `package.version` or `package.binary.tag` properties.*** Within GitHub, create a new authorization: diff --git a/index.js b/index.js index 9c7879c..7264420 100644 --- a/index.js +++ b/index.js @@ -31,10 +31,10 @@ NodePreGypGithub.prototype.release = {}; NodePreGypGithub.prototype.stage_dir = path.join(cwd,"build","stage"); NodePreGypGithub.prototype.init = function() { - var ownerRepo, hostPrefix; - + var ownerRepo, hostPrefix, pathPrefix; + this.package_json = JSON.parse(fs.readFileSync(path.join(cwd,'package.json'))); - + if(!this.package_json.repository || !this.package_json.repository.url){ throw new Error('Missing repository.url in package.json'); } @@ -47,17 +47,26 @@ NodePreGypGithub.prototype.init = function() { } else throw new Error('A correctly formatted GitHub repository.url was not found within package.json'); } - - hostPrefix = 'https://github.com/' + this.owner + '/' + this.repo + '/releases/download/'; + + hostPrefix = 'https://github.com' + pathPrefix = '/' + this.owner + '/' + this.repo + '/releases/download/'; if(!this.package_json.binary || 'object' !== typeof this.package_json.binary || 'string' !== typeof this.package_json.binary.host){ throw new Error('Missing binary.host in package.json'); } else if (this.package_json.binary.host.substr(0, hostPrefix.length) !== hostPrefix){ throw new Error('binary.host in package.json should begin with: "' + hostPrefix + '"'); } - + else if('string' == typeof this.package_json.binary.tag ) { + if (this.package_json.binary.remote_path.substr(0, pathPrefix.length) !== pathPrefix){ + throw new Error('binary.remote_path in package.json should begin with: "' + pathPrefix + '" when using binary.tag'); + } + } + else if(! this.package_json.binary.host.match(pathPrefix)) { + throw new Error('binary.host in package.json should contain: "' + pathPrefix + '"'); + } + this.github.headers = {"user-agent": (this.package_json.name) ? this.package_json.name : "node-pre-gyp-github"}; // GitHub is happy with a unique user agent - + }; NodePreGypGithub.prototype.authenticate_settings = function(){ @@ -80,13 +89,13 @@ NodePreGypGithub.prototype.createRelease = function(args, callback) { 'draft': true, 'prerelease': false }; - + Object.keys(args).forEach(function(key) { if(args.hasOwnProperty(key) && options.hasOwnProperty(key)) { options[key] = args[key]; } }); - + this.github.authenticate(this.authenticate_settings()); this.github.releases.createRelease(options, callback); }; @@ -110,9 +119,9 @@ NodePreGypGithub.prototype.uploadAssets = function(){ consoleLog("Stage directory path: " + path.join(this.stage_dir)); fs.readdir(path.join(this.stage_dir), function(err, files){ if(err) throw err; - + if(!files.length) throw new Error('No files found within the stage directory: ' + this.stage_dir); - + files.forEach(function(file){ asset = this.release.assets.filter(function(element, index, array){ return element.name === file; @@ -141,18 +150,25 @@ NodePreGypGithub.prototype.publish = function(options) { 'repo': this.repo }, function(err, data){ var release; - + if(err) throw err; - + + // prefer `binary.tag` if present, before remote_path - see https://github.com/bchr02/node-pre-gyp-github/issues/18 + if (this.package_json.binary.tag) { + options.tag_name = this.package_json.binary.tag.replace(/\{version\}/g, this.package_json.version); + // stage_dir is still based on remote_path, not tag: + this.stage_dir = path.join(this.stage_dir, (this.package_json.binary.remote_path || '')); + } // when remote_path is set expect files to be in stage_dir / remote_path after substitution - if (this.package_json.binary.remote_path) { + // {version} substitution only takes place in `remote_path` if `package_json.binary.tag` is not present: + else if (this.package_json.binary.remote_path) { options.tag_name = this.package_json.binary.remote_path.replace(/\{version\}/g, this.package_json.version); this.stage_dir = path.join(this.stage_dir, options.tag_name); } else { // This is here for backwards compatibility for before binary.remote_path support was added in version 1.2.0. options.tag_name = this.package_json.version; } - + release = (function(){ // create a new array containing only those who have a matching version. if(data) { data = data.filter(function(element, index, array){ @@ -162,13 +178,13 @@ NodePreGypGithub.prototype.publish = function(options) { } else return []; }.bind(this))(); - + this.release = release[0]; - + if(!release.length) { this.createRelease(options, function(err, release) { if(err) throw err; - + this.release = release; if (release.draft) { consoleLog('Release ' + release.tag_name + " not found, so a draft release was created. YOU MUST MANUALLY PUBLISH THIS DRAFT WITHIN GITHUB FOR IT TO BE ACCESSIBLE."); @@ -185,4 +201,4 @@ NodePreGypGithub.prototype.publish = function(options) { }.bind(this)); }; -module.exports = NodePreGypGithub; \ No newline at end of file +module.exports = NodePreGypGithub; diff --git a/test/test.js b/test/test.js index 7071d7c..2c0fd84 100644 --- a/test/test.js +++ b/test/test.js @@ -12,7 +12,7 @@ var reset_index = function(index_string_ref) { var reset_mocks = function() { process.env.NODE_PRE_GYP_GITHUB_TOKEN = "secret"; fs = reset_index('fs'); - fs.readFileSync = function(){return '{"name":"test","version":"0.0.1","repository": {"url":"git+https://github.com/test/test.git"},"binary":{"host":"https://github.com/test/test/releases/download/","remote_path":"{version}"}}';}; + fs.readFileSync = function(){return '{"name":"test","version":"0.0.1","repository": {"url":"git+https://github.com/test/test.git"},"binary":{"host":"https://github.com","remote_path":"/test/test/releases/download/","tag":"{version}"}}';}; index.stage_dir = stage_dir; index.github.authenticate = function(){}; index.github.releases.listReleases = function(options, cb){ @@ -28,9 +28,9 @@ if(!process.env.COVERALLS_SERVICE_NAME) console.log('To post to coveralls.io, be if(!process.env.COVERALLS_REPO_TOKEN) console.log('To post to coveralls.io, be sure to set COVERALLS_REPO_TOKEN environment variable'); describe("Publishes packages to GitHub Releases", function() { - + describe("Publishes without an error under all options", function() { - + it("should publish a non-draft release without an error", function() { var options = {'draft': false, 'verbose': false}; reset_mocks(); @@ -42,7 +42,7 @@ describe("Publishes packages to GitHub Releases", function() { }; expect(function(){ index.publish(options); }).to.not.throw(); }); - + it("should publish a draft release without an error", function() { var options = {'draft': true, 'verbose': false}; reset_mocks(); @@ -51,46 +51,46 @@ describe("Publishes packages to GitHub Releases", function() { }; expect(function(){ index.publish(options); }).to.not.throw(); }); - + }); - + describe("Throws an error when node-pre-gyp-github is not configured properly", function() { - + it("should throw an error when missing repository.url in package.json", function() { var options = {'draft': true, 'verbose': false}; reset_mocks(); fs.readFileSync = function(){return '{}';}; expect(function(){ index.publish(options); }).to.throw("Missing repository.url in package.json"); }); - + it("should throw an error when a correctly formatted GitHub repository.url is not found in package.json", function() { var options = {'draft': true, 'verbose': false}; reset_mocks(); fs.readFileSync = function(){return '{"repository": {"url":"bad_format_url"}}';}; expect(function(){ index.publish(options); }).to.throw("A correctly formatted GitHub repository.url was not found within package.json"); }); - + it("should throw an error when missing binary.host in package.json", function() { var options = {'draft': true, 'verbose': false}; reset_mocks(); fs.readFileSync = function(){return '{"repository": {"url":"git+https://github.com/test/test.git"}}';}; expect(function(){ index.publish(options); }).to.throw("Missing binary.host in package.json"); }); - + it("should throw an error when binary.host does not begin with the correct url", function() { var options = {'draft': true, 'verbose': false}; reset_mocks(); fs.readFileSync = function(){return '{"repository": {"url":"git+https://github.com/test/test.git"},"binary":{"host":"bad_format_binary"}}';}; expect(function(){ index.publish(options); }).to.throw(/^binary.host in package.json should begin with:/i); }); - + it("should throw an error when the NODE_PRE_GYP_GITHUB_TOKEN environment variable is not found", function() { var options = {'draft': true, 'verbose': false}; reset_mocks(); process.env.NODE_PRE_GYP_GITHUB_TOKEN = ""; expect(function(){ index.publish(options); }).to.throw("NODE_PRE_GYP_GITHUB_TOKEN environment variable not found"); }); - + it("should throw an error when github.releases.listReleases returns an error", function() { var options = {'draft': true, 'verbose': false}; reset_mocks(); @@ -99,7 +99,7 @@ describe("Publishes packages to GitHub Releases", function() { }; expect(function(){ index.publish(options); }).to.throw('listReleases error'); }); - + it("should throw an error when github.releases.createRelease returns an error", function() { var options = {'draft': true, 'verbose': false}; reset_mocks(); @@ -111,7 +111,7 @@ describe("Publishes packages to GitHub Releases", function() { }; expect(function(){ index.publish(options); }).to.throw('createRelease error'); }); - + it("should throw an error when the stage directory structure is missing", function() { var options = {'draft': true, 'verbose': false}; reset_mocks(); @@ -120,7 +120,7 @@ describe("Publishes packages to GitHub Releases", function() { }; expect(function(){ index.publish(options); }).to.throw('readdir Error'); }); - + it("should throw an error when there are no files found within the stage directory", function() { var options = {'draft': true, 'verbose': false}; reset_mocks(); @@ -129,7 +129,7 @@ describe("Publishes packages to GitHub Releases", function() { }; expect(function(){ index.publish(options); }).to.throw(/^No files found within the stage directory:/i); }); - + it("should throw an error when a staged file already exists in the current release", function() { var options = {'draft': true, 'verbose': false}; reset_mocks(); @@ -141,7 +141,7 @@ describe("Publishes packages to GitHub Releases", function() { }; expect(function(){ index.publish(options); }).to.throw(/^Staged file .* found but it already exists in release .*. If you would like to replace it, you must first manually delete it within GitHub./i); }); - + it("should throw an error when github.releases.uploadAsset returns an error", function() { var options = {'draft': true, 'verbose': false}; reset_mocks(); @@ -149,15 +149,15 @@ describe("Publishes packages to GitHub Releases", function() { cb(null,["filename"]); }; index.github.releases.uploadAsset = function(cfg,cb){ - cb(new Error('uploadAsset error')); + cb(new Error('uploadAsset error')); }; expect(function(){ index.publish(options); }).to.throw("uploadAsset error"); }); - + }); - + describe("Verify backwords compatible with any breaking changes made within the same MINOR version.", function() { - + it("should publish even when package.json's binary.remote_path property is not provided and instead the version is hard coded within binary.host", function() { var options = {'draft': false, 'verbose': false}; reset_mocks(); @@ -170,6 +170,6 @@ describe("Publishes packages to GitHub Releases", function() { }; expect(function(){ index.publish(options); }).to.not.throw(); }); - + }); -}); \ No newline at end of file +});