-
Notifications
You must be signed in to change notification settings - Fork 61
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Gracefully abort if user does not have proper ssh access to github (#61)
* Gracefully abort if user does not have ssh access to github * Update snapshot
- Loading branch information
Showing
7 changed files
with
114 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
/node_modules | ||
/coverage | ||
/.coveralls.yml | ||
/test/.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,60 +1,70 @@ | ||
const ERROR_CODES = { | ||
INVALID_CONFIG_ERROR_CODE: 'INVALID_CONFIG_ERROR_CODE', | ||
GITHUB_ERROR_CODE: 'GITHUB_ERROR_CODE', | ||
MISSING_DATA_ERROR_CODE: 'MISSING_DATA_ERROR_CODE', | ||
ABORT_APPLICATION_ERROR_CODE: 'ABORT_APPLICATION_ERROR_CODE', | ||
INVALID_JSON_ERROR_CODE: 'INVALID_JSON_ERROR_CODE' | ||
GITHUB_API_ERROR_CODE: 'GITHUB_API_ERROR_CODE', | ||
GITHUB_SSH_ERROR_CODE: 'GITHUB_SSH_ERROR_CODE', | ||
INVALID_CONFIG_ERROR_CODE: 'INVALID_CONFIG_ERROR_CODE', | ||
INVALID_JSON_ERROR_CODE: 'INVALID_JSON_ERROR_CODE', | ||
MISSING_DATA_ERROR_CODE: 'MISSING_DATA_ERROR_CODE' | ||
}; | ||
|
||
class InvalidConfigError extends Error { | ||
constructor(...args) { | ||
super(...args); | ||
Error.captureStackTrace(this, InvalidConfigError); | ||
this.code = ERROR_CODES.INVALID_CONFIG_ERROR_CODE; | ||
class AbortApplicationError extends Error { | ||
constructor(message) { | ||
super(message); | ||
Error.captureStackTrace(this, AbortApplicationError); | ||
this.code = ERROR_CODES.ABORT_APPLICATION_ERROR_CODE; | ||
this.message = message; | ||
} | ||
} | ||
|
||
class GithubError extends Error { | ||
class GithubApiError extends Error { | ||
constructor(message) { | ||
super(); | ||
Error.captureStackTrace(this, GithubError); | ||
this.code = ERROR_CODES.GITHUB_ERROR_CODE; | ||
super(message); | ||
Error.captureStackTrace(this, GithubApiError); | ||
this.code = ERROR_CODES.GITHUB_API_ERROR_CODE; | ||
this.message = JSON.stringify(message, null, 4); | ||
} | ||
} | ||
|
||
class MissingDataError extends Error { | ||
class GithubSSHError extends Error { | ||
constructor(message) { | ||
super(); | ||
Error.captureStackTrace(this, MissingDataError); | ||
this.code = ERROR_CODES.MISSING_DATA_ERROR_CODE; | ||
super(message); | ||
Error.captureStackTrace(this, GithubSSHError); | ||
this.code = ERROR_CODES.GITHUB_SSH_ERROR_CODE; | ||
this.message = message; | ||
} | ||
} | ||
|
||
class AbortApplicationError extends Error { | ||
class InvalidConfigError extends Error { | ||
constructor(message) { | ||
super(); | ||
Error.captureStackTrace(this, MissingDataError); | ||
this.code = ERROR_CODES.ABORT_APPLICATION_ERROR_CODE; | ||
this.message = message; | ||
super(message); | ||
Error.captureStackTrace(this, InvalidConfigError); | ||
this.code = ERROR_CODES.INVALID_CONFIG_ERROR_CODE; | ||
} | ||
} | ||
|
||
class InvalidJsonError extends Error { | ||
constructor(message, filepath, fileContents) { | ||
super(message); | ||
Error.captureStackTrace(this, MissingDataError); | ||
Error.captureStackTrace(this, InvalidJsonError); | ||
this.code = ERROR_CODES.INVALID_JSON_ERROR_CODE; | ||
this.message = `"${filepath}" contains invalid JSON:\n\n${fileContents}\n\nTry validating the file on https://jsonlint.com/`; | ||
} | ||
} | ||
|
||
class MissingDataError extends Error { | ||
constructor(message) { | ||
super(message); | ||
Error.captureStackTrace(this, MissingDataError); | ||
this.code = ERROR_CODES.MISSING_DATA_ERROR_CODE; | ||
this.message = message; | ||
} | ||
} | ||
|
||
module.exports = { | ||
ERROR_CODES, | ||
InvalidConfigError, | ||
GithubError, | ||
MissingDataError, | ||
AbortApplicationError, | ||
InvalidJsonError | ||
GithubApiError, | ||
GithubSSHError, | ||
InvalidConfigError, | ||
InvalidJsonError, | ||
MissingDataError | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
const env = require('./env'); | ||
const rpc = require('./rpc'); | ||
const { GithubSSHError } = require('./errors'); | ||
|
||
async function folderExists(path) { | ||
try { | ||
|
@@ -20,6 +21,7 @@ function repoExists(owner, repoName) { | |
|
||
// Clone repo and add remotes | ||
async function setupRepo(owner, repoName, username) { | ||
await verifyGithubSshAuth(); | ||
await rpc.mkdirp(env.getRepoOwnerPath(owner)); | ||
await cloneRepo(owner, repoName); | ||
return addRemote(owner, repoName, username); | ||
|
@@ -75,7 +77,7 @@ function push(owner, repoName, username, branchName) { | |
}); | ||
} | ||
|
||
function resetAndPullMaster(owner, repoName) { | ||
async function resetAndPullMaster(owner, repoName) { | ||
return rpc.exec( | ||
`git reset --hard && git clean -d --force && git checkout master && git pull origin master`, | ||
{ | ||
|
@@ -84,7 +86,35 @@ function resetAndPullMaster(owner, repoName) { | |
); | ||
} | ||
|
||
async function verifyGithubSshAuth() { | ||
try { | ||
await rpc.exec(`ssh -oBatchMode=yes -T [email protected]`); | ||
return true; | ||
} catch (e) { | ||
switch (e.code) { | ||
case 1: | ||
return true; | ||
case 255: | ||
if (e.stderr.includes('Host key verification failed.')) { | ||
throw new GithubSSHError( | ||
'Host verification of github.com failed. To automatically add it to .ssh/known_hosts run:\nssh -T [email protected]' | ||
); | ||
} else if (e.stderr.includes('Permission denied')) { | ||
throw new GithubSSHError( | ||
'Permission denied. Please add your ssh private key to the keychain by following these steps:\nhttps://help.github.com/articles/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent/#adding-your-ssh-key-to-the-ssh-agent' | ||
); | ||
} else { | ||
throw e; | ||
} | ||
|
||
default: | ||
throw e; | ||
} | ||
} | ||
} | ||
|
||
module.exports = { | ||
verifyGithubSshAuth, | ||
cherrypick, | ||
cloneRepo, | ||
createAndCheckoutBranch, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,9 @@ | |
|
||
exports[`run through steps exec should be called with correct args 1`] = ` | ||
Array [ | ||
Array [ | ||
"ssh -oBatchMode=yes -T [email protected]", | ||
], | ||
Array [ | ||
"git clone [email protected]:elastic/kibana", | ||
Object { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
const { verifyGithubSshAuth } = require('../src/lib/git'); | ||
const rpc = require('../src/lib/rpc'); | ||
|
||
describe('verifyGithubSshAuth', () => { | ||
it('github.com is not added to known_hosts file', () => { | ||
const err = new Error(); | ||
err.code = 255; | ||
err.stderr = 'Host key verification failed.\r\n'; | ||
rpc.exec = jest.fn().mockReturnValue(Promise.reject(err)); | ||
|
||
return expect(verifyGithubSshAuth()).rejects.toThrow( | ||
'Host verification of github.com failed. To automatically add it to .ssh/known_hosts run:' | ||
); | ||
}); | ||
|
||
it('ssh key rejected', async () => { | ||
const err = new Error(); | ||
err.code = 255; | ||
err.stderr = '[email protected]: Permission denied (publickey).\r\n'; | ||
rpc.exec = jest.fn().mockReturnValue(Promise.reject(err)); | ||
|
||
return expect(verifyGithubSshAuth()).rejects.toThrowError( | ||
'Permission denied. Please add your ssh private key to the keychain by following these steps:' | ||
); | ||
}); | ||
|
||
it('user is successfully authenticated', async () => { | ||
const err = new Error(); | ||
err.code = 1; | ||
err.stderr = | ||
"Hi sqren! You've successfully authenticated, but GitHub does not provide shell access.\n"; | ||
rpc.exec = jest.fn().mockReturnValue(Promise.reject(err)); | ||
|
||
return expect(verifyGithubSshAuth()).resolves.toBe(true); | ||
}); | ||
}); |