├── .gitignore ├── README.md ├── endpoints ├── assignees.js ├── collaborators.js ├── comments.js ├── graphql.js ├── issues.js ├── labels.js ├── login.js ├── organizations.js ├── pulls.js ├── repository.js ├── search.js ├── teams.js ├── users.js └── webhooks.js ├── index.js ├── package-lock.json ├── package.json └── test ├── assignees.test.js ├── comments.test.js ├── fixtures └── single-file │ └── README.md ├── graphql.test.js ├── hulk.js ├── issues.test.js ├── repository.test.js ├── search.test.js └── webhooks.test.js /.gitignore: -------------------------------------------------------------------------------- 1 | test.js 2 | node_modules 3 | npm-debug.log 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Githulk 2 | 3 | GitHulk is a developer friendly API client for the Github API. In addition to 4 | providing access to Github's official API's it also implements new methods that 5 | are either convenience functions or a composition of multiple API calls which will 6 | make certain actions easier or more natural. 7 | 8 | ## Installation 9 | 10 | This module is released in to the npm registry as `githulk`: 11 | 12 | ``` 13 | npm install --save githulk 14 | ``` 15 | 16 | ## Usage 17 | 18 | The GitHulk module is exposed as a single function (constructor) and can 19 | therefor be required as: 20 | 21 | ```js 22 | 'use strict'; 23 | 24 | var GitHulk = require('githulk'); 25 | ``` 26 | 27 | To initialise a new GitHulk instance simply construct it using: 28 | 29 | ```js 30 | var githulk = new GitHulk(/* optional options */); 31 | ``` 32 | 33 | As you've might have noticed from the function call above, it accepts an 34 | optional options object. The following options can be provided: 35 | 36 | - `url`: The API endpoint of the Github API (with trailing slash). 37 | - `maxdelay`: Maximum delay for exponential back off. 38 | - `mindelay`: Minimum delay for exponential back off. 39 | - `retries`: Amount of retries before finally giving up. 40 | - `factor`: Exponential back off factor. 41 | - `cache`: A cache for conditional lookups. 42 | - `user`: A Github username (see Authorization) 43 | - `password`: A Github password (see Authorization) 44 | - `token`: A oauth token (see Authorization) 45 | 46 | ### Authorization 47 | 48 | If no options are supplied your API calls will be severely limited by GitHub and 49 | a really low rate limiting of 60 API calls per hour will be in place. In order 50 | to authorize the GitHulk instance you can provide it with: 51 | 52 | - `token`: An oauth token for your application. 53 | - `user`, `password`: The user and password of your account (for basic auth). 54 | - `authorization`: A predefined authorization header for each request. 55 | 56 | In addition to supplying your authorization details through the constructor of 57 | the function we also support them through `ENV` variables. Set the 58 | `GITHULK_TOKEN` or `GITHUB_TOKEN` variable with your oauth token and we will use 59 | that automatically. 60 | 61 | ### Arguments 62 | 63 | All API endpoints follow the same function signature, the order in which they 64 | are provided does not matter. The only argument that is optional is the 65 | `options` argument. 66 | 67 | - `project`: An **string** that has the username/repo combination. 68 | - `options`: An **object** with additional configuration for the API method. 69 | - `fn`: An **function** which is called with an error first callback pattern. 70 | 71 | The following API endpoints are implemented, they are currently implemented on 72 | a need to have basis: 73 | 74 | ### .repository 75 | 76 | - `githulk.repository.contents` 77 | - `githulk.repository.readme` 78 | - `githulk.repository.raw` 79 | - `githulk.repository.moved` 80 | 81 | ## License 82 | 83 | MIT 84 | -------------------------------------------------------------------------------- /endpoints/assignees.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var debug = require('diagnostics')('githulk:assignees'); 4 | 5 | /** 6 | * List available assignees. 7 | * 8 | * @param {Mana} api The actual API instance. 9 | * @api private 10 | */ 11 | function Assignees(api) { 12 | this.send = api.send.bind(api); 13 | this.api = api; 14 | } 15 | 16 | /** 17 | * Get a list of users that can get assigned. 18 | * 19 | * @param {String} project User/repo combination. 20 | * @param {Options} options Optional options. 21 | * @param {Function} fn The callback. 22 | * @api public 23 | */ 24 | Assignees.prototype.list = function list(args) { 25 | args = this.api.args(arguments); 26 | 27 | var project = this.api.project(args.str) 28 | , options = args.options || {}; 29 | 30 | return this.send( 31 | ['repos', project.user, project.repo, 'assignees'], 32 | this.api.options(options), 33 | args.fn 34 | ); 35 | }; 36 | 37 | /** 38 | * Check if a given assignee is allowed for the repository. 39 | * 40 | * @param {String} project User/repo combination. 41 | * @param {Options} options Optional options. 42 | * @param {Function} fn The callback. 43 | * @api public 44 | */ 45 | Assignees.prototype.assignee = function assignee(args) { 46 | args = this.api.args(arguments); 47 | 48 | var project = this.api.project(args.str) 49 | , options = args.options || {}; 50 | 51 | return this.send( 52 | ['repos', project.user, project.repo, 'assignees', options.assignee], 53 | this.api.options(options), 54 | args.fn 55 | ); 56 | }; 57 | 58 | // 59 | // Expose the assignees API. 60 | // 61 | module.exports = Assignees; 62 | -------------------------------------------------------------------------------- /endpoints/collaborators.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var debug = require('diagnostics')('githulk:collaborators'); 4 | 5 | /** 6 | * Collaborators API endpoint. 7 | * 8 | * @constructor 9 | * @param {Mana} api The actual API instance. 10 | * @api private 11 | */ 12 | function Collaborators(api) { 13 | this.send = api.send.bind(api); 14 | this.options = api.options; 15 | this.api = api; 16 | } 17 | 18 | /** 19 | * Get collaborator information for a username/repo. 20 | * 21 | * @param {String} project The project details. 22 | * @param {Object} options Optional options. 23 | * @param {function} fn The callback. 24 | * @returns {Assign} 25 | * @api public 26 | */ 27 | Collaborators.prototype.get = function get(args) { 28 | args = this.api.args(arguments); 29 | args.options = this.options(args.options); 30 | 31 | var project = this.api.project(args.str); 32 | 33 | return this.send( 34 | ['repos', project.user, project.repo, 'collaborators'], 35 | args.options, 36 | args.fn 37 | ); 38 | }; 39 | 40 | /** 41 | * Add a collaborator to the owner/repo given 42 | * 43 | * @param {String} project 44 | * @param {Object} Options with username/user. 45 | * @returns {Assign} 46 | * @api public 47 | */ 48 | Collaborators.prototype.add = function add(args) { 49 | args = this.api.args(arguments); 50 | args.options = this.options(args.options); 51 | 52 | var user = args.options.username || args.options.user; 53 | 54 | if (!user) return args.fn(new Error('User is a required options')); 55 | 56 | var project = this.api(args.str); 57 | 58 | return this.send( 59 | ['repos', project.user, project.repo, 'collaborators', user], 60 | args.options, 61 | args.fn 62 | ); 63 | }; 64 | 65 | // 66 | // Expose the Collaborators API. 67 | // 68 | module.exports = Collaborators; 69 | -------------------------------------------------------------------------------- /endpoints/comments.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var debug = require('diagnostics')('githulk:comments'); 4 | 5 | /** 6 | * Issues API endpoint. 7 | * 8 | * @param {Mana} api The actual API instance. 9 | * @api private 10 | */ 11 | function Comments(api) { 12 | this.send = api.send.bind(api); 13 | this.api = api; 14 | } 15 | 16 | /** 17 | * List all comments of an issue. 18 | * 19 | * @param {String} project User/repo combination. 20 | * @param {Number} number The issue number. 21 | * @param {Object} options Optional options. 22 | * @param {Function} fn The callback. 23 | * @returns {Assign} 24 | * @api public 25 | */ 26 | Comments.prototype.list = function list(args) { 27 | args = this.api.args(arguments); 28 | 29 | var project = this.api.project(args.str) 30 | , options = args.options || {}; 31 | 32 | return this.send( 33 | ['repos', project.user, project.repo, 'issues', args.number, 'comments'], 34 | this.api.options(options), 35 | args.fn 36 | ); 37 | }; 38 | 39 | /** 40 | * Get all issues for the given repository. 41 | * 42 | * @param {String} project User/repo combination. 43 | * @param {Object} options Optional options. 44 | * @param {Function} fn The callback. 45 | * @returns {Assign} 46 | * @api public 47 | */ 48 | Comments.prototype.repository = function repository(args) { 49 | args = this.api.args(arguments); 50 | 51 | var project = this.api.project(args.str) 52 | , options = args.options || {}; 53 | 54 | return this.send( 55 | ['repos', project.user, project.repo, 'issues', 'comments'], 56 | this.api.options(options, [ 57 | 'direction', 58 | 'since', 59 | 'sort' 60 | ]), 61 | args.fn 62 | ); 63 | }; 64 | 65 | /** 66 | * Get a single comment 67 | * 68 | * @param {String} project User/repo combination. 69 | * @param {Number} number The comment id. 70 | * @param {Object} options Optional options. 71 | * @param {Function} fn The callback. 72 | * @returns {Assign} 73 | * @api public 74 | */ 75 | Comments.prototype.get = function get(args) { 76 | args = this.api.args(arguments); 77 | 78 | var project = this.api.project(args.str) 79 | , options = args.options || {}; 80 | 81 | return this.send( 82 | ['repos', project.user, project.repo, 'issues', 'comments', args.number], 83 | this.api.options(options), 84 | args.fn 85 | ); 86 | }; 87 | 88 | /** 89 | * Create a new issue. 90 | * 91 | * @param {String} project User/repo combination. 92 | * @param {Number} number The issue number. 93 | * @param {Object} options Optional options. 94 | * @param {Function} fn The callback. 95 | * @returns {Assign} 96 | * @api public 97 | */ 98 | Comments.prototype.create = function create(args) { 99 | args = this.api.args(arguments); 100 | 101 | var project = this.api.project(args.str) 102 | , options = args.options || {}; 103 | 104 | return this.send( 105 | ['repos', project.user, project.repo, 'issues', args.number, 'comments'], 106 | this.api.options(this.api.merge(options, { method: 'POST' }), ['body']), 107 | args.fn 108 | ); 109 | }; 110 | 111 | /** 112 | * Edit a single issue. 113 | * 114 | * @param {String} project User/repo combination. 115 | * @param {Number} number The comment id. 116 | * @param {Object} options Optional options. 117 | * @param {Function} fn The callback. 118 | * @returns {Assign} 119 | * @api public 120 | */ 121 | Comments.prototype.edit = function create(args) { 122 | args = this.api.args(arguments); 123 | 124 | var project = this.api.project(args.str) 125 | , options = args.options || {}; 126 | 127 | return this.send( 128 | ['repos', project.user, project.repo, 'issues', 'comments', args.number], 129 | this.api.options(this.api.merge(options, { method: 'PATCH' }), ['body']), 130 | args.fn 131 | ); 132 | }; 133 | 134 | /** 135 | * Remove a single issue. 136 | * 137 | * @param {String} project User/repo combination. 138 | * @param {Number} number The comment id. 139 | * @param {Object} options Optional options. 140 | * @param {Function} fn The callback. 141 | * @returns {Assign} 142 | * @api public 143 | */ 144 | Comments.prototype.remove = function remove(args) { 145 | args = this.api.args(arguments); 146 | 147 | var project = this.api.project(args.str) 148 | , options = args.options || {}; 149 | 150 | return this.send( 151 | ['repos', project.user, project.repo, 'issues', 'comments', args.number], 152 | this.api.options(this.api.merge(options, { method: 'DELETE' })), 153 | args.fn 154 | ); 155 | }; 156 | 157 | // 158 | // Expose the comments API. 159 | // 160 | module.exports = Comments; 161 | -------------------------------------------------------------------------------- /endpoints/graphql.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var debug = require('diagnostics')('githulk:graphql'); 4 | 5 | /** 6 | * GraphQL API endpoint. 7 | * 8 | * @param {Mana} api The actual API instance. 9 | * @api private 10 | */ 11 | function GraphQL(api) { 12 | this.send = api.manaql.send.bind(api.manaql); 13 | this.api = api; 14 | } 15 | 16 | /** 17 | * Dispatch a GraphQL query 18 | * 19 | * @param {String} query The query 20 | * @param {function} fn The callback. 21 | * @returns {Assign} 22 | * @api public 23 | */ 24 | GraphQL.prototype.sendQuery = function sendQuery(args) { 25 | args = this.api.args(arguments); 26 | args.options = args.options || {}; 27 | 28 | var query = args.options.query || '{ \n }'; 29 | 30 | if(!~query.indexOf('rateLimit')) { 31 | var queryEnd = query.lastIndexOf('}'); 32 | var rateLimitFrag = '\n fragment rateLimit on Query { rateLimit { limit cost remaining resetAt } }'; 33 | 34 | query = query.slice(0, queryEnd) + ' ...rateLimit \n' + query.slice(queryEnd); 35 | query += rateLimitFrag; 36 | } 37 | 38 | args.options.method = 'POST'; 39 | args.options.params = args.options.params || {}; 40 | args.options.params.query = query; 41 | args.options.query = query; 42 | 43 | return this.send( 44 | ['graphql'], 45 | args.options, 46 | function handler(err, results) { 47 | if (err) return args.fn(err); 48 | 49 | args.fn(null, results.length ? results[0] : null); 50 | }); 51 | }; 52 | 53 | // 54 | // Expose the GraphQL API. 55 | // 56 | module.exports = GraphQL; 57 | -------------------------------------------------------------------------------- /endpoints/issues.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var debug = require('diagnostics')('githulk:issues'); 4 | 5 | /** 6 | * Issues API endpoint. 7 | * 8 | * @param {Mana} api The actual API instance. 9 | * @api private 10 | */ 11 | function Issues(api) { 12 | this.send = api.send.bind(api); 13 | this.api = api; 14 | } 15 | 16 | /** 17 | * All the available options for listing issues. 18 | * 19 | * @type {Array} 20 | * @private 21 | */ 22 | Issues.params = [ 23 | 'assignee', // Assigned to which user. 24 | 'creator', // Created by which user. 25 | 'direction', // Sort direction. 26 | 'filter', // Filter issues. 27 | 'lables', // Contains these labels. 28 | 'mentioned', // Mentions user. 29 | 'milestone', // List issues for milestone> 30 | 'since', // Issues created since. 31 | 'sort', // Sort on. 32 | 'state' // Issue state. 33 | ]; 34 | 35 | /** 36 | * All the available options for creating and modifying an issue. 37 | * 38 | * @type {Array} 39 | * @private 40 | */ 41 | Issues.create = [ 42 | 'assignee', // Login for the user that this issue should be assigned to. 43 | 'body', // The contents of the issue. 44 | 'labels', // Labels to associate with this issue. 45 | 'milestone', // Milestone to associate this issue with. 46 | 'state', // Issue state. 47 | 'title' // The title of the issue. 48 | ]; 49 | 50 | /** 51 | * Get all issues (repos, orgs etc), for the authenticated user. 52 | * 53 | * @param {Object} options Optional options. 54 | * @param {function} fn The callback. 55 | * @returns {Assign} 56 | * @api public 57 | */ 58 | Issues.prototype.list = function list(args) { 59 | args = this.api.args(arguments); 60 | 61 | var options = args.options || {}; 62 | 63 | return this.send( 64 | ['issues'], 65 | this.api.options(options, Issues.params), 66 | args.fn 67 | ); 68 | }; 69 | 70 | /** 71 | * List all issues for a given user name. 72 | * 73 | * @param {String} org The user name. 74 | * @param {Object} options Optional options. 75 | * @param {Function} fn The callback. 76 | * @returns {Assign} 77 | * @api public 78 | */ 79 | Issues.prototype.user = function user(args) { 80 | args = this.api.args(arguments); 81 | 82 | var options = args.options || {}; 83 | 84 | return this.send( 85 | ['user', 'issues'], 86 | this.api.options(options, Issues.params), 87 | args.fn 88 | ); 89 | }; 90 | 91 | /** 92 | * List all issues for a given organization. 93 | * 94 | * @param {String} org The organization 95 | * @param {Object} options Optional options. 96 | * @param {Function} fn The callback. 97 | * @returns {Assign} 98 | * @api public 99 | */ 100 | Issues.prototype.organization = function organization(args) { 101 | args = this.api.args(arguments); 102 | 103 | var options = args.options || {}; 104 | 105 | return this.send( 106 | ['orgs', args.string, 'issues'], 107 | this.api.options(options, Issues.params), 108 | args.fn 109 | ); 110 | }; 111 | 112 | /** 113 | * List all issues for a given repository. 114 | * 115 | * @param {String} org The organization 116 | * @param {Object} options Optional options. 117 | * @param {Function} fn The callback. 118 | * @returns {Assign} 119 | * @api public 120 | */ 121 | Issues.prototype.repository = function repository(args) { 122 | args = this.api.args(arguments); 123 | 124 | var project = this.api.project(args.str) 125 | , options = args.options || {}; 126 | 127 | return this.send( 128 | ['repos', project.user, project.repo, 'issues'], 129 | this.api.options(options, Issues.params), 130 | args.fn 131 | ); 132 | }; 133 | 134 | /** 135 | * Get a single issue. 136 | * 137 | * @param {String} project User/repo combination. 138 | * @param {Number} number The issue number. 139 | * @param {Object} options Optional options. 140 | * @param {Function} fn The callback. 141 | * @returns {Assign} 142 | * @api public 143 | */ 144 | Issues.prototype.get = function get(args) { 145 | args = this.api.args(arguments); 146 | 147 | var project = this.api.project(args.str) 148 | , options = args.options || {}; 149 | 150 | return this.send( 151 | ['repos', project.user, project.repo, 'issues', args.number], 152 | this.api.options(options), 153 | args.fn 154 | ); 155 | }; 156 | 157 | /** 158 | * Create a new issue. 159 | * 160 | * @param {String} project User/repo combination. 161 | * @param {Object} options Optional options. 162 | * @param {Function} fn The callback. 163 | * @returns {Assign} 164 | * @api public 165 | */ 166 | Issues.prototype.create = function create(args) { 167 | args = this.api.args(arguments); 168 | 169 | var project = this.api.project(args.str) 170 | , options = args.options || {}; 171 | 172 | return this.send( 173 | ['repos', project.user, project.repo, 'issues'], 174 | this.api.options(this.api.merge(options, { method: 'POST' }), Issues.create), 175 | args.fn 176 | ); 177 | }; 178 | 179 | /** 180 | * Edit a single issue. 181 | * 182 | * @param {String} project User/repo combination. 183 | * @param {Number} number The issue number. 184 | * @param {Object} options Optional options. 185 | * @param {Function} fn The callback. 186 | * @returns {Assign} 187 | * @api public 188 | */ 189 | Issues.prototype.edit = function edit(args) { 190 | args = this.api.args(arguments); 191 | 192 | var project = this.api.project(args.str) 193 | , options = args.options || {}; 194 | 195 | return this.send( 196 | ['repos', project.user, project.repo, 'issues', args.number], 197 | this.api.options(this.api.merge(options, { method: 'PATCH' }), Issues.create), 198 | args.fn 199 | ); 200 | }; 201 | 202 | // 203 | // Expose the issues API. 204 | // 205 | module.exports = Issues; 206 | -------------------------------------------------------------------------------- /endpoints/labels.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var debug = require('diagnostics')('githulk:labels'); 4 | 5 | /** 6 | * Labels API endpoint. 7 | * 8 | * @param {Mana} api The actual API instance. 9 | * @api private 10 | */ 11 | function Labels(api) { 12 | this.send = api.send.bind(api); 13 | this.api = api; 14 | } 15 | 16 | /** 17 | * Get all labels for the given project. 18 | * 19 | * @param {String} project The User/repo combination. 20 | * @param {Object} options Optional options. 21 | * @param {Function} fn The callback. 22 | * @api public 23 | */ 24 | Labels.prototype.list = function list(args) { 25 | args = this.api.args(arguments); 26 | 27 | var project = this.api.project(args.str) 28 | , options = args.options || {}; 29 | 30 | return this.send( 31 | ['repos', project.user, project.repo, 'labels'], 32 | this.api.options(options), 33 | args.fn 34 | ); 35 | }; 36 | 37 | /** 38 | * Get a single label. 39 | * 40 | * @param {String} project The User/repo combination. 41 | * @param {Object} options Optional options. 42 | * @param {Function} fn The callback. 43 | * @api public 44 | */ 45 | Labels.prototype.get = function get(args) { 46 | args = this.api.args(arguments); 47 | 48 | var project = this.api.project(args.str) 49 | , options = args.options || {}; 50 | 51 | return this.send( 52 | ['repos', project.user, project.repo, 'labels', options.label], 53 | this.api.options(options), 54 | args.fn 55 | ); 56 | }; 57 | 58 | /** 59 | * Create a new label. 60 | * 61 | * @param {String} project The User/repo combination. 62 | * @param {Object} options Optional options. 63 | * @param {Function} fn The callback. 64 | * @api public 65 | */ 66 | Labels.prototype.create = function create(args) { 67 | args = this.api.args(arguments); 68 | 69 | var project = this.api.project(args.str) 70 | , options = args.options || {}; 71 | 72 | return this.send( 73 | ['repos', project.user, project.repo, 'labels', this.qs(options, [ 74 | 'name', // Name of the label. 75 | 'color' // Color of the label. 76 | ])], 77 | this.api.options(this.api.merge(options, { method: 'POST' })), 78 | args.fn 79 | ); 80 | }; 81 | 82 | /** 83 | * Remove a label. 84 | * 85 | * @param {String} project The User/repo combination. 86 | * @param {Object} options Optional options. 87 | * @param {Function} fn The callback. 88 | * @api public 89 | */ 90 | Labels.prototype.remove = function remove(args) { 91 | args = this.api.args(arguments); 92 | 93 | var project = this.api.project(args.str) 94 | , options = args.options || {}; 95 | 96 | return this.send( 97 | ['repos', project.user, project.repo, 'labels', options.label], 98 | this.api.options(this.api.merge(options, { method: 'PATCH' })), 99 | args.fn 100 | ); 101 | }; 102 | 103 | // 104 | // Expose the labels API. 105 | // 106 | module.exports = Labels; 107 | -------------------------------------------------------------------------------- /endpoints/login.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var debug = require('diagnostics')('githulk:labels') 4 | , crypto = require('crypto') 5 | , url = require('url'); 6 | 7 | /** 8 | * Expose a login API endpoint. 9 | * 10 | * @param {Mana} api The actual API instance. 11 | * @api private 12 | */ 13 | function Login(api) { 14 | this.qs = api.querystring.bind(api); 15 | this.send = api.send.bind(api); 16 | this.api = api; 17 | } 18 | 19 | /** 20 | * Transform a response into an GitHub authorization hook 21 | * 22 | * @param {Response} res HTTP Response instance to answer. 23 | * @param {String} client The application client id. 24 | * @param {Array} scopes The OAuth access scopes. 25 | * @returns {String} The state. 26 | * @api public 27 | */ 28 | Login.prototype.authorize = function authorize(res, client, scopes, redirect) { 29 | var state = crypto.randomBytes(8).toString('hex') 30 | , endpoint = 'https://github.com/login/oauth/authorize'+ this.qs({ 31 | scope: (Array.isArray(scopes) ? scopes : [scopes]).join(','), 32 | redirect_url: redirect, 33 | client_id: client, 34 | state: state 35 | }, ['client_id','scope', 'redirect_uri', 'state']); 36 | 37 | // 38 | // Optionally set store the `state` in the session so we can re-use it for the 39 | // callback. 40 | // 41 | if (res.session) res.session.oauth_state = state; 42 | 43 | if (res.redirect) { 44 | res.redirect(endpoint, 302); 45 | } else { 46 | res.setHeader('Location', endpoint); 47 | res.statusCode = 302; 48 | res.end(''); 49 | } 50 | 51 | return state; 52 | }; 53 | 54 | /** 55 | * Process the OAuth callback from GitHub. 56 | * 57 | * @param {Request} req The incoming HTTP request. 58 | * @param {String} client The application client id. 59 | * @param {String} secret The application client secret. 60 | * @param {String} state The returned state token from the .authorize method. 61 | * @param {Function} fn The callback. 62 | * @returns {Assign} 63 | * @api public 64 | */ 65 | Login.prototype.callback = function callback(req, client, secret, state, fn) { 66 | var data = url.parse(req.url, true).query 67 | , err; 68 | 69 | if ('function' === typeof state && req.session) { 70 | fn = state; 71 | state = req.session.oauth_state; 72 | } 73 | 74 | if (data.error) { 75 | err = new Error(data.error_description); 76 | err.url = data.error_uri; 77 | err.error = data.error; 78 | } else if (!data.code) { 79 | err = new Error('Missing OAuth code.'); 80 | } 81 | 82 | if (err) return this.bail(fn, err); 83 | 84 | return this.send( 85 | ['login', 'oauth', 'access_token'], 86 | { 87 | params: ['client_id', 'client_secret', 'code', 'state'], 88 | client_id: client, 89 | client_secret: secret, 90 | code: data.code, 91 | state: state 92 | }, 93 | fn 94 | ); 95 | }; 96 | 97 | // 98 | // Expose the login API. 99 | // 100 | module.exports = Login; 101 | -------------------------------------------------------------------------------- /endpoints/organizations.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var debug = require('diagnostics')('githulk:organization'); 4 | 5 | /** 6 | * Organization API endpoint. 7 | * 8 | * @param {Mana} api The actual API instance. 9 | * @api private 10 | */ 11 | function Organization(api) { 12 | this.send = api.send.bind(api); 13 | this.api = api; 14 | } 15 | 16 | /** 17 | * Get organization information for a given org. 18 | * 19 | * @param {String} project The project details. 20 | * @param {Object} options Optional options. 21 | * @param {function} fn The callback. 22 | * @returns {Assign} 23 | * @api public 24 | */ 25 | Organization.prototype.get = function get(args) { 26 | args = this.api.args(arguments); 27 | 28 | var project = this.api.project(args.str) || {}; 29 | 30 | return this.send( 31 | ['orgs', project.user || args.str], 32 | this.api.options(args.options || {}), 33 | args.fn 34 | ); 35 | }; 36 | 37 | /** 38 | * List all organizations for the authenticated user. 39 | * 40 | * @param {Object} options Optional options. 41 | * @param {function} fn The callback. 42 | * @returns {Assign} 43 | * @api public 44 | */ 45 | Organization.prototype.list = function list(args) { 46 | args = this.api.args(arguments); 47 | 48 | return this.send( 49 | ['user', 'orgs'], 50 | this.api.options(args.options || {}), 51 | args.fn 52 | ); 53 | }; 54 | 55 | /** 56 | * Get public member information for a given org. 57 | * 58 | * @param {String} project The project details. 59 | * @param {Object} options Optional options. 60 | * @param {function} fn The callback. 61 | * @returns {Assign} 62 | * @api public 63 | */ 64 | Organization.prototype.publicMembers = function publicMembers(args) { 65 | args = this.api.args(arguments); 66 | 67 | var project = this.api.project(args.str) || {}; 68 | 69 | return this.send( 70 | ['orgs', project.user || args.str, 'public_members'], 71 | this.api.options(args.options || {}), 72 | args.fn 73 | ); 74 | }; 75 | 76 | // 77 | // Expose the organization API. 78 | // 79 | module.exports = Organization; 80 | -------------------------------------------------------------------------------- /endpoints/pulls.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var debug = require('diagnostics')('githulk:pullrequest'); 4 | 5 | /** 6 | * Pull Request API endpoint. 7 | * 8 | * @param {Mana} api The actual API instance. 9 | * @api private 10 | */ 11 | function Pulls(api) { 12 | this.send = api.send.bind(api); 13 | this.api = api; 14 | } 15 | 16 | /** 17 | * All the available options for listing pull requests. 18 | * 19 | * @type {Array} 20 | * @private 21 | */ 22 | Pulls.params = [ 23 | 'base', // Filter pulls by base branch name. 24 | 'direction', // The direction of the sort. Can be either asc or desc. 25 | 'head', // Filter pulls by head user and branch name. 26 | 'sort', // What to sort results by. 27 | 'state' // Either open, closed, or all to filter by state. 28 | ]; 29 | 30 | /** 31 | * All available options for creating a new pull request. 32 | * 33 | * @type {Array} 34 | * @private 35 | */ 36 | Pulls.create = [ 37 | 'base', // The name of the branch you want your changes pulled into. 38 | 'body', // The contents of the pull request. 39 | 'head', // The name of the branch where your changes are implemented. 40 | 'issue', // The issue number in this repository to turn into a Pull Request. 41 | 'state', // Either open, closed. 42 | 'title' // The title of the pull request. 43 | ]; 44 | 45 | /** 46 | * All available options for requesting pull request review. 47 | * 48 | * @type {Array} 49 | * @private 50 | */ 51 | Pulls.requestReviewers = [ 52 | 'reviewers', // An array of reviewer users to request review from 53 | 'team_reviewers' // An array of team slugs to request review from 54 | ]; 55 | 56 | /** 57 | * List pull requests. 58 | * 59 | * @param {Object} options Optional options. 60 | * @param {function} fn The callback. 61 | * @returns {Assign} 62 | * @api public 63 | */ 64 | Pulls.prototype.list = function list(args) { 65 | args = this.api.args(arguments); 66 | 67 | var project = this.api.project(args.str) 68 | , options = args.options || {}; 69 | 70 | return this.send( 71 | ['repos', project.user, project.repo, 'pulls'], 72 | this.api.options(options, Pulls.params), 73 | args.fn 74 | ); 75 | }; 76 | 77 | /** 78 | * Get a single pull request 79 | * 80 | * @param {String} project User/repo combination. 81 | * @param {Number} number The pull request number. 82 | * @param {Object} options Optional options. 83 | * @param {Function} fn The callback. 84 | * @returns {Assign} 85 | * @api public 86 | */ 87 | Pulls.prototype.get = function get(args) { 88 | args = this.api.args(arguments); 89 | 90 | var project = this.api.project(args.str) 91 | , options = args.options || {}; 92 | 93 | return this.send( 94 | ['repos', project.user, project.repo, 'pulls', args.number], 95 | this.api.options(options), 96 | args.fn 97 | ); 98 | }; 99 | 100 | /** 101 | * Create a new pull request. 102 | * 103 | * @param {String} project User/repo combination. 104 | * @param {Object} options Optional options. 105 | * @param {Function} fn The callback. 106 | * @returns {Assign} 107 | * @api public 108 | */ 109 | Pulls.prototype.create = function create(args) { 110 | args = this.api.args(arguments); 111 | 112 | var project = this.api.project(args.str) 113 | , options = args.options || {}; 114 | 115 | return this.send( 116 | ['repos', project.user, project.repo, 'pulls'], 117 | this.api.options(this.api.merge(options, { method: 'POST' }), Pulls.create), 118 | args.fn 119 | ); 120 | }; 121 | 122 | /** 123 | * Edit a single pull request. 124 | * 125 | * @param {String} project User/repo combination. 126 | * @param {Number} number The pull request number. 127 | * @param {Object} options Optional options. 128 | * @param {Function} fn The callback. 129 | * @returns {Assign} 130 | * @api public 131 | */ 132 | Pulls.prototype.edit = function edit(args) { 133 | args = this.api.args(arguments); 134 | 135 | var project = this.api.project(args.str) 136 | , options = args.options || {}; 137 | 138 | return this.send( 139 | ['repos', project.user, project.repo, 'pulls', args.number], 140 | this.api.options(this.api.merge(options, { method: 'PATCH' }), Pulls.create), 141 | args.fn 142 | ); 143 | }; 144 | 145 | /** 146 | * List all commits on the pull request. 147 | * 148 | * @param {String} project User/repo combination. 149 | * @param {Number} number The pull request number. 150 | * @param {Object} options Optional options. 151 | * @param {Function} fn The callback. 152 | * @returns {Assign} 153 | * @api public 154 | */ 155 | Pulls.prototype.commits = function commits(args) { 156 | args = this.api.args(arguments); 157 | 158 | var project = this.api.project(args.str) 159 | , options = args.options || {}; 160 | 161 | return this.send( 162 | ['repos', project.user, project.repo, 'pulls', args.number, 'commits'], 163 | this.api.options(options), 164 | args.fn 165 | ); 166 | }; 167 | 168 | /** 169 | * List all files on the pull request. 170 | * 171 | * @param {String} project User/repo combination. 172 | * @param {Number} number The pull request number. 173 | * @param {Object} options Optional options. 174 | * @param {Function} fn The callback. 175 | * @returns {Assign} 176 | * @api public 177 | */ 178 | Pulls.prototype.files = function files(args) { 179 | args = this.api.args(arguments); 180 | 181 | var project = this.api.project(args.str) 182 | , options = args.options || {}; 183 | 184 | return this.send( 185 | ['repos', project.user, project.repo, 'pulls', args.number, 'files'], 186 | this.api.options(options), 187 | args.fn 188 | ); 189 | }; 190 | 191 | /** 192 | * Check if the pull request has been merged. 193 | * 194 | * @param {String} project User/repo combination. 195 | * @param {Number} number The pull request number. 196 | * @param {Object} options Optional options. 197 | * @param {Function} fn The callback. 198 | * @returns {Assign} 199 | * @api public 200 | */ 201 | Pulls.prototype.merged = function merge(args) { 202 | args = this.api.args(arguments); 203 | 204 | var project = this.api.project(args.str) 205 | , options = args.options || {}; 206 | 207 | return this.send( 208 | ['repos', project.user, project.repo, 'pulls', args.number, 'merge'], 209 | this.api.options(options), 210 | args.fn 211 | ); 212 | }; 213 | 214 | /** 215 | * Merge the given pull request. 216 | * 217 | * @param {String} project User/repo combination. 218 | * @param {Number} number The pull request number. 219 | * @param {Object} options Optional options. 220 | * @param {Function} fn The callback. 221 | * @returns {Assign} 222 | * @api public 223 | */ 224 | Pulls.prototype.merge = function merge(args) { 225 | args = this.api.args(arguments); 226 | 227 | var project = this.api.project(args.str) 228 | , options = args.options || {}; 229 | 230 | return this.send( 231 | ['repos', project.user, project.repo, 'pulls', args.number, 'merge'], 232 | this.api.options(this.api.merge(options, { method: 'PUT' })), 233 | args.fn 234 | ); 235 | }; 236 | 237 | /** 238 | * Request review from the specified set of users on the given pull request. 239 | * 240 | * @param {String} project User/repo combination. 241 | * @param {Number} number The pull request number. 242 | * @param {Object} options Optional options. 243 | * @param {String[]} options.reviewers List of usernames to request review from 244 | * @param {String[]} options.team_reviewers List of team slugs to request review from 245 | * @param {Function} fn The callback. 246 | * @returns {Assign} 247 | * @api public 248 | */ 249 | Pulls.prototype.requestReviewers = function requestReviewers(args) { 250 | args = this.api.args(arguments); 251 | 252 | var project = this.api.project(args.str) 253 | , options = args.options || {}; 254 | 255 | return this.send( 256 | ['repos', project.user, project.repo, 'pulls', args.number, 'requested_reviewers'], 257 | this.api.options(this.api.merge(options, { method: 'POST' }), Pulls.requestReviewers), 258 | args.fn 259 | ); 260 | }; 261 | 262 | // 263 | // Expose the issues API. 264 | // 265 | module.exports = Pulls; 266 | -------------------------------------------------------------------------------- /endpoints/repository.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var debug = require('diagnostics')('githulk:repository'); 4 | var unwrap = require('unwrapper'); 5 | 6 | /** 7 | * Repositories API endpoint. 8 | * 9 | * @constructor 10 | * @param {Mana} api The actual API instance. 11 | * @api private 12 | */ 13 | function Repository(api) { 14 | this.send = api.send.bind(api); 15 | this.options = api.options; 16 | this.api = api; 17 | } 18 | 19 | /** 20 | * List all repositories for the given user. 21 | * 22 | * @param {String} project The project details. 23 | * @param {Object} options Optional options. 24 | * @param {function} fn The callback. 25 | * @returns {Assign} 26 | * @api public 27 | */ 28 | Repository.prototype.list = function list(args) { 29 | args = this.api.args(arguments); 30 | args.options = this.options(args.options, [ 31 | 'type', 'sort', 'direction' 32 | ]); 33 | 34 | var project = this.api.project(args.str); 35 | 36 | return this.send( 37 | [args.options.organization ? 'orgs' : 'users', project ? project.user : args.str, 'repos'], 38 | args.options, 39 | args.fn 40 | ); 41 | }; 42 | 43 | /** 44 | * List all the public repositories for the authorized user. 45 | * 46 | * @param {Object} options Optional options. 47 | * @param {function} fn The callback. 48 | * @returns {Assign} 49 | * @api public 50 | */ 51 | Repository.prototype.public = function publics(args) { 52 | args = this.api.args(arguments); 53 | args.options = this.options(args.options, [ 54 | 'since' 55 | ]); 56 | 57 | return this.send( 58 | '/repositories', 59 | args.options, 60 | args.fn 61 | ); 62 | }; 63 | 64 | /** 65 | * Get repository information for a given repo. 66 | * 67 | * @param {String} project The project details. 68 | * @param {Object} options Optional options. 69 | * @param {function} fn The callback. 70 | * @returns {Assign} 71 | * @api public 72 | */ 73 | Repository.prototype.get = function get(args) { 74 | args = this.api.args(arguments); 75 | args.options = this.options(args.options); 76 | 77 | var project = this.api.project(args.str); 78 | 79 | return this.send( 80 | ['repos', project.user, project.repo], 81 | args.options, 82 | args.fn 83 | ); 84 | }; 85 | 86 | /** 87 | * Get all branches for a given repo. 88 | * 89 | * @param {String} project The project details. 90 | * @param {Object} options Optional options. 91 | * @param {function} fn The callback. 92 | * @returns {Assign} 93 | * @api public 94 | */ 95 | Repository.prototype.branches = function branches(args) { 96 | args = this.api.args(arguments); 97 | args.options = this.options(args.options); 98 | 99 | var project = this.api.project(args.str); 100 | 101 | return this.send( 102 | ['repos', project.user, project.repo, 'branches'], 103 | args.options, 104 | args.fn 105 | ); 106 | }; 107 | 108 | /** 109 | * Get information about the specified branch in the specified repo. 110 | * 111 | * @param {String} project The project details. 112 | * @param {Object} options Optional options. 113 | * @param {Function} fn The callback. 114 | * @returns {Assign} 115 | * @api public 116 | */ 117 | Repository.prototype.branch = function branch(args) { 118 | args = this.api.args(arguments); 119 | var options = args.options = this.options(args.options); 120 | 121 | var project = this.api.project(args.str); 122 | 123 | return this.send( 124 | ['repos', project.user, project.repo, 'branches', options.branch], 125 | args.options, 126 | function handler(err, data) { 127 | if (err) return args.fn(err); 128 | 129 | args.fn(null, data.length && data.length > 0 ? data[0] : null); 130 | } 131 | ); 132 | }; 133 | 134 | /** 135 | * Get all tags for a given repo. 136 | * 137 | * @param {String} project The project details. 138 | * @param {Object} options Optional options. 139 | * @param {function} fn The callback. 140 | * @returns {Assign} 141 | * @api public 142 | */ 143 | Repository.prototype.tags = function tags(args) { 144 | args = this.api.args(arguments); 145 | args.options = this.options(args.options); 146 | 147 | var project = this.api.project(args.str); 148 | 149 | return this.send( 150 | ['repos', project.user, project.repo, 'git', 'refs', 'tags'], 151 | args.options, 152 | args.fn 153 | ); 154 | }; 155 | 156 | /** 157 | * Get the given tag for a given repo. 158 | * 159 | * @param {String} project The project details. 160 | * @param {Object} options Optional options. 161 | * @param {function} fn The callback. 162 | * @returns {Assign} 163 | * @api public 164 | */ 165 | Repository.prototype.tag = function tag(args) { 166 | args = this.api.args(arguments); 167 | var options = args.options = this.options(args.options); 168 | 169 | var project = this.api.project(args.str); 170 | 171 | return this.send( 172 | ['repos', project.user, project.repo, 'git', 'refs', 'tags', options.tag], 173 | args.options, 174 | unwrap(args.fn) 175 | ); 176 | }; 177 | 178 | /** 179 | * Get commits for a given repo. 180 | * 181 | * @param {String} project The project details. 182 | * @param {Object} options Optional options. 183 | * @param {function} fn The callback. 184 | * @returns {Assign} 185 | * @api public 186 | */ 187 | Repository.prototype.commits = function commits(args) { 188 | args = this.api.args(arguments); 189 | args.options = this.options(args.options); 190 | 191 | var project = this.api.project(args.str); 192 | 193 | return this.send( 194 | ['repos', project.user, project.repo, 'commits'], 195 | args.options, 196 | args.fn 197 | ); 198 | }; 199 | 200 | /** 201 | * Get the long SHA of a specific commit for a given repo 202 | * 203 | * @param {String} project The project details. 204 | * @param {Object} options Optional options. 205 | * @param {function} fn The callback. 206 | * @returns {Assign} 207 | * @api public 208 | */ 209 | Repository.prototype.commitSha = function commitSha(args) { 210 | args = this.api.args(arguments); 211 | var options = args.options = this.options(args.options); 212 | 213 | var project = this.api.project(args.str); 214 | 215 | return this.send( 216 | ['repos', project.user, project.repo, 'commits', options.sha], 217 | args.options, 218 | function handler(err, data) { 219 | if (err) return args.fn(err); 220 | 221 | args.fn(null, data.length && data.length > 0 ? data[0]['sha'] : null); 222 | } 223 | ); 224 | }; 225 | 226 | /** 227 | * Get the README contents of an project. 228 | * 229 | * @param {String} project The project details. 230 | * @param {Object} options Optional options. 231 | * @param {function} fn The callback. 232 | * @returns {Assign} 233 | * @api public 234 | */ 235 | Repository.prototype.readme = function readme(args) { 236 | args = this.api.args(arguments); 237 | 238 | var options = args.options = this.options(args.options, [ 'ref' ]) 239 | , project = this.api.project(args.str); 240 | 241 | options.headers = options.headers || {}; 242 | options.headers.Accept = this.api.accepts(options.headers.Accept || 'html'); 243 | 244 | return this.send( 245 | ['repos', project.user, project.repo, 'readme'], 246 | options, 247 | args.fn 248 | ); 249 | }; 250 | 251 | /** 252 | * Retrieve the raw contents of a file from the repository. 253 | * 254 | * @param {String} project The project details. 255 | * @param {Object} options Optional options. 256 | * @param {Function} fn The Callback. 257 | * @returns {Assign} 258 | * @api public 259 | */ 260 | Repository.prototype.raw = function raw(args) { 261 | args = this.api.args(arguments); 262 | 263 | var options = args.options = this.options(args.options) 264 | , project = this.api.project(args.str); 265 | 266 | options.headers = options.headers || {}; 267 | options.headers.Accept = options.headers.Accept || 'text/plain'; 268 | options.branch = options.branch || 'master'; 269 | options.api = 'https://raw.github.com/'; 270 | 271 | return this.send( 272 | [project.user, project.repo, options.branch, options.path], 273 | options, 274 | args.fn 275 | ); 276 | }; 277 | 278 | /** 279 | * Retrieve the contents of a file or directory. 280 | * 281 | * @param {String} project The project details. 282 | * @param {Object} options Optional options. 283 | * @param {Function} fn The Callback. 284 | * @returns {Assign} 285 | * @api public 286 | */ 287 | Repository.prototype.contents = function contents(args) { 288 | args = this.api.args(arguments); 289 | 290 | var options = args.options = this.options(args.options, [ 'ref' ]) 291 | , project = this.api.project(args.str); 292 | 293 | var cleanedPath = options.path.replace(/(^\/+)|(\/+$)/g, ''); 294 | options.path = 'contents/'+ (options.path || ''); 295 | 296 | return this.send( 297 | ['repos', project.user, project.repo, options.path], 298 | options, 299 | unwrap(args.fn, function(content) { 300 | return content.path === cleanedPath 301 | }) 302 | ); 303 | }; 304 | 305 | /** 306 | * It's possible that a user has moved the repository to a new location. 307 | * Github automatically redirects you when you access the old page. But it 308 | * doesn't provide any redirection for API calls causing them to fail with 309 | * 404's. 310 | * 311 | * In order to detect the correct repository location we need to do a HEAD 312 | * check of the public github URL and use the location header as source URL 313 | * when we're presented with a 301 status code. 314 | * 315 | * @param {String} project The project details. 316 | * @param {Function} fn The Callback. 317 | * @returns {Assign} 318 | * @api public 319 | */ 320 | Repository.prototype.moved = function moved(args) { 321 | args = this.api.args(arguments); 322 | 323 | var project = this.api.project(args.str) 324 | , api = this.api; 325 | 326 | return this.send([project.user, project.repo], { 327 | api: 'https://github.com/', 328 | method: 'HEAD' 329 | }, function gothead(err, data) { 330 | if (err) return args.fn(err); 331 | 332 | var parsed = api.project(data[0].res.request.href) 333 | , changed; 334 | 335 | if (!parsed) changed = true; 336 | else changed = parsed.user !== project.user || parsed.repo !== project.repo; 337 | 338 | args.fn(undefined, parsed || project, changed); 339 | }); 340 | }; 341 | 342 | // 343 | // Expose the repository API. 344 | // 345 | module.exports = Repository; 346 | -------------------------------------------------------------------------------- /endpoints/search.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Search 5 | * 6 | * @param {Mana} api The actual API instance. 7 | * @api private 8 | */ 9 | function Search(api) { 10 | this.qs = api.querystring.bind(api); 11 | this.send = api.send.bind(api); 12 | this.api = api; 13 | } 14 | 15 | /** 16 | * Search github 17 | * 18 | * @param {String} type The resource type to search for (repositories, commits, code, issues, users, topics, or labels) 19 | * @param {Object} options Request options 20 | * @param {String} options.query The query 21 | * @param {function} fn The callback. 22 | * @api public 23 | */ 24 | Search.prototype.query = function query(args) { 25 | args = this.api.args(arguments); 26 | 27 | var type = args.str 28 | , options = args.options || {}; 29 | 30 | return this.send( 31 | ['search', type], 32 | this.api.options(options, { q: options.query }), 33 | args.fn 34 | ); 35 | }; 36 | 37 | // 38 | // Expose the search API. 39 | // 40 | module.exports = Search; 41 | -------------------------------------------------------------------------------- /endpoints/teams.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var debug = require('diagnostics')('githulk:team'); 4 | 5 | /** 6 | * Team API endpoint. 7 | * 8 | * @constructor 9 | * @param {Mana} api The actual API instance. 10 | * @api private 11 | */ 12 | function Team(api) { 13 | this.send = api.send.bind(api); 14 | this.options = api.options; 15 | this.api = api; 16 | } 17 | 18 | /** 19 | * List all active teams for a given repository. 20 | * 21 | * @param {String} project The project information. 22 | * @param {Object} options Optional options. 23 | * @param {Function} fn The callback. 24 | * @returns {Assign} 25 | * @api public 26 | */ 27 | Team.prototype.list = function list(args) { 28 | args = this.api.args(arguments); 29 | args.options = this.options(args.options); 30 | 31 | var project = this.api.project(args.str); 32 | 33 | return this.send( 34 | ['orgs', project.user, 'teams'], 35 | args.options, 36 | args.fn 37 | ); 38 | }; 39 | 40 | /** 41 | * Get Team information for a given id. 42 | * 43 | * @param {Number} id The team id. 44 | * @param {Object} options Optional options. 45 | * @param {function} fn The callback. 46 | * @returns {Assign} 47 | * @api public 48 | */ 49 | Team.prototype.get = function get(args) { 50 | args = this.api.args(arguments); 51 | args.options = this.options(args.options); 52 | 53 | return this.send( 54 | ['teams', args.number], 55 | args.options, 56 | args.fn 57 | ); 58 | }; 59 | 60 | /** 61 | * Little syntax sugar for working with the teams API. It has no nice way of 62 | * just listing a team by name. It's not like someone is going to memorize the 63 | * id of a given team.. This function does a double lookup to get all teams, 64 | * filter by name and return all members of that team. 65 | * 66 | * @param {String} project / string. 67 | * @param {Object} options Optional options. 68 | * @param {Function} fn The callback. 69 | * @returns {Assign} 70 | * @api public 71 | */ 72 | Team.prototype.members = function members(args) { 73 | args = this.api.args(arguments); 74 | args.options = this.options(args.options); 75 | 76 | var project = this.api.project(args.str) 77 | , self = this; 78 | 79 | return this.list(project.user, function listed(err, teams) { 80 | if (err) return args.fn(err); 81 | 82 | var id; 83 | 84 | teams.some(function some(team) { 85 | if (team.name === project.repo) id = team.id; 86 | 87 | return typeof id === 'number'; 88 | }); 89 | 90 | self.send( 91 | ['teams', id, 'members'], 92 | args.options, 93 | args.fn 94 | ); 95 | }); 96 | }; 97 | 98 | // 99 | // Expose the Team API. 100 | // 101 | module.exports = Team; 102 | -------------------------------------------------------------------------------- /endpoints/users.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var debug = require('diagnostics')('githulk:user'); 4 | 5 | /** 6 | * User API endpoint. 7 | * 8 | * @param {Mana} api The actual API instance. 9 | * @api private 10 | */ 11 | function User(api) { 12 | this.send = api.send.bind(api); 13 | this.api = api; 14 | } 15 | 16 | /** 17 | * Get user information for a given username. 18 | * 19 | * @param {String} project The project details. 20 | * @param {Object} options Optional options. 21 | * @param {function} fn The callback. 22 | * @returns {Assign} 23 | * @api public 24 | */ 25 | User.prototype.get = function get(args) { 26 | args = this.api.args(arguments); 27 | 28 | if (!args.str) return this.send( 29 | ['user'], 30 | args.options || {}, 31 | args.fn 32 | ); 33 | 34 | var project = this.api.project(args.str) || {}; 35 | 36 | return this.send( 37 | ['users', project.user || args.str], 38 | this.api.options(args.options || {}), 39 | args.fn 40 | ); 41 | }; 42 | 43 | /** 44 | * Get organization information for a given username. 45 | * 46 | * @param {String} project The project details. 47 | * @param {Object} options Optional options. 48 | * @param {function} fn The callback. 49 | * @returns {Assign} 50 | * @api public 51 | */ 52 | User.prototype.orgs = function get(args) { 53 | args = this.api.args(arguments); 54 | 55 | if (!args.str) return this.send( 56 | ['user', 'orgs'], 57 | args.options || {}, 58 | args.fn 59 | ); 60 | 61 | var project = this.api.project(args.str) || {}; 62 | 63 | return this.send( 64 | ['users', project.user || args.str, 'orgs'], 65 | this.api.options(args.options || {}), 66 | args.fn 67 | ); 68 | }; 69 | 70 | // 71 | // Expose the User API. 72 | // 73 | module.exports = User; 74 | -------------------------------------------------------------------------------- /endpoints/webhooks.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var unwrapper = require('unwrapper'); 4 | 5 | /** 6 | * Webhooks API endpoint 7 | * 8 | * @constructor 9 | * @param {mana} api The actual API instance 10 | * @api private 11 | */ 12 | function Webhook(api) { 13 | this.send = api.send.bind(api); 14 | this.options = api.options; 15 | this.api = api; 16 | } 17 | 18 | /** 19 | * Properties we need in the body of the request for create 20 | * 21 | * @type {Object} 22 | * @public 23 | */ 24 | Webhook.create = [ 25 | 'name', // Name of a valid webhook service 26 | 'config', // config, object with k, v for the service 27 | 'events', // array of events that we hook into 28 | 'active' // specify a boolean whether the service is active 29 | ]; 30 | 31 | /** 32 | * Create a new webhook for the project 33 | * 34 | * @param {String} project user/repo project information. 35 | * @param {Object} options Configuration for the post information. 36 | * @returns {Assign} 37 | * @api public 38 | */ 39 | Webhook.prototype.create = function create(args) { 40 | args = this.api.args(arguments); 41 | args.options = this.options(this.api.merge(args.options, { method: 'POST' }), Webhook.create); 42 | 43 | var project = this.api.project(args.str); 44 | 45 | return this.send( 46 | ['repos', project.user, project.repo, 'hooks'], 47 | args.options, 48 | unwrapper(args.fn) 49 | ); 50 | }; 51 | 52 | /** 53 | * Get a webhooks by id. 54 | * 55 | * @param {String} project user/repo project information. 56 | * @param {Object} options Configuration for the post information. 57 | * @returns {Assign} 58 | * @api public 59 | */ 60 | Webhook.prototype.get = function get(args) { 61 | args = this.api.args(arguments); 62 | 63 | var project = this.api.project(args.str) 64 | , id; 65 | 66 | args.options = this.options(args.options); 67 | id = args.options.id; 68 | 69 | if (!id) { 70 | return args.fn(new Error('id is required parameter')); 71 | } 72 | 73 | return this.send( 74 | ['repos', project.user, project.repo, 'hooks', id], 75 | args.options, 76 | unwrapper(args.fn) 77 | ); 78 | }; 79 | 80 | /** 81 | * Delete a webhook for the given project and id 82 | * 83 | * @param {String} project user/repo project information. 84 | * @param {Object} options Configuration for the post information. 85 | * @returns {Assign} 86 | * @api public 87 | */ 88 | Webhook.prototype.delete = function del(args) { 89 | args = this.api.args(arguments); 90 | 91 | var project = this.api.project(args.str) 92 | , id; 93 | 94 | args.options = this.options(this.api.merge(args.options, { method: 'DELETE' }), []); 95 | id = args.options.id; 96 | 97 | if (!id) { 98 | return args.fn(new Error('id is required parameter')); 99 | } 100 | 101 | return this.send( 102 | ['repos', project.user, project.repo, 'hooks', id], 103 | args.options, 104 | unwrapper(args.fn) 105 | ); 106 | }; 107 | 108 | /** 109 | * List the webhooks for a given repo 110 | * 111 | * @param {String} project user/repo project information. 112 | * @param {Object} options Configuration for the post information. 113 | * @returns {Assign} 114 | * @api public 115 | */ 116 | Webhook.prototype.list = function (args) { 117 | args = this.api.args(arguments); 118 | args.options = this.options(args.options); 119 | 120 | var project = this.api.project(args.str); 121 | 122 | return this.send( 123 | ['repos', project.user, project.repo, 'hooks'], 124 | args.options, 125 | args.fn 126 | ); 127 | }; 128 | 129 | module.exports = Webhook; 130 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var debug = require('diagnostics')('githulk') 4 | , mana = require('mana') 5 | , url = require('url'); 6 | 7 | /** 8 | * GitHulk smash API. 9 | * 10 | * Options: 11 | * - url: The location of the API. 12 | * - maxdelay: Maximum delay for exponential back off. 13 | * - mindelay: Minimum delay for exponential back off. 14 | * - retries: The amount of retries we should before failing. 15 | * - factor: Exponential back off factor. 16 | * - cache: We need to use to store requests in so we can handle 304's and not 17 | * eat the API tokens. 18 | * - tokens: Array of tokens we should use for these requests. 19 | * - token: Single token we should use to connect. I added to the tokens array. 20 | * - user: Username of your GitHub account (if you don't want to use tokens) 21 | * - password: Password of your GitHub account (if you don't want to use tokens) 22 | * - authorization: Custom authorization we should be using instead of tokens. 23 | * 24 | * @constructor 25 | * @param {Object} options GitHulk configuration 26 | * @api public 27 | */ 28 | mana.extend({ 29 | initialise: function initialise(options) { 30 | options = options || {}; 31 | 32 | options.url = 'url' in options ? options.url : 'https://api.github.com/'; 33 | options.maxdelay = 'maxdelay' in options ? options.maxdelay : 60000; 34 | options.mindelay = 'mindelay' in options ? options.mindelay : 100; 35 | options.retries = 'retries' in options ? options.retries : 3; 36 | options.factor = 'factor' in options ? options.factor : 2; 37 | options.cache = 'cache' in options ? options.cache : null; 38 | options.tokens = 'tokens' in options ? options.tokens : []; 39 | 40 | this.authorization = options.authorization; 41 | this.mindelay = options.mindelay; 42 | this.maxdelay = options.maxdelay; 43 | this.mirrors = options.mirrors; 44 | this.retries = options.retries; 45 | this.factor = options.factor; 46 | this.tokens = options.tokens; 47 | this.user = options.user; 48 | this.api = options.url; 49 | 50 | // 51 | // Pre-compile the basic authorization so we can do updates and deletes 52 | // against the registries. 53 | // 54 | if (!this.authorization && options.user && options.password) { 55 | debug('received authorization information for %s', options.user); 56 | this.authorization = 'Basic '+ new Buffer( 57 | options.user +':'+ options.password 58 | ).toString('base64'); 59 | } 60 | 61 | // 62 | // Transform tokens in an array, strings are here as they can be more 63 | // readable. 64 | // 65 | if ('string' === typeof this.tokens) { 66 | this.tokens = this.tokens.split(','); 67 | } 68 | 69 | // 70 | // No user / password, no predefined authorization, so maybe we've received 71 | // an OAuth token. 72 | // 73 | var token = options.token 74 | || process.env.GITHULK_TOKEN 75 | || process.env.GITHULK 76 | || process.env.GITHUB_TOKEN; 77 | 78 | if (!this.authorization && token) { 79 | this.tokens.push(token); 80 | } 81 | 82 | // 83 | // We want to use a cache engine for the optimizing our responses. This 84 | // makes us use conditional requests for the Github API making it easier to 85 | // stay within your API rate limit. 86 | // 87 | if (options.cache) { 88 | this.cache = options.cache; 89 | } 90 | 91 | // 92 | // Make a second instance of mana for the v4 api 93 | // 94 | this.manaql = mana.extend({ 95 | initialise: function initialise(opts) { 96 | this.api = opts.api; 97 | this.authorization = opts.authorization; 98 | this.cache = opts.cache; 99 | this.tokens = opts.tokens; 100 | } 101 | }).new(Object.assign(options, { 102 | api: this.api, 103 | authorization: this.authorization, 104 | cache: this.cache, 105 | tokens: this.tokens, 106 | isGraphql: true 107 | })); 108 | 109 | this.manaql.setRatelimitParser(function parseRatelimitFromBody(res, body, setRatelimit) { 110 | if(body && body.data && body.data.rateLimit) { 111 | var limitData = body.data.rateLimit; 112 | 113 | setRatelimit(limitData.resetAt, limitData.limit, limitData.remaining); 114 | } 115 | }); 116 | }, 117 | 118 | /** 119 | * Parse out github information from a given string or object. For the object 120 | * we assume that we're given an object with repository information as seen in 121 | * your package.json 122 | * 123 | * @param {String|Object} data The information that needs to be parsed. 124 | * @returns {Object} 125 | * @api public 126 | */ 127 | project: require('extract-github'), 128 | 129 | /** 130 | * Return the correct Accept headers for a given content type. 131 | * 132 | * @param {String} type 133 | * @returns {String} 134 | */ 135 | accepts: function accepts(type) { 136 | var types = { 137 | text: 'application/vnd.github.v3.text+json', 138 | html: 'application/vnd.github.v3.html+json', 139 | full: 'application/vnd.github.v3.full+json', 140 | raw: 'application/vnd.github.v3.raw+json' 141 | }; 142 | 143 | return types[type] || types[type.toLowerCase()] || type; 144 | }, 145 | 146 | /** 147 | * Helper function to create some sane default options that we supply to the 148 | * API. 149 | * 150 | * @param {Object} args Received optional options 151 | * @param {Array|Object} params Optional params. 152 | * @returns {Object} Args. 153 | * @api private 154 | */ 155 | options: function options(args, params) { 156 | args = args || {}; 157 | args.params = args.params || params || []; 158 | 159 | // 160 | // Add some default values. 161 | // 162 | var defaults = { page: 1, per_page: 100 }; 163 | Object.keys(defaults).forEach(function each(key) { 164 | if (Array.isArray(args.params)) { 165 | if (!~args.params.indexOf(key)) args.params.push(key); 166 | } else { 167 | if (!(key in args.params)) args.params[key] = defaults[key]; 168 | } 169 | }); 170 | 171 | return args; 172 | }, 173 | 174 | /** 175 | * Parse Github link headers. 176 | * 177 | * @param {String} header The link header we need to parse. 178 | * @returns {Object} 179 | * @api public 180 | */ 181 | link: function link(header) { 182 | return header.split(',').reduce(function reduce(memo, part) { 183 | var chunks = /<([^<]+)?>\;\srel="([^"]+)?"/.exec(part.trim()); 184 | 185 | if (!chunks) return memo; 186 | memo[chunks[2]] = url.parse(chunks[1], true); 187 | 188 | return memo; 189 | }, Object.create(null)); 190 | }, 191 | 192 | /** 193 | * We need to override the `send` method of mana so we can attempt to parse the 194 | * pagination headers of GitHub and follow if needed so this can all be 195 | * handled transparently. 196 | * 197 | * @returns {Assign} The assign instance that receives all the things 198 | * @api private 199 | */ 200 | send: function send(args) { 201 | args = this.args(arguments); 202 | 203 | var options = JSON.parse(JSON.stringify(args.options)) 204 | , hulk = this; 205 | 206 | /** 207 | * A simple optional callback. 208 | * 209 | * @param {Response} res Incoming HTTP response. 210 | * @param {Assign} assign The assign instance that got returned. 211 | * @param {Object} oargs The original args that got passed in to the request. 212 | * @api private 213 | */ 214 | args.options.next = function next(res, assign, oargs) { 215 | oargs = oargs || args; 216 | 217 | // 218 | // When the `nofollow` option is provided we should not follow the 219 | // returned link headers from the GitHub API. It's something that users 220 | // want to manage them selfs. 221 | // 222 | if (!res.headers.link || oargs.options.nofollow) return assign.end(); 223 | 224 | var link = hulk.link(res.headers.link); 225 | 226 | // 227 | // We've reached the end of the of the iteration, also bail out. 228 | // 229 | if (!link.next || !link.next.query) return assign.end(); 230 | 231 | // 232 | // We've received instructions from GitHub that there are more pages with 233 | // information that we need to parse out. Continue to follow these link 234 | // headers to create a full set of data. We leverage the `oargs` (original 235 | // args) from the first request and only update the request params. 236 | // 237 | if (link.next.query.page) { 238 | options.page = link.next.query.page; 239 | } 240 | 241 | if (link.next.query.per_page) { 242 | options.per_page = link.next.query.per_page; 243 | } 244 | 245 | // 246 | // This will be the last batch of data we need to process, so we can 247 | // remove this next function and just make this thing process as normal 248 | // again. 249 | // 250 | if (!link.last || link.next.query.page === link.last.query.page) { 251 | delete oargs.options.next; 252 | } 253 | 254 | // 255 | // Merge the options, mana can delete options while it creates params for 256 | // the connection. We've stored a copy of them before, and we're going to 257 | // merge them over again. 258 | // 259 | hulk.merge(oargs.options, options); 260 | 261 | // 262 | // Remove oargs.str if we have an array preference. 263 | // 264 | if (oargs.str && oargs.array) oargs.str = ''; 265 | oargs.options.assign = assign; 266 | 267 | // No need to enqueue another callback. 268 | return mana.prototype.send.call(hulk, 269 | oargs.array, oargs.nr, oargs.options, oargs.str 270 | ); 271 | }; 272 | 273 | return mana.prototype.send.call(hulk, 274 | args.array, args.fn, args.nr, args.options, args.str 275 | ); 276 | } 277 | }).drink(module); 278 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "githulk", 3 | "version": "1.7.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "ajv": { 8 | "version": "6.10.2", 9 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", 10 | "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", 11 | "requires": { 12 | "fast-deep-equal": "^2.0.1", 13 | "fast-json-stable-stringify": "^2.0.0", 14 | "json-schema-traverse": "^0.4.1", 15 | "uri-js": "^4.2.2" 16 | } 17 | }, 18 | "asn1": { 19 | "version": "0.2.4", 20 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", 21 | "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", 22 | "requires": { 23 | "safer-buffer": "~2.1.0" 24 | } 25 | }, 26 | "assert-plus": { 27 | "version": "1.0.0", 28 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 29 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 30 | }, 31 | "assign": { 32 | "version": "0.1.7", 33 | "resolved": "https://registry.npmjs.org/assign/-/assign-0.1.7.tgz", 34 | "integrity": "sha1-5jv+Ooh7hjCRPCdmPkzJv/Hd0l8=", 35 | "requires": { 36 | "fusing": "0.4.x" 37 | }, 38 | "dependencies": { 39 | "fusing": { 40 | "version": "0.4.0", 41 | "resolved": "https://registry.npmjs.org/fusing/-/fusing-0.4.0.tgz", 42 | "integrity": "sha1-yZBo9Uyj4R3AEYkCFSq/Nnq6Sk0=", 43 | "requires": { 44 | "emits": "1.0.x", 45 | "predefine": "0.1.x" 46 | } 47 | } 48 | } 49 | }, 50 | "assume": { 51 | "version": "2.1.0", 52 | "resolved": "https://registry.npmjs.org/assume/-/assume-2.1.0.tgz", 53 | "integrity": "sha512-ugye6y85HyKkMe1GOprT9eGImzGDJ/Yci7/beHE0M63osWljUoiQ7ix6WwxWK5gvEVFnP7b65/ZS7jf1Um17MQ==", 54 | "dev": true, 55 | "requires": { 56 | "deep-eql": "^3.0.1", 57 | "fn.name": "^1.0.1", 58 | "is-buffer": "^2.0.0", 59 | "object-inspect": "^1.5.0", 60 | "propget": "^1.1.0", 61 | "pruddy-error": "^2.0.2" 62 | } 63 | }, 64 | "asynckit": { 65 | "version": "0.4.0", 66 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 67 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" 68 | }, 69 | "aws-sign2": { 70 | "version": "0.7.0", 71 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", 72 | "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" 73 | }, 74 | "aws4": { 75 | "version": "1.8.0", 76 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", 77 | "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" 78 | }, 79 | "back": { 80 | "version": "1.0.2", 81 | "resolved": "https://registry.npmjs.org/back/-/back-1.0.2.tgz", 82 | "integrity": "sha1-qT9ebOaXKZhNWQGiuxbjsBpNY2k=", 83 | "requires": { 84 | "xtend": "^4.0.0" 85 | } 86 | }, 87 | "balanced-match": { 88 | "version": "1.0.0", 89 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/balanced-match/-/balanced-match-1.0.0.tgz", 90 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 91 | "dev": true 92 | }, 93 | "bcrypt-pbkdf": { 94 | "version": "1.0.2", 95 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", 96 | "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", 97 | "requires": { 98 | "tweetnacl": "^0.14.3" 99 | } 100 | }, 101 | "brace-expansion": { 102 | "version": "1.1.11", 103 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/brace-expansion/-/brace-expansion-1.1.11.tgz", 104 | "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=", 105 | "dev": true, 106 | "requires": { 107 | "balanced-match": "^1.0.0", 108 | "concat-map": "0.0.1" 109 | } 110 | }, 111 | "browser-stdout": { 112 | "version": "1.3.0", 113 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/browser-stdout/-/browser-stdout-1.3.0.tgz", 114 | "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", 115 | "dev": true 116 | }, 117 | "buffer-from": { 118 | "version": "1.1.1", 119 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/buffer-from/-/buffer-from-1.1.1.tgz", 120 | "integrity": "sha1-MnE7wCj3XAL9txDXx7zsHyxgcO8=", 121 | "dev": true 122 | }, 123 | "caseless": { 124 | "version": "0.12.0", 125 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", 126 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" 127 | }, 128 | "color": { 129 | "version": "3.0.0", 130 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/color/-/color-3.0.0.tgz", 131 | "integrity": "sha1-2SC0Mo1TSjrIKV1o971LpsQnvpo=", 132 | "requires": { 133 | "color-convert": "^1.9.1", 134 | "color-string": "^1.5.2" 135 | } 136 | }, 137 | "color-convert": { 138 | "version": "1.9.2", 139 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/color-convert/-/color-convert-1.9.2.tgz", 140 | "integrity": "sha1-SYgbj7pn3xKpa98/VsCqueeRMUc=", 141 | "requires": { 142 | "color-name": "1.1.1" 143 | } 144 | }, 145 | "color-name": { 146 | "version": "1.1.1", 147 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/color-name/-/color-name-1.1.1.tgz", 148 | "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=" 149 | }, 150 | "color-string": { 151 | "version": "1.5.3", 152 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/color-string/-/color-string-1.5.3.tgz", 153 | "integrity": "sha1-ybvF8BtYtUkvPWhXRZy2WQziBMw=", 154 | "requires": { 155 | "color-name": "^1.0.0", 156 | "simple-swizzle": "^0.2.2" 157 | } 158 | }, 159 | "colornames": { 160 | "version": "1.1.1", 161 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/colornames/-/colornames-1.1.1.tgz", 162 | "integrity": "sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y=" 163 | }, 164 | "colorspace": { 165 | "version": "1.1.1", 166 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/colorspace/-/colorspace-1.1.1.tgz", 167 | "integrity": "sha1-msJJHhvG+PtpDiF2gU+NCRY22XI=", 168 | "requires": { 169 | "color": "3.0.x", 170 | "text-hex": "1.0.x" 171 | } 172 | }, 173 | "combined-stream": { 174 | "version": "1.0.8", 175 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 176 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 177 | "requires": { 178 | "delayed-stream": "~1.0.0" 179 | } 180 | }, 181 | "commander": { 182 | "version": "2.11.0", 183 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/commander/-/commander-2.11.0.tgz", 184 | "integrity": "sha1-FXFS/R56bI2YpbcVzzdt+SgARWM=", 185 | "dev": true 186 | }, 187 | "concat-map": { 188 | "version": "0.0.1", 189 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/concat-map/-/concat-map-0.0.1.tgz", 190 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 191 | "dev": true 192 | }, 193 | "concat-stream": { 194 | "version": "1.6.2", 195 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/concat-stream/-/concat-stream-1.6.2.tgz", 196 | "integrity": "sha1-kEvfGUzTEi/Gdcd/xKw9T/D9GjQ=", 197 | "dev": true, 198 | "requires": { 199 | "buffer-from": "^1.0.0", 200 | "inherits": "^2.0.3", 201 | "readable-stream": "^2.2.2", 202 | "typedarray": "^0.0.6" 203 | } 204 | }, 205 | "core-util-is": { 206 | "version": "1.0.2", 207 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/core-util-is/-/core-util-is-1.0.2.tgz", 208 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 209 | }, 210 | "cross-spawn": { 211 | "version": "5.1.0", 212 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/cross-spawn/-/cross-spawn-5.1.0.tgz", 213 | "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", 214 | "dev": true, 215 | "requires": { 216 | "lru-cache": "^4.0.1", 217 | "shebang-command": "^1.2.0", 218 | "which": "^1.2.9" 219 | } 220 | }, 221 | "dashdash": { 222 | "version": "1.14.1", 223 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", 224 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", 225 | "requires": { 226 | "assert-plus": "^1.0.0" 227 | } 228 | }, 229 | "debug": { 230 | "version": "3.1.0", 231 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/debug/-/debug-3.1.0.tgz", 232 | "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", 233 | "dev": true, 234 | "requires": { 235 | "ms": "2.0.0" 236 | } 237 | }, 238 | "deep-eql": { 239 | "version": "3.0.1", 240 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", 241 | "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", 242 | "dev": true, 243 | "requires": { 244 | "type-detect": "^4.0.0" 245 | } 246 | }, 247 | "delayed-stream": { 248 | "version": "1.0.0", 249 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 250 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" 251 | }, 252 | "diagnostics": { 253 | "version": "1.1.1", 254 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/diagnostics/-/diagnostics-1.1.1.tgz", 255 | "integrity": "sha1-yrasM99wydmnJ0kK5DrJladpsio=", 256 | "requires": { 257 | "colorspace": "1.1.x", 258 | "enabled": "1.0.x", 259 | "kuler": "1.0.x" 260 | } 261 | }, 262 | "diff": { 263 | "version": "3.3.1", 264 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/diff/-/diff-3.3.1.tgz", 265 | "integrity": "sha1-qoVnpu7QPFMfyJ0/cRzQ5SWd7HU=", 266 | "dev": true 267 | }, 268 | "ecc-jsbn": { 269 | "version": "0.1.2", 270 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", 271 | "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", 272 | "requires": { 273 | "jsbn": "~0.1.0", 274 | "safer-buffer": "^2.1.0" 275 | } 276 | }, 277 | "emits": { 278 | "version": "1.0.2", 279 | "resolved": "https://registry.npmjs.org/emits/-/emits-1.0.2.tgz", 280 | "integrity": "sha1-2yDsZmgyUHHDE0QeMM/ipp6nOFk=" 281 | }, 282 | "enabled": { 283 | "version": "1.0.2", 284 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/enabled/-/enabled-1.0.2.tgz", 285 | "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=", 286 | "requires": { 287 | "env-variable": "0.0.x" 288 | } 289 | }, 290 | "env-variable": { 291 | "version": "0.0.4", 292 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/env-variable/-/env-variable-0.0.4.tgz", 293 | "integrity": "sha1-DWKAz1B9hCQr7+NaUSta5L53xU4=" 294 | }, 295 | "escape-string-regexp": { 296 | "version": "1.0.5", 297 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 298 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 299 | "dev": true 300 | }, 301 | "eventemitter3": { 302 | "version": "4.0.0", 303 | "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz", 304 | "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==" 305 | }, 306 | "extend": { 307 | "version": "3.0.2", 308 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 309 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" 310 | }, 311 | "extendible": { 312 | "version": "0.1.1", 313 | "resolved": "https://registry.npmjs.org/extendible/-/extendible-0.1.1.tgz", 314 | "integrity": "sha1-4qN+2HEp+0+VM+io11BiMKU5yQU=" 315 | }, 316 | "extract-github": { 317 | "version": "1.0.0", 318 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/extract-github/-/extract-github-1.0.0.tgz", 319 | "integrity": "sha1-sKPyOSApMJhiEyvWLPklq2pSAM4=" 320 | }, 321 | "extsprintf": { 322 | "version": "1.3.0", 323 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", 324 | "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" 325 | }, 326 | "fast-deep-equal": { 327 | "version": "2.0.1", 328 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", 329 | "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" 330 | }, 331 | "fast-json-stable-stringify": { 332 | "version": "2.0.0", 333 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", 334 | "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" 335 | }, 336 | "fn.name": { 337 | "version": "1.1.0", 338 | "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", 339 | "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", 340 | "dev": true 341 | }, 342 | "forever-agent": { 343 | "version": "0.6.1", 344 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", 345 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" 346 | }, 347 | "form-data": { 348 | "version": "2.3.3", 349 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", 350 | "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", 351 | "requires": { 352 | "asynckit": "^0.4.0", 353 | "combined-stream": "^1.0.6", 354 | "mime-types": "^2.1.12" 355 | } 356 | }, 357 | "fs.realpath": { 358 | "version": "1.0.0", 359 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/fs.realpath/-/fs.realpath-1.0.0.tgz", 360 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 361 | "dev": true 362 | }, 363 | "fusing": { 364 | "version": "1.0.0", 365 | "resolved": "https://registry.npmjs.org/fusing/-/fusing-1.0.0.tgz", 366 | "integrity": "sha1-VQwV12r5Jld4qgUezkTUAAoJjUU=", 367 | "requires": { 368 | "emits": "3.0.x", 369 | "predefine": "0.1.x" 370 | }, 371 | "dependencies": { 372 | "emits": { 373 | "version": "3.0.0", 374 | "resolved": "https://registry.npmjs.org/emits/-/emits-3.0.0.tgz", 375 | "integrity": "sha1-MnUrupXhcHshlWI4Srm7ix/WL3A=" 376 | } 377 | } 378 | }, 379 | "getpass": { 380 | "version": "0.1.7", 381 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", 382 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", 383 | "requires": { 384 | "assert-plus": "^1.0.0" 385 | } 386 | }, 387 | "glob": { 388 | "version": "7.1.2", 389 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/glob/-/glob-7.1.2.tgz", 390 | "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", 391 | "dev": true, 392 | "requires": { 393 | "fs.realpath": "^1.0.0", 394 | "inflight": "^1.0.4", 395 | "inherits": "2", 396 | "minimatch": "^3.0.4", 397 | "once": "^1.3.0", 398 | "path-is-absolute": "^1.0.0" 399 | } 400 | }, 401 | "growl": { 402 | "version": "1.10.3", 403 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/growl/-/growl-1.10.3.tgz", 404 | "integrity": "sha1-GSa6kM8+3+KttJJ/WIC8IsZseQ8=", 405 | "dev": true 406 | }, 407 | "har-schema": { 408 | "version": "2.0.0", 409 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", 410 | "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" 411 | }, 412 | "har-validator": { 413 | "version": "5.1.3", 414 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", 415 | "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", 416 | "requires": { 417 | "ajv": "^6.5.5", 418 | "har-schema": "^2.0.0" 419 | } 420 | }, 421 | "has-flag": { 422 | "version": "2.0.0", 423 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/has-flag/-/has-flag-2.0.0.tgz", 424 | "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", 425 | "dev": true 426 | }, 427 | "he": { 428 | "version": "1.1.1", 429 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/he/-/he-1.1.1.tgz", 430 | "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", 431 | "dev": true 432 | }, 433 | "http-signature": { 434 | "version": "1.2.0", 435 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", 436 | "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", 437 | "requires": { 438 | "assert-plus": "^1.0.0", 439 | "jsprim": "^1.2.2", 440 | "sshpk": "^1.7.0" 441 | } 442 | }, 443 | "inflight": { 444 | "version": "1.0.6", 445 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/inflight/-/inflight-1.0.6.tgz", 446 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 447 | "dev": true, 448 | "requires": { 449 | "once": "^1.3.0", 450 | "wrappy": "1" 451 | } 452 | }, 453 | "inherits": { 454 | "version": "2.0.3", 455 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/inherits/-/inherits-2.0.3.tgz", 456 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 457 | "dev": true 458 | }, 459 | "is-arrayish": { 460 | "version": "0.3.2", 461 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/is-arrayish/-/is-arrayish-0.3.2.tgz", 462 | "integrity": "sha1-RXSirlb3qyBolvtDHq7tBm/fjwM=" 463 | }, 464 | "is-buffer": { 465 | "version": "2.0.3", 466 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", 467 | "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==", 468 | "dev": true 469 | }, 470 | "is-node": { 471 | "version": "1.0.2", 472 | "resolved": "https://registry.npmjs.org/is-node/-/is-node-1.0.2.tgz", 473 | "integrity": "sha1-19ACdF733ru3R36YiVarCk/MtlM=", 474 | "dev": true 475 | }, 476 | "is-typedarray": { 477 | "version": "1.0.0", 478 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 479 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" 480 | }, 481 | "isarray": { 482 | "version": "1.0.0", 483 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/isarray/-/isarray-1.0.0.tgz", 484 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", 485 | "dev": true 486 | }, 487 | "isexe": { 488 | "version": "2.0.0", 489 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/isexe/-/isexe-2.0.0.tgz", 490 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 491 | "dev": true 492 | }, 493 | "isstream": { 494 | "version": "0.1.2", 495 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 496 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" 497 | }, 498 | "jsbn": { 499 | "version": "0.1.1", 500 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", 501 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" 502 | }, 503 | "json-schema": { 504 | "version": "0.2.3", 505 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", 506 | "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" 507 | }, 508 | "json-schema-traverse": { 509 | "version": "0.4.1", 510 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 511 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" 512 | }, 513 | "json-stringify-safe": { 514 | "version": "5.0.1", 515 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", 516 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" 517 | }, 518 | "jsprim": { 519 | "version": "1.4.1", 520 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", 521 | "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", 522 | "requires": { 523 | "assert-plus": "1.0.0", 524 | "extsprintf": "1.3.0", 525 | "json-schema": "0.2.3", 526 | "verror": "1.10.0" 527 | } 528 | }, 529 | "kuler": { 530 | "version": "1.0.0", 531 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/kuler/-/kuler-1.0.0.tgz", 532 | "integrity": "sha1-kErTHDc7eBaVhU0yvjOBi/HWAlA=", 533 | "requires": { 534 | "colornames": "^1.1.1" 535 | } 536 | }, 537 | "left-pad": { 538 | "version": "1.3.0", 539 | "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", 540 | "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", 541 | "dev": true 542 | }, 543 | "lru-cache": { 544 | "version": "4.1.3", 545 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/lru-cache/-/lru-cache-4.1.3.tgz", 546 | "integrity": "sha1-oRdc80lt/IQ2wVbDNLSVWZK85pw=", 547 | "dev": true, 548 | "requires": { 549 | "pseudomap": "^1.0.2", 550 | "yallist": "^2.1.2" 551 | } 552 | }, 553 | "mana": { 554 | "version": "1.1.0", 555 | "resolved": "https://registry.npmjs.org/mana/-/mana-1.1.0.tgz", 556 | "integrity": "sha512-RUOF5zMMPRvTyZ3oPPhnsZFAoG1qp+Y3QQZG85/qzxWDGhHBZKrvF9TnbOYmBTfb4VIvCVgwbPdQ5EMtKBpmTQ==", 557 | "requires": { 558 | "assign": ">=0.1.7", 559 | "back": "1.0.x", 560 | "diagnostics": "^2.0.2", 561 | "eventemitter3": "^4.0.0", 562 | "fusing": "1.0.x", 563 | "millisecond": "0.1.x", 564 | "request": "^2.88.0" 565 | }, 566 | "dependencies": { 567 | "diagnostics": { 568 | "version": "2.0.2", 569 | "resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-2.0.2.tgz", 570 | "integrity": "sha512-gvnlQHwkWTOeSM1iRNEwPcUuUwlhovzbuQzalKrTbcJhI5cvhtkRVZZqomwZt4pCl2dvbsugD6yyu+66rtMy3Q==", 571 | "requires": { 572 | "colorspace": "1.1.x", 573 | "enabled": "2.0.x", 574 | "kuler": "^2.0.0", 575 | "storage-engine": "3.0.x" 576 | } 577 | }, 578 | "enabled": { 579 | "version": "2.0.0", 580 | "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", 581 | "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" 582 | }, 583 | "kuler": { 584 | "version": "2.0.0", 585 | "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", 586 | "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" 587 | } 588 | } 589 | }, 590 | "millisecond": { 591 | "version": "0.1.2", 592 | "resolved": "https://registry.npmjs.org/millisecond/-/millisecond-0.1.2.tgz", 593 | "integrity": "sha1-bMWtOGJByrjniv+WT4cCjuyS2sU=" 594 | }, 595 | "mime-db": { 596 | "version": "1.40.0", 597 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", 598 | "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" 599 | }, 600 | "mime-types": { 601 | "version": "2.1.24", 602 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", 603 | "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", 604 | "requires": { 605 | "mime-db": "1.40.0" 606 | } 607 | }, 608 | "minimatch": { 609 | "version": "3.0.4", 610 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/minimatch/-/minimatch-3.0.4.tgz", 611 | "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", 612 | "dev": true, 613 | "requires": { 614 | "brace-expansion": "^1.1.7" 615 | } 616 | }, 617 | "minimist": { 618 | "version": "0.0.8", 619 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/minimist/-/minimist-0.0.8.tgz", 620 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 621 | "dev": true 622 | }, 623 | "mkdirp": { 624 | "version": "0.5.1", 625 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/mkdirp/-/mkdirp-0.5.1.tgz", 626 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 627 | "dev": true, 628 | "requires": { 629 | "minimist": "0.0.8" 630 | } 631 | }, 632 | "mocha": { 633 | "version": "4.1.0", 634 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/mocha/-/mocha-4.1.0.tgz", 635 | "integrity": "sha1-fYbPvPNcuCnidUwy4XNV7AUzh5Q=", 636 | "dev": true, 637 | "requires": { 638 | "browser-stdout": "1.3.0", 639 | "commander": "2.11.0", 640 | "debug": "3.1.0", 641 | "diff": "3.3.1", 642 | "escape-string-regexp": "1.0.5", 643 | "glob": "7.1.2", 644 | "growl": "1.10.3", 645 | "he": "1.1.1", 646 | "mkdirp": "0.5.1", 647 | "supports-color": "4.4.0" 648 | } 649 | }, 650 | "ms": { 651 | "version": "2.0.0", 652 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/ms/-/ms-2.0.0.tgz", 653 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 654 | "dev": true 655 | }, 656 | "oauth-sign": { 657 | "version": "0.9.0", 658 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", 659 | "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" 660 | }, 661 | "object-inspect": { 662 | "version": "1.6.0", 663 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", 664 | "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==", 665 | "dev": true 666 | }, 667 | "once": { 668 | "version": "1.4.0", 669 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/once/-/once-1.4.0.tgz", 670 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 671 | "dev": true, 672 | "requires": { 673 | "wrappy": "1" 674 | } 675 | }, 676 | "os-shim": { 677 | "version": "0.1.3", 678 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/os-shim/-/os-shim-0.1.3.tgz", 679 | "integrity": "sha1-a2LDeRz3kJ6jXtRuF2WLtBfLORc=", 680 | "dev": true 681 | }, 682 | "path-is-absolute": { 683 | "version": "1.0.1", 684 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 685 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 686 | "dev": true 687 | }, 688 | "performance-now": { 689 | "version": "2.1.0", 690 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", 691 | "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" 692 | }, 693 | "pre-commit": { 694 | "version": "1.2.2", 695 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/pre-commit/-/pre-commit-1.2.2.tgz", 696 | "integrity": "sha1-287g7p3nI15X95xW186UZBpp7sY=", 697 | "dev": true, 698 | "requires": { 699 | "cross-spawn": "^5.0.1", 700 | "spawn-sync": "^1.0.15", 701 | "which": "1.2.x" 702 | } 703 | }, 704 | "predefine": { 705 | "version": "0.1.2", 706 | "resolved": "https://registry.npmjs.org/predefine/-/predefine-0.1.2.tgz", 707 | "integrity": "sha1-KqkrRJa8H4VU5DpF92v75Q0z038=", 708 | "requires": { 709 | "extendible": "0.1.x" 710 | } 711 | }, 712 | "process-nextick-args": { 713 | "version": "2.0.0", 714 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/process-nextick-args/-/process-nextick-args-2.0.0.tgz", 715 | "integrity": "sha1-o31zL0JxtKsa0HDTVQjoKQeI/6o=", 716 | "dev": true 717 | }, 718 | "propget": { 719 | "version": "1.1.0", 720 | "resolved": "https://registry.npmjs.org/propget/-/propget-1.1.0.tgz", 721 | "integrity": "sha1-bBx6yaCcBb21yWfwzY4cCUCf72s=", 722 | "dev": true 723 | }, 724 | "pruddy-error": { 725 | "version": "2.0.2", 726 | "resolved": "https://registry.npmjs.org/pruddy-error/-/pruddy-error-2.0.2.tgz", 727 | "integrity": "sha512-cEMUxXtU7iD+he5Hh1Jr3RHdTvAID2/VHBpC2TDLWP7UmbvZmR4/B50mQK7lguZhqcBTwdtsN9JI8diVTWedNw==", 728 | "dev": true, 729 | "requires": { 730 | "is-node": "^1.0.2", 731 | "left-pad": "^1.2.0" 732 | } 733 | }, 734 | "pseudomap": { 735 | "version": "1.0.2", 736 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/pseudomap/-/pseudomap-1.0.2.tgz", 737 | "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", 738 | "dev": true 739 | }, 740 | "psl": { 741 | "version": "1.4.0", 742 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz", 743 | "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==" 744 | }, 745 | "punycode": { 746 | "version": "2.1.1", 747 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 748 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" 749 | }, 750 | "qs": { 751 | "version": "6.5.2", 752 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 753 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" 754 | }, 755 | "readable-stream": { 756 | "version": "2.3.6", 757 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/readable-stream/-/readable-stream-2.3.6.tgz", 758 | "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", 759 | "dev": true, 760 | "requires": { 761 | "core-util-is": "~1.0.0", 762 | "inherits": "~2.0.3", 763 | "isarray": "~1.0.0", 764 | "process-nextick-args": "~2.0.0", 765 | "safe-buffer": "~5.1.1", 766 | "string_decoder": "~1.1.1", 767 | "util-deprecate": "~1.0.1" 768 | } 769 | }, 770 | "request": { 771 | "version": "2.88.0", 772 | "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", 773 | "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", 774 | "requires": { 775 | "aws-sign2": "~0.7.0", 776 | "aws4": "^1.8.0", 777 | "caseless": "~0.12.0", 778 | "combined-stream": "~1.0.6", 779 | "extend": "~3.0.2", 780 | "forever-agent": "~0.6.1", 781 | "form-data": "~2.3.2", 782 | "har-validator": "~5.1.0", 783 | "http-signature": "~1.2.0", 784 | "is-typedarray": "~1.0.0", 785 | "isstream": "~0.1.2", 786 | "json-stringify-safe": "~5.0.1", 787 | "mime-types": "~2.1.19", 788 | "oauth-sign": "~0.9.0", 789 | "performance-now": "^2.1.0", 790 | "qs": "~6.5.2", 791 | "safe-buffer": "^5.1.2", 792 | "tough-cookie": "~2.4.3", 793 | "tunnel-agent": "^0.6.0", 794 | "uuid": "^3.3.2" 795 | } 796 | }, 797 | "safe-buffer": { 798 | "version": "5.1.2", 799 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/safe-buffer/-/safe-buffer-5.1.2.tgz", 800 | "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=" 801 | }, 802 | "safer-buffer": { 803 | "version": "2.1.2", 804 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 805 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 806 | }, 807 | "shebang-command": { 808 | "version": "1.2.0", 809 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/shebang-command/-/shebang-command-1.2.0.tgz", 810 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", 811 | "dev": true, 812 | "requires": { 813 | "shebang-regex": "^1.0.0" 814 | } 815 | }, 816 | "shebang-regex": { 817 | "version": "1.0.0", 818 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/shebang-regex/-/shebang-regex-1.0.0.tgz", 819 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", 820 | "dev": true 821 | }, 822 | "simple-swizzle": { 823 | "version": "0.2.2", 824 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/simple-swizzle/-/simple-swizzle-0.2.2.tgz", 825 | "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", 826 | "requires": { 827 | "is-arrayish": "^0.3.1" 828 | } 829 | }, 830 | "spawn-sync": { 831 | "version": "1.0.15", 832 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/spawn-sync/-/spawn-sync-1.0.15.tgz", 833 | "integrity": "sha1-sAeZVX63+wyDdsKdROih6mfldHY=", 834 | "dev": true, 835 | "requires": { 836 | "concat-stream": "^1.4.7", 837 | "os-shim": "^0.1.2" 838 | } 839 | }, 840 | "sshpk": { 841 | "version": "1.16.1", 842 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", 843 | "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", 844 | "requires": { 845 | "asn1": "~0.2.3", 846 | "assert-plus": "^1.0.0", 847 | "bcrypt-pbkdf": "^1.0.0", 848 | "dashdash": "^1.12.0", 849 | "ecc-jsbn": "~0.1.1", 850 | "getpass": "^0.1.1", 851 | "jsbn": "~0.1.0", 852 | "safer-buffer": "^2.0.2", 853 | "tweetnacl": "~0.14.0" 854 | } 855 | }, 856 | "storage-engine": { 857 | "version": "3.0.7", 858 | "resolved": "https://registry.npmjs.org/storage-engine/-/storage-engine-3.0.7.tgz", 859 | "integrity": "sha512-V/jJykpPdsyDImLwu19syIAWn/Tb41tBDikQS+aQPH2h2OgqdLxwOg7wI9nPH3Y0Mh1ce566JZl2u+4eH1nAsg==", 860 | "requires": { 861 | "enabled": "^2.0.0", 862 | "eventemitter3": "^4.0.0" 863 | }, 864 | "dependencies": { 865 | "enabled": { 866 | "version": "2.0.0", 867 | "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", 868 | "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" 869 | } 870 | } 871 | }, 872 | "string_decoder": { 873 | "version": "1.1.1", 874 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/string_decoder/-/string_decoder-1.1.1.tgz", 875 | "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", 876 | "dev": true, 877 | "requires": { 878 | "safe-buffer": "~5.1.0" 879 | } 880 | }, 881 | "supports-color": { 882 | "version": "4.4.0", 883 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/supports-color/-/supports-color-4.4.0.tgz", 884 | "integrity": "sha1-iD992rwWUUKyphQn8zUt7RldGj4=", 885 | "dev": true, 886 | "requires": { 887 | "has-flag": "^2.0.0" 888 | } 889 | }, 890 | "text-hex": { 891 | "version": "1.0.0", 892 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/text-hex/-/text-hex-1.0.0.tgz", 893 | "integrity": "sha1-adycGxdEbueakr9biEu0uRJ1BvU=" 894 | }, 895 | "tough-cookie": { 896 | "version": "2.4.3", 897 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", 898 | "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", 899 | "requires": { 900 | "psl": "^1.1.24", 901 | "punycode": "^1.4.1" 902 | }, 903 | "dependencies": { 904 | "punycode": { 905 | "version": "1.4.1", 906 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", 907 | "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" 908 | } 909 | } 910 | }, 911 | "tunnel-agent": { 912 | "version": "0.6.0", 913 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 914 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", 915 | "requires": { 916 | "safe-buffer": "^5.0.1" 917 | } 918 | }, 919 | "tweetnacl": { 920 | "version": "0.14.5", 921 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", 922 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" 923 | }, 924 | "type-detect": { 925 | "version": "4.0.8", 926 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", 927 | "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", 928 | "dev": true 929 | }, 930 | "typedarray": { 931 | "version": "0.0.6", 932 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/typedarray/-/typedarray-0.0.6.tgz", 933 | "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", 934 | "dev": true 935 | }, 936 | "unwrapper": { 937 | "version": "1.1.0", 938 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/unwrapper/-/unwrapper-1.1.0.tgz", 939 | "integrity": "sha1-+WPIkYRhFG9Se4bxnSkTpck62Vk=" 940 | }, 941 | "uri-js": { 942 | "version": "4.2.2", 943 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", 944 | "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", 945 | "requires": { 946 | "punycode": "^2.1.0" 947 | } 948 | }, 949 | "util-deprecate": { 950 | "version": "1.0.2", 951 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/util-deprecate/-/util-deprecate-1.0.2.tgz", 952 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", 953 | "dev": true 954 | }, 955 | "uuid": { 956 | "version": "3.3.3", 957 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", 958 | "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" 959 | }, 960 | "verror": { 961 | "version": "1.10.0", 962 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", 963 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", 964 | "requires": { 965 | "assert-plus": "^1.0.0", 966 | "core-util-is": "1.0.2", 967 | "extsprintf": "^1.2.0" 968 | } 969 | }, 970 | "which": { 971 | "version": "1.2.14", 972 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/which/-/which-1.2.14.tgz", 973 | "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=", 974 | "dev": true, 975 | "requires": { 976 | "isexe": "^2.0.0" 977 | } 978 | }, 979 | "wrappy": { 980 | "version": "1.0.2", 981 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/wrappy/-/wrappy-1.0.2.tgz", 982 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 983 | "dev": true 984 | }, 985 | "xtend": { 986 | "version": "4.0.2", 987 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 988 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" 989 | }, 990 | "yallist": { 991 | "version": "2.1.2", 992 | "resolved": "https://artifactory.secureserver.net/artifactory/api/npm/node-virt/yallist/-/yallist-2.1.2.tgz", 993 | "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", 994 | "dev": true 995 | } 996 | } 997 | } 998 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "githulk", 3 | "version": "1.7.1", 4 | "description": "Small but powerful Github client", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha test/*.test.js" 8 | }, 9 | "repository": "githulks/githulk", 10 | "keywords": [ 11 | "github", 12 | "api" 13 | ], 14 | "author": "Arnout Kazemier", 15 | "license": "MIT", 16 | "dependencies": { 17 | "diagnostics": "~1.1.0", 18 | "extract-github": "1.0.x", 19 | "mana": "^1.1.0", 20 | "unwrapper": "^1.1.0" 21 | }, 22 | "devDependencies": { 23 | "assume": "^2.1.0", 24 | "mocha": "^4.0.1", 25 | "pre-commit": "~1.2.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/assignees.test.js: -------------------------------------------------------------------------------- 1 | describe('githulk.assignees', function () { 2 | 'use strict'; 3 | 4 | var hulk = require('./hulk') 5 | , assume = hulk.assume 6 | , githulk = hulk.hulk; 7 | 8 | this.timeout(30000); 9 | 10 | describe('.list', function () { 11 | it('returns a list of owners + collab', function (next) { 12 | githulk.assignees.list(hulk.repo, function listed(err, list) { 13 | if (err) return next(err); 14 | 15 | assume(list).to.be.a('array'); 16 | assume(list.some(function some(collab) { 17 | return collab.login === hulk.owner; 18 | })).to.be.true(); 19 | 20 | next(); 21 | }); 22 | }); 23 | }); 24 | 25 | describe('.assignee', function () { 26 | it('confirms that the supplied user can be assigned', function (next) { 27 | githulk.assignees.assignee(hulk.repo, { 28 | assignee: hulk.owner 29 | }, next); 30 | }); 31 | 32 | it('confirms that the supplied user CANNOT be assigned', function (next) { 33 | githulk.assignees.assignee(hulk.repo, { 34 | assignee: hulk.owner + Math.random().toString().split('').map(function (n) { 35 | return String.fromCharCode(97 + (+n || 1)); 36 | }).join('') 37 | }, function (err) { 38 | if (err) return next(); 39 | next(new Error('I should receive an ERRORORR')); 40 | }); 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /test/comments.test.js: -------------------------------------------------------------------------------- 1 | describe('githulk.comments', function () { 2 | 'use strict'; 3 | 4 | var hulk = require('./hulk') 5 | , assume = hulk.assume 6 | , githulk = hulk.hulk 7 | , id; 8 | 9 | this.timeout(30000); 10 | 11 | describe('.list', function () { 12 | it('lists all comments on the specified repository', function (next) { 13 | githulk.comments.list(hulk.repo, hulk.issues.comments, function (err, comments) { 14 | if (err) return next(err); 15 | 16 | assume(comments).is.a('array'); 17 | assume(comments.length).to.equal(1); 18 | assume(comments[0].user.login).to.equal(hulk.owner); 19 | 20 | next(); 21 | }); 22 | }); 23 | }); 24 | 25 | describe('.repository', function () { 26 | it('lists all comments from all isues', function (next) { 27 | githulk.comments.repository(hulk.repo, function (err, comments) { 28 | if (err) return next(err); 29 | 30 | assume(comments).to.be.a('array'); 31 | assume(comments.length).to.be.above(1); 32 | assume(comments.some(function woop(comment) { 33 | return comment.user.login === hulk.owner; 34 | })).to.be.true(); 35 | 36 | next(); 37 | }); 38 | }); 39 | }); 40 | 41 | describe('.get', function () { 42 | it('returns a single comment', function (next) { 43 | githulk.comments.get(hulk.repo, hulk.comment, function (err, data) { 44 | if (err) return next(err); 45 | data = Array.isArray(data) ? data.pop() : data; 46 | 47 | assume(data.user.login === hulk.owner); 48 | assume(data.id).to.equal(hulk.comment); 49 | 50 | next(); 51 | }); 52 | }); 53 | }); 54 | 55 | describe('.create', function () { 56 | var body = 'This comment was automatically generated through the API **this is a test**'; 57 | 58 | it('adds a new comment to the issue', function (next) { 59 | githulk.comments.create(hulk.repo, hulk.issues.comments, { 60 | body: body 61 | }, function (err, data) { 62 | if (err) return next(err); 63 | data = Array.isArray(data) ? data.pop() : data; 64 | 65 | githulk.comments.get(hulk.repo, data.id, function (err, data) { 66 | if (err) return next(err); 67 | data = Array.isArray(data) ? data.pop() : data; 68 | 69 | assume(data.body).to.equal(body); 70 | id = data.id; // needed for deleting 71 | 72 | next(); 73 | }); 74 | }); 75 | }); 76 | }); 77 | 78 | describe('.edit', function () { 79 | var body = 'This comment was changed through the API :+1: '; 80 | 81 | it('changes the content of the comment', function index(next) { 82 | githulk.comments.edit(hulk.repo, id, { body: body }, function (err) { 83 | if (err) return next(err); 84 | 85 | githulk.comments.get(hulk.repo, id, function (err, data) { 86 | if (err) return next(err); 87 | data = Array.isArray(data) ? data.pop() : data; 88 | 89 | assume(data.body).to.equal(body); 90 | next(); 91 | }); 92 | }); 93 | }); 94 | }); 95 | 96 | describe('.remove', function () { 97 | it('removes the issue', function (next) { 98 | githulk.comments.remove(hulk.repo, id, function (err) { 99 | if (err) return next(err); 100 | 101 | githulk.comments.get(hulk.repo, id, function (err, data) { 102 | if (!err) return next(new Error('I should error, does not exist')); 103 | next(); 104 | }); 105 | }); 106 | }); 107 | }); 108 | }); 109 | -------------------------------------------------------------------------------- /test/fixtures/single-file/README.md: -------------------------------------------------------------------------------- 1 | This is a test for #24 which ensures we still return an 2 | Array for directories with a single file. 3 | -------------------------------------------------------------------------------- /test/graphql.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('githulk.graphql', function () { 3 | var hulk = require('./hulk') 4 | , assume = hulk.assume 5 | , githulk = hulk.hulk; 6 | 7 | this.timeout(30000); 8 | 9 | describe('queries', function() { 10 | it('processes queries', function (next) { 11 | var query = '{' + 12 | 'repository(owner:"githulks", name:"githulk") {' + 13 | 'packagejson: object(expression: "master:package.json") {' + 14 | '... on Blob { text }' + 15 | '}' + 16 | 'commit: object(expression: "HEAD") {' + 17 | '... on Commit { HEAD: abbreviatedOid }' + 18 | '}' + 19 | '}' + 20 | '}'; 21 | 22 | githulk.graphql.sendQuery({ 23 | query: query 24 | }, function (err, results) { 25 | if (err) return next(err); 26 | 27 | var data = results.data; 28 | var packageJson = require('../package.json'); 29 | 30 | assume(JSON.parse(data.repository.packagejson.text).name).equals(packageJson.name); 31 | assume(JSON.parse(data.repository.packagejson.text).description).equals(packageJson.description); 32 | 33 | assume(data.repository.commit).hasOwn('HEAD'); 34 | 35 | assume(data.rateLimit).hasOwn('limit'); 36 | assume(data.rateLimit).hasOwn('cost'); 37 | assume(data.rateLimit).hasOwn('remaining'); 38 | assume(data.rateLimit).hasOwn('resetAt'); 39 | 40 | next(); 41 | } 42 | ); 43 | }); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /test/hulk.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assume = require('assume') 4 | , GitHulk = require('../'); 5 | 6 | /** 7 | * A pre-configured GitHulk instance which will be used for testing purposes. 8 | * 9 | * @type {GitHulk} 10 | * @public 11 | */ 12 | exports.hulk = new GitHulk({ token: process.env.GITHULK_TEST }); 13 | 14 | /** 15 | * The id's of issues which are in various of states. 16 | * 17 | * @type {Object} 18 | * @public 19 | */ 20 | exports.issues = { closed: 3, open: 2, comments: 2 }; 21 | 22 | /** 23 | * The Github location of the repository. 24 | * 25 | * @type {String} 26 | * @public 27 | */ 28 | exports.repo = 'githulks/githulk'; 29 | 30 | /** 31 | * Name of the owner of the test suite and repository. 32 | * 33 | * @type {String} 34 | * @public 35 | */ 36 | exports.owner = '3rd-Eden'; 37 | 38 | /** 39 | * Example webhook fixture options used for testing 40 | */ 41 | exports.webhooks = { 42 | web: { 43 | repo: 'githulks/webhook-test', 44 | options: { 45 | name: 'web', 46 | config: { 47 | url: 'http://my-jenkins-server/ghprbhook/' 48 | }, 49 | events: ['pull_request', 'pull_request_review_comment', 'issue_comment'], 50 | active: true 51 | } 52 | }, 53 | // There are no more github service hooks, just regular webhooks 54 | jenkins: { 55 | repo: 'githulks/webhook-test', 56 | type: 'web', 57 | options: { 58 | name: 'web', 59 | config: { 60 | url: 'https://my-fun-times/github_webhook/' 61 | }, 62 | events: ['push'], 63 | active: true 64 | } 65 | } 66 | }; 67 | 68 | /** 69 | * A single comment id which we need to retrieve. 70 | * 71 | * @type {Number} 72 | * @public 73 | */ 74 | exports.comment = 55562200; 75 | 76 | /** 77 | * Reference to the GitHulk constructor. 78 | * 79 | * @type {Function} 80 | * @public 81 | */ 82 | exports.GitHulk = GitHulk; 83 | 84 | /** 85 | * Our assertion library. 86 | * 87 | * @type {Function} 88 | * @public 89 | */ 90 | exports.assume = assume; 91 | -------------------------------------------------------------------------------- /test/issues.test.js: -------------------------------------------------------------------------------- 1 | describe('githulk.issues', function () { 2 | 'use strict'; 3 | 4 | var hulk = require('./hulk') 5 | , assume = hulk.assume 6 | , githulk = hulk.hulk 7 | , edit; 8 | 9 | this.timeout(30000); 10 | 11 | describe('.list', function () { 12 | it('lists open all issues', function (next) { 13 | githulk.issues.list(function (err, list) { 14 | if (err) return next(err); 15 | 16 | assume(list).is.a('array'); 17 | assume(list.every(function (issue) { 18 | return issue.state === 'open'; 19 | })).is.true(); 20 | 21 | next(); 22 | }); 23 | }); 24 | 25 | it('lists closed all issues', function (next) { 26 | githulk.issues.list({ state: 'closed' }, function (err, list) { 27 | if (err) return next(err); 28 | 29 | assume(list).is.a('array'); 30 | assume(list.every(function (issue) { 31 | return issue.state === 'closed'; 32 | })).is.true(); 33 | 34 | next(); 35 | }); 36 | }); 37 | }); 38 | 39 | describe('.user', function () { 40 | it('lists open all issues', function (next) { 41 | githulk.issues.user(function (err, list) { 42 | if (err) return next(err); 43 | 44 | assume(list).is.a('array'); 45 | assume(list.every(function (issue) { 46 | return issue.state === 'open'; 47 | })).is.true(); 48 | 49 | next(); 50 | }); 51 | }); 52 | 53 | it('lists closed all issues', function (next) { 54 | githulk.issues.user({ state: 'closed' }, function (err, list) { 55 | if (err) return next(err); 56 | 57 | assume(list).is.a('array'); 58 | assume(list.every(function (issue) { 59 | return issue.state === 'closed'; 60 | })).is.true(); 61 | 62 | next(); 63 | }); 64 | }); 65 | }); 66 | 67 | describe('.repository', function () { 68 | it('lists open all issues', function (next) { 69 | githulk.issues.repository(hulk.repo, function (err, list) { 70 | if (err) return next(err); 71 | 72 | assume(list).is.a('array'); 73 | assume(list.every(function (issue) { 74 | return issue.state === 'open'; 75 | })).is.true(); 76 | 77 | next(); 78 | }); 79 | }); 80 | 81 | it('lists closed all issues', function (next) { 82 | githulk.issues.repository(hulk.repo, { state: 'closed' }, function (err, list) { 83 | if (err) return next(err); 84 | 85 | assume(list).is.a('array'); 86 | assume(list.every(function (issue) { 87 | return issue.state === 'closed'; 88 | })).is.true(); 89 | 90 | next(); 91 | }); 92 | }); 93 | }); 94 | 95 | describe('.get', function () { 96 | it('gets the information for one single issue', function (next) { 97 | githulk.issues.get(hulk.repo, hulk.issues.open, function (err, data) { 98 | if (err) return next(err); 99 | var issue = data.pop(); 100 | 101 | assume(issue.number).equals(hulk.issues.open); 102 | next(); 103 | }); 104 | }); 105 | }); 106 | 107 | describe('.create', function () { 108 | it('creates a new issue', function (next) { 109 | githulk.issues.create(hulk.repo, { 110 | title: 'Generated through API, please ignore', 111 | body: 'This issue has been generated through the `githulk` api', 112 | labels: ['used by test suite'], 113 | assignee: hulk.owner 114 | }, function (err, data) { 115 | if (err) return next(err); 116 | var issue = data.pop(); 117 | 118 | edit = issue.number; 119 | assume(edit).is.a('number'); 120 | 121 | githulk.issues.get(hulk.repo, edit, next); 122 | }); 123 | }); 124 | }); 125 | 126 | describe('.edit', function () { 127 | it('can edit all the things', function (next) { 128 | githulk.issues.edit(hulk.repo, edit, { 129 | state: 'closed', 130 | title: 'Editted through the API, please ignore', 131 | body: '```how do you like them markdowns```' 132 | }, next); 133 | }); 134 | }); 135 | }); 136 | -------------------------------------------------------------------------------- /test/repository.test.js: -------------------------------------------------------------------------------- 1 | describe('githulk.repository', function () { 2 | 'use strict'; 3 | 4 | var hulk = require('./hulk') 5 | , assume = hulk.assume 6 | , githulk = hulk.hulk 7 | , edit; 8 | 9 | this.timeout(30000); 10 | 11 | describe('.branches', function () { 12 | it('lists all branches', function (next) { 13 | githulk.repository.branches('twbs/bootstrap', function (err, branches) { 14 | if (err) return next(err); 15 | 16 | assume(branches).is.a('array'); 17 | branches.forEach(function (branch) { 18 | assume(branch.name).to.be.a('string'); 19 | assume(branch.commit).to.be.an('object'); 20 | }); 21 | 22 | next(); 23 | }); 24 | }); 25 | }); 26 | 27 | describe('.branch', function () { 28 | it('gets details about a branch', function (next) { 29 | githulk.repository.branch('twbs/bootstrap', { branch: 'gh-pages' }, function (err, branch) { 30 | if (err) return next(err); 31 | 32 | assume(branch).is.an('object'); 33 | assume(branch.name).to.be.a('string'); 34 | assume(branch.commit).to.be.an('object'); 35 | 36 | next(); 37 | }); 38 | }); 39 | }); 40 | 41 | describe('.tags', function () { 42 | it('lists all tags', function (next) { 43 | githulk.repository.tags('twbs/bootstrap', function (err, tags) { 44 | if (err) return next(err); 45 | 46 | assume(tags).is.a('array'); 47 | tags.forEach(function (tag) { 48 | assume(tag.ref).to.be.a('string'); 49 | 50 | assume(tag.object).to.be.an('object'); 51 | assume(tag.object.sha).to.be.a('string'); 52 | assume(tag.object.type).to.be.a('string'); 53 | }); 54 | 55 | next(); 56 | }); 57 | }); 58 | }); 59 | 60 | describe('.tag', function () { 61 | it('gets a single tag', function (next) { 62 | githulk.repository.tag('twbs/bootstrap', { tag: 'v3.3.7' }, function (err, tag) { 63 | if (err) return next(err); 64 | 65 | assume(tag).is.an('object'); 66 | assume(tag.ref).to.be.a('string'); 67 | assume(tag.ref).includes('v3.3.7'); 68 | 69 | assume(tag.object).to.be.an('object'); 70 | assume(tag.object.sha).to.be.a('string'); 71 | assume(tag.object.type).to.be.a('string'); 72 | 73 | next(); 74 | }); 75 | }); 76 | }); 77 | 78 | describe('.commits', function () { 79 | it('lists commits', function (next) { 80 | githulk.repository.commits('foreverjs/forever', function (err, commits) { 81 | if (err) return next(err); 82 | 83 | assume(commits).is.a('array'); 84 | commits.forEach(function (commit) { 85 | assume(commit.sha).to.be.a('string'); 86 | assume(commit.commit).to.be.an('object'); 87 | }); 88 | 89 | next(); 90 | }); 91 | }); 92 | }); 93 | 94 | describe('.commitSha', function () { 95 | it('get the LONG SHA for a specific commit', function (next) { 96 | 97 | githulk.repository.commitSha('twbs/bootstrap', { sha: 'f2e912b' }, function (err, longSha) { 98 | if (err) return next(err); 99 | 100 | assume(longSha).is.a('string'); 101 | assume(longSha).equals('f2e912bb0e5041fead48bdbcaaa23de1c40291d5'); 102 | 103 | next(); 104 | }); 105 | }); 106 | }); 107 | 108 | describe('.contents', function () { 109 | it('returns an error when getting files from an unknown repo', function (next) { 110 | githulk.repository.contents('3rd-Eden/githulk-doesnt-exist-please', { path: '/index.js' }, function (err) { 111 | assume(err).is.a('error'); 112 | next(); 113 | }); 114 | }); 115 | 116 | it('returns array for directory', function (next) { 117 | githulk.repository.contents(hulk.repo, { path: 'endpoints' }, function (err, results) { 118 | if (err) return next(err); 119 | 120 | assume(results).is.a('array'); 121 | 122 | next(); 123 | }); 124 | }); 125 | 126 | it('returns array for directory with a single file', function (next) { 127 | githulk.repository.contents('indexzero/indexzero', { path: 'test' }, function (err, results) { 128 | if (err) return next(err); 129 | 130 | assume(results).is.a('array'); 131 | 132 | next(); 133 | }); 134 | }); 135 | 136 | it('returns object for file', function (next) { 137 | githulk.repository.contents(hulk.repo, { path: '/index.js' }, function (err, result) { 138 | if (err) return next(err); 139 | 140 | assume(result).to.be.an('object'); 141 | assume(result).owns('content'); 142 | 143 | next(); 144 | }); 145 | }); 146 | 147 | it('returns object for submodule', function (next) { 148 | githulk.repository.contents('githulks/githulk-test', { path: 'githulk' }, function (err, result) { 149 | if (err) return next(err); 150 | 151 | assume(result).to.be.an('object'); 152 | assume(result).owns('submodule_git_url'); 153 | 154 | next(); 155 | }); 156 | }); 157 | 158 | it('returns symlink object for symlink to a directory', function (next) { 159 | githulk.repository.contents('githulks/githulk-test', { path: 'symlink-dir' }, function (err, result) { 160 | if (err) return next(err); 161 | 162 | assume(result).to.be.an('object'); 163 | assume(result.type).equals('symlink'); 164 | assume(result).owns('target'); 165 | 166 | next(); 167 | }); 168 | }); 169 | 170 | it('returns file object for symlink to a file', function (next) { 171 | githulk.repository.contents('githulks/githulk-test', { path: 'symlink' }, function (err, result) { 172 | if (err) return next(err); 173 | 174 | assume(result).to.be.an('object'); 175 | assume(result.type).equals('file'); 176 | 177 | next(); 178 | }); 179 | }); 180 | 181 | }); 182 | }); 183 | -------------------------------------------------------------------------------- /test/search.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | describe('githulk.search', function () { 3 | var hulk = require('./hulk') 4 | , assume = hulk.assume 5 | , githulk = hulk.hulk; 6 | 7 | this.timeout(30000); 8 | 9 | describe('query', function() { 10 | it('processes queries', function (next) { 11 | githulk.search.query('code', { 12 | query: 'assume repo:githulks/githulk filename:package.json' 13 | }, function (err, results) { 14 | if (err) return next(err); 15 | 16 | assume(results).is.a('array'); 17 | assume(results[0].items).is.a('array'); 18 | assume(results[0].total_count).equals(1); 19 | 20 | next(); 21 | } 22 | ); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/webhooks.test.js: -------------------------------------------------------------------------------- 1 | describe('githulk.webhooks', function () { 2 | 'use strict'; 3 | 4 | var hulk = require('./hulk') 5 | , assume = hulk.assume 6 | , githulk = hulk.hulk 7 | , hooks = hulk.webhooks; 8 | 9 | this.timeout(30000); 10 | 11 | describe('.create', function () { 12 | Object.keys(hooks).forEach(function (type) { 13 | var hook = hooks[type]; 14 | it('should create webhook for ' + type, function (next) { 15 | githulk.webhooks.create(hook.repo, hook.options, function (err, result) { 16 | assume(err).to.be.falsey(); 17 | assume(result.name).equals(hook.type || type); 18 | next(); 19 | }); 20 | }); 21 | }); 22 | }); 23 | 24 | describe('.list', function () { 25 | it('lists the set of webhooks that currently exist', function (next) { 26 | githulk.webhooks.list(hooks.jenkins.repo, function (err, result) { 27 | assume(err).to.be.falsey(); 28 | assume(result.length).equals(2); 29 | next(); 30 | }); 31 | }); 32 | }); 33 | 34 | describe('.get', function () { 35 | var webhooks; 36 | before(function (next) { 37 | githulk.webhooks.list(hooks.jenkins.repo, function (err, results) { 38 | webhooks = results; 39 | next(err); 40 | }); 41 | }); 42 | 43 | it('should be able to get the all webhook by id', function (next) { 44 | next = assume.wait(2, 4, next); 45 | webhooks.forEach(function (hook) { 46 | githulk.webhooks.get(hooks.jenkins.repo, { 47 | id: hook.id 48 | }, function (err, result) { 49 | assume(err).to.be.falsey(); 50 | assume(result.name).equals(hook.name); 51 | next(); 52 | }); 53 | }); 54 | }); 55 | }); 56 | 57 | describe('.delete', function () { 58 | var webhooks; 59 | before(function (next) { 60 | githulk.webhooks.list(hooks.jenkins.repo, function (err, results) { 61 | webhooks = results; 62 | next(err); 63 | }); 64 | }); 65 | 66 | it('should be able to delete the webhook by id', function (next) { 67 | next = assume.wait(2, 2, next); 68 | webhooks.forEach(function (hook) { 69 | githulk.webhooks.delete(hooks.jenkins.repo, { 70 | id: hook.id 71 | }, function (err) { 72 | assume(err).is.falsey(); 73 | next(); 74 | }); 75 | }); 76 | }); 77 | }); 78 | }); 79 | --------------------------------------------------------------------------------