From 9da58a5403118fb029ac979574a8743884f5f614 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Scott=20Gonz=C3=A1lez?= Date: Fri, 6 Dec 2013 10:56:27 -0500 Subject: [PATCH 1/5] GitHub: Implemented changelog generation Fixes gh-1 --- lib/changelog.js | 38 +++---- lib/contributors.js | 61 +++++------ lib/github.js | 77 ++++++++++++++ lib/trac.js | 24 +++++ node_modules/github-request/LICENSE.txt | 20 ++++ node_modules/github-request/README.md | 69 +++++++++++++ node_modules/github-request/lib/request.js | 114 +++++++++++++++++++++ node_modules/github-request/package.json | 45 ++++++++ package.json | 1 + 9 files changed, 390 insertions(+), 59 deletions(-) create mode 100644 lib/github.js create mode 100644 node_modules/github-request/LICENSE.txt create mode 100644 node_modules/github-request/README.md create mode 100644 node_modules/github-request/lib/request.js create mode 100644 node_modules/github-request/package.json diff --git a/lib/changelog.js b/lib/changelog.js index 6847359..54560c9 100644 --- a/lib/changelog.js +++ b/lib/changelog.js @@ -7,14 +7,19 @@ module.exports = function( Release ) { Release.define({ _generateChangelog: function( callback ) { Release._generateCommitChangelog(function( commitChangelog ) { - var changelogPath = Release.dir.base + "/changelog", - changelog = Release.changelogShell() + - commitChangelog + - Release._generateIssueChangelog(); - - fs.writeFileSync( changelogPath, changelog ); - console.log( "Stored changelog in " + chalk.cyan( changelogPath ) + "." ); - callback(); + Release._generateIssueChangelog(function( issueChangelog ) { + var changelogPath = Release.dir.base + "/changelog", + changelog = Release.changelogShell() + + commitChangelog + + "\n\n\n" + + "--- Issue List ---\n" + + issueChangelog; + + fs.writeFileSync( changelogPath, changelog ); + console.log( "Stored changelog in " + chalk.cyan( changelogPath ) + "." ); + + callback(); + }); }); }, @@ -43,21 +48,10 @@ Release.define({ }); }, - _generateIssueChangelog: function() { + _generateIssueChangelog: function( callback ) { return Release.issueTracker === "trac" ? - Release._generateTracChangelog() : - Release._generateGithubChangelog(); - }, - - _generateTracChangelog: function() { - console.log( "Adding Trac tickets..." ); - return Release.trac( "/query?milestone=" + Release.tracMilestone() + "&resolution=fixed" + - "&col=id&col=component&col=summary&order=component" ) + "\n"; - }, - - _generateGithubChangelog: function() { - console.log( "Adding GitHub issues..." ); - // TODO + Release._generateTracChangelog( callback ) : + Release._generateGithubChangelog( callback ); } }); diff --git a/lib/contributors.js b/lib/contributors.js index ab7cf59..eb66e91 100644 --- a/lib/contributors.js +++ b/lib/contributors.js @@ -12,51 +12,38 @@ function unique( arr ) { } Release.define({ - _gatherContributors: function() { + _gatherContributors: function( callback ) { var contributors, contributorsPath = Release.dir.base + "/contributors.txt"; - console.log( "Adding committers and authors..." ); - Release.chdir( Release.dir.repo ); - contributors = Release.gitLog( "%aN%n%cN" ); - console.log( "Adding reporters and commenters from issues..." ); - contributors = contributors.concat( Release._gatherIssueContributors() ); - - console.log( "Sorting contributors..." ); - contributors = unique( contributors ).sort(function( a, b ) { - return a.toLowerCase() < b.toLowerCase() ? -1 : 1; + Release._gatherIssueContributors(function( contributors ) { + console.log( "Adding committers and authors..." ); + Release.chdir( Release.dir.repo ); + contributors = contributors.concat( Release.gitLog( "%aN%n%cN" ) ); + + console.log( "Sorting contributors..." ); + contributors = unique( contributors ).sort(function( a, b ) { + return a.toLowerCase() < b.toLowerCase() ? -1 : 1; + }); + + console.log( "Adding people thanked in commits..." ); + contributors = contributors.concat( + Release.gitLog( "%b%n%s" ).filter(function( line ) { + return (/thank/i).test( line ); + })); + + fs.writeFileSync( contributorsPath, contributors.join( "\n" ) ); + console.log( "Stored contributors in " + chalk.cyan( contributorsPath ) + "." ); + + callback(); }); - - console.log( "Adding people thanked in commits..." ); - contributors = contributors.concat( - Release.gitLog( "%b%n%s" ).filter(function( line ) { - return (/thank/i).test( line ); - })); - - fs.writeFileSync( contributorsPath, contributors.join( "\n" ) ); - console.log( "Stored contributors in " + chalk.cyan( contributorsPath ) + "." ); }, - _gatherIssueContributors: function() { + _gatherIssueContributors: function( callback ) { return Release.issueTracker === "trac" ? - Release._gatherTracContributors() : - Release._gatherGithubIssueContributors(); - }, - - _gatherTracContributors: function() { - var url = "/report/" + Release.contributorReportId + - "?V=" + Release.tracMilestone() + "&max=-1"; - - return Release.trac( url ) - .split( /\r?\n/ ) - - // Remove header and trailing newline - .slice( 1, -1 ); - }, - - _gatherGithubIssueContributors: function() { - // TODO + Release._gatherTracContributors( callback ) : + Release._gatherGithubIssueContributors( callback ); } }); diff --git a/lib/github.js b/lib/github.js new file mode 100644 index 0000000..e10c9a8 --- /dev/null +++ b/lib/github.js @@ -0,0 +1,77 @@ +var querystring = require( "querystring" ); +var github = require( "github-request" ); + +module.exports = function( Release ) { + +Release.define({ + _githubApiPath: function( path ) { + var repoUrl = Release._packageUrl( "bugs" ); + var repo = repoUrl.match( /github\.com\/(\w+\/\w+)/ )[ 1 ]; + return "/repos/" + repo + "/" + path; + }, + + _githubMilestone: function( callback ) { + github.requestAll({ + path: Release._githubApiPath( "milestones" ) + }, function( error, milestones ) { + if ( error ) { + Release.abort( "Error getting milestones.", error ); + } + + var milestone = milestones.filter(function( milestone ) { + return milestone.title === Release.newVersion; + })[ 0 ]; + + if ( !milestone ) { + Release.abort( "No milestone found." ); + } + + callback( milestone.number ); + }); + }, + + _generateGithubChangelog: function( callback ) { + Release._githubMilestone(function( milestone ) { + github.requestAll({ + path: Release._githubApiPath( "issues?" + querystring.stringify( { + milestone: milestone, + state: "closed" + } ) ), + }, function( error, issues ) { + if ( error ) { + return Release.abort( "Error getting issues.", error ); + } + + var changelog = issues.map(function( issue ) { + var component = "(none)"; + + issue.labels.forEach(function( label ) { + if ( /^component:/i.test( label.name ) ) { + component = label.name.substring( 11 ); + } + }); + + return [ + "#" + issue.number, + component, + issue.title + ].sort(function( a, b ) { + return a.component > b.component ? 1 : -1; + }).join( "\t" ); + }).join( "\n" ) + "\n"; + + callback( changelog ); + }); + }); + }, + + _gatherGithubIssueContributors: function( callback ) { + + // TODO + process.nextTick(function() { + callback( [] ); + }); + } +}); + +}; diff --git a/lib/trac.js b/lib/trac.js index 38aa69e..848decc 100644 --- a/lib/trac.js +++ b/lib/trac.js @@ -7,6 +7,30 @@ Release.define({ command: "curl -s '" + tracUrl + path + "&format=tab'", silent: true }, "Error getting Trac data." ); + }, + + _generateTracChangelog: function( callback ) { + process.nextTick(function() { + console.log( "Adding Trac tickets..." ); + var changelog = Release.trac( + "/query?milestone=" + Release.newVersion + "&resolution=fixed" + + "&col=id&col=component&col=summary&order=component" ) + "\n"; + callback( changelog ); + }); + }, + + _gatherTracContributors: function( callback ) { + var url = "/report/" + Release.contributorReportId + + "?V=" + Release.tracMilestone() + "&max=-1"; + + process.nextTick(function() { + callback( Release.trac( url ) + .split( /\r?\n/ ) + + // Remove header and trailing newline + .slice( 1, -1 ) + ); + }); } }); diff --git a/node_modules/github-request/LICENSE.txt b/node_modules/github-request/LICENSE.txt new file mode 100644 index 0000000..6ca2fdd --- /dev/null +++ b/node_modules/github-request/LICENSE.txt @@ -0,0 +1,20 @@ +Copyright Scott González http://scottgonzalez.com + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/github-request/README.md b/node_modules/github-request/README.md new file mode 100644 index 0000000..647aff1 --- /dev/null +++ b/node_modules/github-request/README.md @@ -0,0 +1,69 @@ +# GitHub Request + +Simplified GitHub API requests. + +Support this project by [donating on Gratipay](https://gratipay.com/scottgonzalez/). + +## About + +Low level helper for working with the GitHub API. + +## Installation + +```sh +npm install github-request +``` + +## Usage + +```js +var github = require("github-request"); +github.request({ + path: "/orgs/jquery/repos" +}, function(error, repos) { + console.log(repos); +}); +``` + +## API + +### request(settings, data, callback) + +Performs a single request based on the provided settings. + +* `settings` (Object): Settings for the HTTPS request. +* `data` (Mixed): Data to pass for POST requests. Data is encoded as JSON prior to making the request. +* `callback` (`function( error, response, meta )`): A callback to invoke when the API call is complete. + * `response` (Object): The parsed JSON response. + * `meta` (Object): Metadata from the response headers. + +### requestAll(settings, callback) + +Performs a request based on the provided settings and then requests any additional paged content based on the response. Data from all pages are concatenated together and buffered until the last page of data has been retrieved. + +* `settings` (Object): Settings for the HTTPS request. +* `callback` (`function( error, response, meta )`): A callback to invoke when all API calls are complete. + * `response` (Object): The parsed JSON response. + * `meta` (Object): Metadata from the headers of the *last* response. + +### Response Metadata + +The metadata provided contains information from the following headers: + +* `x-ratelimit-*` +* `x-github-*` +* `link` + +These headers are parsed into a more friendly format before being passed as the `meta` parameter in the `callback`. + +All `x-*` headers have the `x-` prefix removed and the names are changed from dashed form to camel case. For example, `x-ratelimit-remaining` becomes `ratelimitRemaining`. + +The `link` header is parsed into the named `rel` values. For example, `; rel="next"` becomes `{next: "https://api.github.com/resource?page=2"}` and is provided in the `links` property. + +## License + +Copyright Scott González. Released under the terms of the MIT license. + +--- + +Support this project by [donating on Gratipay](https://gratipay.com/scottgonzalez/). diff --git a/node_modules/github-request/lib/request.js b/node_modules/github-request/lib/request.js new file mode 100644 index 0000000..a4e9356 --- /dev/null +++ b/node_modules/github-request/lib/request.js @@ -0,0 +1,114 @@ +var https = require("https"); +var url = require("url"); +var userAgent = "Node GitHub Request " + require( "../package" ).version; + +function extend(a, b) { + for (var prop in b) { + a[prop] = b[prop]; + } + + return a; +} + +function xHeader(str) { + if (str.substring(0, 2) === "x-" ) { + str = str.substring(2); + } + + return str.replace(/-([a-z])/g, function(all, letter) { + return letter.toUpperCase(); + }); +} + +function request(settings, data, callback) { + if (typeof data === "function") { + callback = data; + data = null; + } else { + data = JSON.stringify(data); + } + callback = callback || function() {}; + + settings = extend({ + method: "GET" + }, settings); + settings.headers = extend({ + "user-agent": userAgent, + "content-length": typeof data === "string" ? data.length : 0 + }, settings.headers || {}); + + var req = https.request(extend({ + host: "api.github.com" + }, settings), function(res) { + var meta = {}; + Object.keys(res.headers).forEach(function(header) { + if (/^(x-(ratelimit|github))/.test(header)) { + meta[xHeader(header)] = res.headers[header]; + } else if (header === "link") { + var links = res.headers.link.split(/,\s*/); + meta.links = {}; + links.forEach(function(link) { + var parts = /<([^>]+)>;\s*rel="([^"]+)"/.exec(link); + meta.links[parts[2]] = parts[1]; + }); + } + }); + + var response = ""; + res.setEncoding("utf8"); + res.on("data", function(chunk) { + response += chunk; + }); + + res.on("end", function() { + if (res.statusCode >= 400) { + var message; + if (res.headers["content-type"].indexOf("json") !== -1) { + message = JSON.parse(response).message; + } else { + message = response; + } + if (!message && res.statusCode === 403) { + message = "Forbidden"; + } + callback(new Error(message)); + } else { + callback(null, JSON.parse(response), meta); + } + }); + }); + + req.on("error", callback); + + if (data) { + req.write(data); + } + + req.end(); +} + +function requestAll(settings, callback) { + request(settings, function(error, data, meta) { + if (error) { + return callback(error); + } + + if (!meta.links || !meta.links.next) { + return callback(null, data, meta); + } + + settings.path = url.parse(meta.links.next).path; + requestAll(settings, function(error, nextData, nextMeta) { + if (error) { + return callback(error); + } + + callback(null, data.concat(nextData), nextMeta); + }); + }); +} + +module.exports = { + request: request, + requestAll: requestAll +}; diff --git a/node_modules/github-request/package.json b/node_modules/github-request/package.json new file mode 100644 index 0000000..9bdea48 --- /dev/null +++ b/node_modules/github-request/package.json @@ -0,0 +1,45 @@ +{ + "name": "github-request", + "version": "1.2.2", + "description": "Simplified GitHub API requests.", + "keywords": [ + "github", + "request" + ], + "homepage": "https://github.com/scottgonzalez/github-request", + "bugs": { + "url": "https://github.com/scottgonzalez/github-request/issues" + }, + "author": { + "name": "Scott González", + "email": "scott.gonzalez@gmail.com", + "url": "http://scottgonzalez.com" + }, + "main": "./lib/request.js", + "repository": { + "type": "git", + "url": "git://github.com/scottgonzalez/github-request.git" + }, + "gitHead": "bec76ffe4854901aee0a4d589934acbc6a5cc589", + "_id": "github-request@1.2.2", + "scripts": {}, + "_shasum": "a60022753a4212c4426cbaafc0478bb4d8252bdb", + "_from": "github-request@1.2.2", + "_npmVersion": "1.4.28", + "_npmUser": { + "name": "scott.gonzalez", + "email": "scott.gonzalez@gmail.com" + }, + "maintainers": [ + { + "name": "scott.gonzalez", + "email": "scott.gonzalez@gmail.com" + } + ], + "dist": { + "shasum": "a60022753a4212c4426cbaafc0478bb4d8252bdb", + "tarball": "http://registry.npmjs.org/github-request/-/github-request-1.2.2.tgz" + }, + "directories": {}, + "_resolved": "https://registry.npmjs.org/github-request/-/github-request-1.2.2.tgz" +} diff --git a/package.json b/package.json index fe6b1b5..62d2311 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "dependencies": { "chalk": "1.0.0", "changelogplease": "1.1.1", + "github-request": "1.2.2", "semver": "2.2.1", "shelljs": "0.2.6", "which": "1.0.5" From d727bfd7d95270ee6f25dee2dc75045f5a35390e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Scott=20Gonz=C3=A1lez?= Date: Tue, 17 Mar 2015 19:36:30 -0400 Subject: [PATCH 2/5] [fixup] Exclude PRs --- lib/github.js | 45 ++++++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/lib/github.js b/lib/github.js index e10c9a8..031199c 100644 --- a/lib/github.js +++ b/lib/github.js @@ -42,23 +42,34 @@ Release.define({ return Release.abort( "Error getting issues.", error ); } - var changelog = issues.map(function( issue ) { - var component = "(none)"; - - issue.labels.forEach(function( label ) { - if ( /^component:/i.test( label.name ) ) { - component = label.name.substring( 11 ); - } - }); - - return [ - "#" + issue.number, - component, - issue.title - ].sort(function( a, b ) { - return a.component > b.component ? 1 : -1; - }).join( "\t" ); - }).join( "\n" ) + "\n"; + var changelog = issues + + // Remove pull requests from results + .filter(function( issue ) { + return !issue.pull_request; + }) + + // Convert each issue to text + .map(function( issue ) { + var component = "(none)"; + + issue.labels.forEach(function( label ) { + if ( /^component:/i.test( label.name ) ) { + component = label.name.substring( 11 ); + } + }); + + return [ + "#" + issue.number, + component, + issue.title + ].sort(function( a, b ) { + return a.component > b.component ? 1 : -1; + }).join( "\t" ); + }) + + // Concatenate the issues into a string + .join( "\n" ) + "\n"; callback( changelog ); }); From 28b0d42f72b82dda384b740bb2b903bfed3b95e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Scott=20Gonz=C3=A1lez?= Date: Tue, 17 Mar 2015 19:49:54 -0400 Subject: [PATCH 3/5] [fixup] lint --- lib/contributors.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/contributors.js b/lib/contributors.js index eb66e91..1a50f96 100644 --- a/lib/contributors.js +++ b/lib/contributors.js @@ -13,8 +13,7 @@ function unique( arr ) { Release.define({ _gatherContributors: function( callback ) { - var contributors, - contributorsPath = Release.dir.base + "/contributors.txt"; + var contributorsPath = Release.dir.base + "/contributors.txt"; console.log( "Adding reporters and commenters from issues..." ); Release._gatherIssueContributors(function( contributors ) { From 69f9fafeba70d6961351c048679fde2304d0ebb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Scott=20Gonz=C3=A1lez?= Date: Tue, 17 Mar 2015 20:01:07 -0400 Subject: [PATCH 4/5] [fixup] style --- lib/github.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/github.js b/lib/github.js index 031199c..03718d3 100644 --- a/lib/github.js +++ b/lib/github.js @@ -63,9 +63,11 @@ Release.define({ "#" + issue.number, component, issue.title - ].sort(function( a, b ) { - return a.component > b.component ? 1 : -1; - }).join( "\t" ); + ] + .sort(function( a, b ) { + return a.component > b.component ? 1 : -1; + }) + .join( "\t" ); }) // Concatenate the issues into a string From 519617d51c242f2a8e58580e6ae23bc38986ed64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Scott=20Gonz=C3=A1lez?= Date: Mon, 23 Mar 2015 12:01:01 -0400 Subject: [PATCH 5/5] [fixup]: Upgrade github-request --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 62d2311..03343f0 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "dependencies": { "chalk": "1.0.0", "changelogplease": "1.1.1", - "github-request": "1.2.2", + "github-request": "1.2.3", "semver": "2.2.1", "shelljs": "0.2.6", "which": "1.0.5"