1 | #!/usr/bin/env node
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 | var which = require('which'),
|
12 | spawn = require('child_process').spawn,
|
13 | exec = require('child_process').exec,
|
14 | chalk = require('chalk'),
|
15 | https = require('https'),
|
16 | prompt = require('prompt'),
|
17 | Travis = require('travis-ci'),
|
18 | travis,
|
19 | good = chalk.green("✔"),
|
20 | bad = chalk.red("✖"),
|
21 | progress = chalk.yellow("♢");
|
22 |
|
23 | if (process.platform === 'win32') {
|
24 | good = chalk.green('OK');
|
25 | bad = chalk.red('X');
|
26 | progress = chalk.yellow('O');
|
27 | }
|
28 |
|
29 | exports.good = good;
|
30 | exports.bad = bad;
|
31 |
|
32 | var getInfo = function(callback) {
|
33 | which('git', function(err, git) {
|
34 | if (err) {
|
35 | console.error(err);
|
36 | process.exit(1);
|
37 | }
|
38 | var child = spawn(git, ['remote', '-v']),
|
39 | user, repo;
|
40 | child.stdout.on('data', function(data) {
|
41 | data = data.toString().split('\n');
|
42 | data.forEach(function(line) {
|
43 | if (line.indexOf('origin') === 0 && !user && !repo) {
|
44 | line = line.replace(' (fetch)', '').replace(' (push)', '');
|
45 | var origin = line.split('\t')[1];
|
46 | if (origin.indexOf('git@') === 0) {
|
47 |
|
48 | origin = origin.replace('git@github.com:', '').replace('.git', '').split('/');
|
49 | } else if (origin.indexOf('git://') === 0) {
|
50 | origin = origin.replace('git://github.com/', '').replace('.git', '').split('/');
|
51 | } else if (origin.indexOf('https://') === 0) {
|
52 | origin = origin.replace('https://github.com/', '').replace('.git', '').split('/');
|
53 | }
|
54 | if (origin && origin.length) {
|
55 | user = origin[0].trim();
|
56 | repo = origin[1].trim();
|
57 | if (!user || !repo) {
|
58 | throw('failed to parse git remote');
|
59 | }
|
60 | exec(git + ' status', {
|
61 | cwd: process.cwd()
|
62 | }, function(err, stdout) {
|
63 | var branch = stdout.trim().split('\n')[0];
|
64 | branch = branch.replace('# On branch ', '').replace('On branch ', '') || 'master';
|
65 | callback(user, repo, branch);
|
66 | });
|
67 | }
|
68 | }
|
69 | });
|
70 | });
|
71 | });
|
72 | };
|
73 |
|
74 | exports.info = getInfo;
|
75 |
|
76 | var fetch = function(user, repo, branch, callback) {
|
77 | travis.repos(user, repo).builds().get(function(err, res) {
|
78 | if (err) {
|
79 | return callback(err);
|
80 | }
|
81 | var build = res.builds || res,
|
82 | item, other, commit, msg = null;
|
83 |
|
84 | if (build && !build.hasOwnProperty('length')) {
|
85 | callback('failed to fetch info for ' +user + '/' + repo);
|
86 | return;
|
87 | }
|
88 |
|
89 | if (Array.isArray(res)) {
|
90 | res.some(function(i) {
|
91 | var ret = (i.branch === branch);
|
92 | if (ret) {
|
93 | item = i;
|
94 | }
|
95 | return ret;
|
96 | });
|
97 | other = res[0];
|
98 | } else {
|
99 | res.commits.some(function(i) {
|
100 | var ret = (i.branch === branch);
|
101 | if (ret) {
|
102 | commit = i;
|
103 | }
|
104 | return ret;
|
105 | });
|
106 |
|
107 | res.builds.some(function(i) {
|
108 | var ret = (i.commit_id === commit.id);
|
109 | if (ret) {
|
110 | item = i;
|
111 | }
|
112 | return ret;
|
113 | });
|
114 |
|
115 | other = res.builds[0];
|
116 | commit = res.commits[0];
|
117 | }
|
118 |
|
119 | if (!item) {
|
120 | msg = 'no recent builds on ' + branch + ' showing latest';
|
121 | item = other;
|
122 | }
|
123 |
|
124 | if (!item) {
|
125 | return callback('no item found');
|
126 | }
|
127 |
|
128 | callback(null, {
|
129 | msg: msg,
|
130 | item: item,
|
131 | commit: commit
|
132 | });
|
133 | });
|
134 |
|
135 | };
|
136 |
|
137 | exports.fetch = fetch;
|
138 |
|
139 | var isPublicRepository = function (user, repo, callback) {
|
140 | https.request({
|
141 | method: 'HEAD',
|
142 | host: 'api.github.com',
|
143 | path: '/repos/' + user + '/' + repo,
|
144 | headers: {
|
145 | 'user-agent': 'git-travis cli tool'
|
146 | }
|
147 | }, function (res) {
|
148 |
|
149 | res.on('data', function() { });
|
150 | if (res.statusCode === 200) {
|
151 | return callback(null, true);
|
152 | } else if (res.statusCode === 404) {
|
153 | return callback(null, false);
|
154 | } else {
|
155 | return callback('unknown');
|
156 | }
|
157 | }).end();
|
158 | };
|
159 |
|
160 | exports.print = function(user, repo, branch, callback) {
|
161 | console.log('Fetching build status for', user + '/' + repo + ':' + branch);
|
162 |
|
163 | var onFetch = function(err, data) {
|
164 | var status, commit, item;
|
165 |
|
166 | if (err) {
|
167 | if (callback) {
|
168 | return callback(err);
|
169 | } else {
|
170 | throw err;
|
171 | }
|
172 | }
|
173 |
|
174 | if (data.msg) {
|
175 | console.log(' ', data.msg);
|
176 | }
|
177 |
|
178 | commit = data.commit;
|
179 | item = data.item;
|
180 |
|
181 | status = (item.result ? bad : good);
|
182 |
|
183 | if (item.status === null) {
|
184 | status = progress;
|
185 | }
|
186 |
|
187 | console.log(' ', status, user + '/' + repo);
|
188 | travis.repos(user, repo).builds(item.id).get(function(err, res) {
|
189 | if (err) {
|
190 | console.log(' ', bad, 'failed to fetch info for', user + '/' + repo);
|
191 | if (callback) {
|
192 | callback();
|
193 | }
|
194 | return;
|
195 | }
|
196 | var json = res,
|
197 | message = json.message || json.commit.message,
|
198 | sha = json.commit.sha || json.commit,
|
199 | url = json.commit.compare_url || json.compare_url,
|
200 | branch = json.branch || json.commit.branch,
|
201 | name = json.author_name || json.commit.author_name,
|
202 | email = json.author_email || json.commit.author_email,
|
203 | state = json.state || json.build.state,
|
204 | jobs = json.matrix || json.jobs;
|
205 |
|
206 | message = message.split('\n')[0];
|
207 | sha = sha.substring(0, 7);
|
208 |
|
209 | console.log(' ', 'Compare: ', url);
|
210 | console.log(' ', ((state === 'failed') ? bad : ((state === 'passed') ? good: progress)), sha, '(' + branch + ')',
|
211 | message, '(' + name + ' <' + email + '>)', chalk.white('(' + state + ')'));
|
212 |
|
213 | jobs.forEach(function(m) {
|
214 | var lang = m.config.language;
|
215 | console.log(' ', ((m.state === 'failed') ? bad : ((m.finished_at === null) ? progress : good)),
|
216 | m.number, lang, m.config[lang], chalk.white('(' + m.state + ')'));
|
217 | });
|
218 | if (callback) {
|
219 | callback();
|
220 | }
|
221 | });
|
222 | },
|
223 | travisAuthWithUsernamePassword = function (callback) {
|
224 | prompt.start();
|
225 | prompt.get({
|
226 | properties: {
|
227 | username: {
|
228 | required: true
|
229 | },
|
230 | password: {
|
231 | required: true,
|
232 | hidden: true
|
233 | }
|
234 | }
|
235 | }, function (err, result) {
|
236 | if (err) {
|
237 | return callback(err);
|
238 | }
|
239 |
|
240 | var auth = {
|
241 | username: result.username,
|
242 | password: result.password
|
243 | };
|
244 | callback(null, auth);
|
245 | });
|
246 | },
|
247 | getGHToken = function () {
|
248 | return process.env.GITHUB_ACCESS_TOKEN;
|
249 | },
|
250 | travisAuthWithToken = function (callback) {
|
251 |
|
252 | var auth = {
|
253 | github_token: getGHToken()
|
254 | };
|
255 | callback(null, auth);
|
256 | },
|
257 | hasToken = !!getGHToken();
|
258 |
|
259 | isPublicRepository(user, repo, function (err, repositoryIsPublic) {
|
260 | if (err) {
|
261 | return callback(err);
|
262 | }
|
263 |
|
264 | travis = new Travis({
|
265 | version: '2.0.0',
|
266 | pro: !repositoryIsPublic
|
267 | });
|
268 |
|
269 | if (repositoryIsPublic) {
|
270 | fetch(user, repo, branch, onFetch);
|
271 | } else {
|
272 | var travisAuthMethod = hasToken ?
|
273 | travisAuthWithToken :
|
274 | travisAuthWithUsernamePassword;
|
275 |
|
276 | travisAuthMethod(function (err, authMethod) {
|
277 | if (err) {
|
278 | return callback(err);
|
279 | }
|
280 |
|
281 | travis.authenticate(authMethod, function (err) {
|
282 | if (err) {
|
283 | return callback(err);
|
284 | }
|
285 |
|
286 | console.log();
|
287 | fetch(user, repo, branch, onFetch);
|
288 | });
|
289 | });
|
290 | }
|
291 | });
|
292 | };
|
293 |
|