Skip to content

Commit 4013f34

Browse files
committed
Merge pull request #25 from coderly/cache-credentials
Cache credentials
2 parents 9f64bb6 + 7c96412 commit 4013f34

File tree

5 files changed

+153
-58
lines changed

5 files changed

+153
-58
lines changed

lib/cli/publish.js

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,10 @@
11
#! /usr/bin/env node
22

33
var program = require('commander');
4-
var cliPrompt = require('prompt');
54

65
var dasherize = require('../utils/string').dasherize;
76
var ui = require('../ui');
87

9-
cliPrompt.message = '';
10-
cliPrompt.delimiter = '';
11-
12-
var promptSchema = {
13-
properties: {
14-
username: {
15-
description: 'GitHub username: ',
16-
message: 'A username is required.',
17-
required: true
18-
},
19-
password: {
20-
description: 'GitHub password: ',
21-
message: 'A password is required.',
22-
required: true,
23-
hidden: true
24-
}
25-
}
26-
};
27-
288
program
299
.arguments('<addonName>')
3010
.usage('<addonName> -u <username> -p <password>')
@@ -48,20 +28,7 @@ function runPublishTask() {
4828
}
4929

5030
if (program.addonName) {
51-
if(program.username && program.password) {
52-
runPublishTask();
53-
} else {
54-
cliPrompt.start();
55-
cliPrompt.get(promptSchema, function(error, result) {
56-
if (error) {
57-
return ui.error(error);
58-
} else {
59-
program.username = result.username;
60-
program.password = result.password;
61-
runPublishTask();
62-
}
63-
});
64-
}
31+
runPublishTask();
6532
} else {
6633
program.outputHelp();
6734
}

lib/tasks/publish.js

Lines changed: 79 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,11 @@ var GitHubAPI = require('github');
77

88
var Promise = require('../ext/promise');
99
var ui = require('../ui');
10+
var gitHubCredentials = require('../utils/github-credentials');
11+
var promptForCredentials = require('../utils/credentials-prompt');
1012

11-
function createGitSignature(options) {
12-
var millisecondsFromStandardEpoch = Date.now();
13-
var secondsFromStandardEpoch = Math.floor(millisecondsFromStandardEpoch / 1000);
14-
15-
return git.Signature.create('ember-micro', options.username, secondsFromStandardEpoch, 60);
13+
function createGitSignature(repo) {
14+
return git.Signature.default(repo);
1615
}
1716

1817
function createLocalRepo(options) {
@@ -42,16 +41,15 @@ function addExistingFilesToLocalRepo(localRepo) {
4241
}
4342

4443
function createInitialCommitOnLocalRepo(options, localRepo, oid) {
45-
var signature = createGitSignature(options);
46-
44+
var signature = createGitSignature(localRepo);
4745
return localRepo.createCommit("HEAD", signature, signature, "Initial commit", oid, []);
4846
}
4947

50-
function createRemoteRepo(gitHubApi, repoName) {
48+
function createRemoteRepo(github, repoName) {
5149
ui.write('Creating remote repository...');
5250

5351
return new Promise(function(resolve, reject) {
54-
gitHubApi.repos.create({
52+
github.repos.create({
5553
name: repoName
5654
}, function(err, result) {
5755
if (err) {
@@ -72,7 +70,12 @@ function pushToRepo(options, localRepo, remote) {
7270

7371
remote.setCallbacks({
7472
credentials: function() {
75-
return git.Cred.userpassPlaintextNew(options.username, options.password);
73+
// the code here is misleading, but it works
74+
// usually, we pass in a username and password to the function,
75+
// but passing in the two parameters bellow makes the functio work with an
76+
// oauth token as well. there is no properly named function
77+
// available that does this.
78+
return git.Cred.userpassPlaintextNew(options.token, 'x-oauth-basic');
7679
}
7780
});
7881

@@ -84,33 +87,85 @@ function pushToRepo(options, localRepo, remote) {
8487
return remote.push(refSpecs, pushOptions, signature, message);
8588
}
8689

87-
function initializeGitHubAPI() {
88-
return new GitHubAPI({
90+
function initializeGitHub(options) {
91+
var github = new GitHubAPI({
8992
version: '3.0.0',
9093
});
94+
95+
if (options.username && options.password) {
96+
authenticateWithUserProvidedCredentials(github,options);
97+
return createToken(github).then(function(token) {
98+
options.token = token;
99+
return gitHubCredentials.setToken(token);
100+
}).then(function() {
101+
return github;
102+
});
103+
} else {
104+
return gitHubCredentials.getToken().then(function(token) {
105+
options.token = token;
106+
authenticateWithToken(github, token);
107+
return github;
108+
}).catch(function() {
109+
return promptForCredentials().then(function(credentials) {
110+
options.username = credentials.username;
111+
options.password = credentials.password;
112+
authenticateWithUserProvidedCredentials(github,credentials);
113+
return createToken(github).then(function(token) {
114+
options.token = token;
115+
return gitHubCredentials.setToken(token);
116+
}).then(function() {
117+
return github;
118+
});
119+
});
120+
});
121+
}
122+
}
123+
124+
function authenticateWithToken(github, token) {
125+
github.authenticate({
126+
type: 'oauth',
127+
token: token,
128+
});
91129
}
92130

93-
function authenticateGitHubAPI(gitHubApi, options) {
94-
gitHubApi.authenticate({
131+
function authenticateWithUserProvidedCredentials(github, options) {
132+
github.authenticate({
95133
type: 'basic',
96134
username: options.username,
97135
password: options.password
98136
});
99137
}
100138

139+
function createToken(github) {
140+
return new Promise(function(resolve, reject) {
141+
github.authorization.create({
142+
scopes: ['user', 'public_repo', 'repo', 'repo:status', 'gist'],
143+
note: 'ember-micro-addon@' + Date.now(),
144+
note_url: 'https://github.com/coderly/ember-micro-addon',
145+
headers: {
146+
'X-GitHub-OTP': 'two-factor-code'
147+
},
148+
}, function(err, res) {
149+
if (err) {
150+
reject(err);
151+
} else {
152+
resolve(res.token);
153+
}
154+
});
155+
});
156+
}
157+
101158
module.exports = function(options) {
102159
options.workingDirectory = path.join(process.cwd(), options.addonName);
103160

104-
var gitHubApi = initializeGitHubAPI();
105-
106-
authenticateGitHubAPI(gitHubApi, options);
107-
108-
ui.write('Publishing ' + options.addonName + ' to GitHub...');
109-
return createLocalRepo(options).then(function(localRepo) {
110-
return createRemoteRepo(gitHubApi, options.addonName).then(function(remoteRepo) {
111-
return addRemoteOrigin(localRepo, remoteRepo);
112-
}).then(function(remoteResult) {
113-
return pushToRepo(options, localRepo, remoteResult);
161+
return initializeGitHub(options).then(function(github) {
162+
ui.write('Publishing ' + options.addonName + ' to GitHub...');
163+
return createLocalRepo(options).then(function(localRepo) {
164+
return createRemoteRepo(github, options.addonName).then(function(remoteRepo) {
165+
return addRemoteOrigin(localRepo, remoteRepo);
166+
}).then(function(remoteResult) {
167+
return pushToRepo(options, localRepo, remoteResult);
168+
});
114169
});
115170
});
116171

lib/utils/credentials-prompt.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
var cliPrompt = require('prompt');
2+
3+
cliPrompt.message = '';
4+
cliPrompt.delimiter = '';
5+
6+
var promptSchema = {
7+
properties: {
8+
username: {
9+
description: 'GitHub username: ',
10+
message: 'A username is required.',
11+
required: true
12+
},
13+
password: {
14+
description: 'GitHub password: ',
15+
message: 'A password is required.',
16+
required: true,
17+
hidden: true
18+
}
19+
}
20+
};
21+
22+
var Promise = require('../ext/promise');
23+
24+
module.exports = function promptForCredentials() {
25+
cliPrompt.start();
26+
return new Promise(function(resolve, reject) {
27+
cliPrompt.get(promptSchema, function(error, result) {
28+
if (error) {
29+
reject(error);
30+
} else {
31+
resolve(result);
32+
}
33+
});
34+
});
35+
};

lib/utils/github-credentials.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/* jshint node:true*/
2+
3+
var storage = require('node-persist');
4+
var path = require('path');
5+
6+
var TOKEN_KEY = 'ember-micro-github-token';
7+
var CREDENTIAL_FOLDER = '.ember-micro-addon-credentials';
8+
9+
var Promise = require('../ext/promise');
10+
var setItem = Promise.denodeify(storage.setItem);
11+
var getItem = Promise.denodeify(storage.getItem);
12+
13+
14+
var cachedToken;
15+
16+
function getUserHome() {
17+
var userHome = process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME'];
18+
return path.join(userHome, CREDENTIAL_FOLDER);
19+
}
20+
21+
storage.initSync({ dir: getUserHome() });
22+
23+
module.exports = {
24+
setToken: function(token) {
25+
cachedToken = token;
26+
return setItem(TOKEN_KEY, token);
27+
},
28+
getToken: function() {
29+
return new Promise(function(resolve) {
30+
if (cachedToken) {
31+
resolve(cachedToken);
32+
} else {
33+
return getItem(TOKEN_KEY).then(resolve);
34+
}
35+
});
36+
}
37+
};

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"fs-extra": "^0.19.0",
4343
"github": "^0.2.4",
4444
"lodash": "^3.9.3",
45+
"node-persist": "0.0.4",
4546
"nodegit": "^0.4.1",
4647
"prompt": "^0.2.14",
4748
"rsvp": "^3.0.18"

0 commit comments

Comments
 (0)