From 191e9ce4f42f1b06ee3cf3be79808d5a2ba29f63 Mon Sep 17 00:00:00 2001 From: Ryan Park Date: Wed, 11 May 2022 10:53:14 -0700 Subject: [PATCH] refactor: stop using .netrc file, instead include credentials in the HTTPS URL --- dist/index.js | 62 +++++---------------------------------- src/controllers/create.js | 5 +--- src/controllers/update.js | 5 +--- src/git.js | 7 +++-- src/heroku.js | 2 +- src/main.js | 4 --- src/netrc.js | 32 -------------------- test/git.test.js | 10 +++++-- test/netrc.test.js | 55 ---------------------------------- 9 files changed, 24 insertions(+), 158 deletions(-) delete mode 100644 src/netrc.js delete mode 100644 test/netrc.test.js diff --git a/dist/index.js b/dist/index.js index 7180133f..0f70438e 100644 --- a/dist/index.js +++ b/dist/index.js @@ -8689,7 +8689,6 @@ const comments = __nccwpck_require__(4975); const core = __nccwpck_require__(2186); const git = __nccwpck_require__(109); const heroku = __nccwpck_require__(7213); -const netrc = __nccwpck_require__(8063); async function createController(params) { const { pipelineName, pipelineId, appName, refName } = params; @@ -8734,9 +8733,7 @@ async function createController(params) { `[Step ${currentStep}/${stepCount}] Deploying the app to Heroku for the first time -- this may take a few minutes...\n` ); const credentials = heroku.getCredentials(); - netrc.createNetrc(credentials); - const pushResult = git.push(appName, refName); - netrc.deleteNetrc(); + const pushResult = git.push(credentials, appName, refName); if (pushResult.status !== 0) { throw new Error(`Created Heroku app "${appName}" but ran into errors deploying for the first time.`); } @@ -8806,7 +8803,6 @@ const comments = __nccwpck_require__(4975); const core = __nccwpck_require__(2186); const git = __nccwpck_require__(109); const heroku = __nccwpck_require__(7213); -const netrc = __nccwpck_require__(8063); async function updateController(params) { const { pipelineName, appName, refName } = params; @@ -8827,9 +8823,7 @@ async function updateController(params) { core.info(`Deploying to Heroku app "${appName}" -- this may take a few minutes...\n`); const credentials = heroku.getCredentials(); - netrc.createNetrc(credentials); - const pushResult = git.push(appName, refName); - netrc.deleteNetrc(); + const pushResult = git.push(credentials, appName, refName); if (pushResult.status !== 0) { throw new Error(`Ran into errors deploying the app "${appName}"`); } @@ -8884,8 +8878,11 @@ module.exports.refExists = ref => { * Returns the result of child_process.spawn(); for documentation see * https://nodejs.org/api/child_process.html. */ -module.exports.push = (appName, ref) => { - return git('push', ['--force', `https://git.heroku.com/${appName}.git`, `${ref}:refs/heads/master`]); +module.exports.push = (credentials, appName, ref) => { + const url = new URL(`https://git.heroku.com/${appName}.git`); + url.username = credentials.email; + url.password = credentials.apiKey; + return git('push', ['--force', url.href, `${ref}:refs/heads/master`]); }; @@ -8954,7 +8951,7 @@ module.exports.initializeCredentials = function () { }; /* - * Returns the Heroku credentials which are needed for the .netrc file + * Returns the Heroku credentials, used for pushing code to Heroku */ module.exports.getCredentials = function () { return { email: HEROKU_EMAIL, apiKey: HEROKU_API_KEY }; @@ -9104,7 +9101,6 @@ const core = __nccwpck_require__(2186); const git = __nccwpck_require__(109); const github = __nccwpck_require__(5438); const heroku = __nccwpck_require__(7213); -const netrc = __nccwpck_require__(8063); const updateController = __nccwpck_require__(2477); /* @@ -9182,54 +9178,12 @@ async function main() { } } catch (error) { core.setFailed(error.message); - } finally { - // extra cleanup just to make sure we don't leave a .netrc file - netrc.deleteNetrc(); } } module.exports = main; -/***/ }), - -/***/ 8063: -/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { - -const core = __nccwpck_require__(2186); -const fs = __nccwpck_require__(7147); -const path = __nccwpck_require__(1017); - -module.exports.getNetrcPath = function () { - return path.join(process.env.HOME, '.netrc'); -}; - -module.exports.createNetrc = function (credentials) { - const filename = module.exports.getNetrcPath(); - if (fs.existsSync(filename)) { - throw new Error(`Credentials file ${filename} already exists`); // NOCOMMIT? - } - fs.writeFileSync( - filename, - `machine git.heroku.com\n login ${credentials.email}\n password ${credentials.apiKey}\n` - ); - if (process.env.NODE_ENV !== 'test') { - core.info(`Saved Heroku login credentials to ${filename}`); - } -}; - -module.exports.deleteNetrc = function () { - const filename = module.exports.getNetrcPath(); - if (!fs.existsSync(filename)) { - return; - } - fs.unlinkSync(filename); - if (process.env.NODE_ENV !== 'test') { - core.info(`Deleted Heroku login credentials from ${filename}`); - } -}; - - /***/ }), /***/ 2877: diff --git a/src/controllers/create.js b/src/controllers/create.js index 27a1ca9c..a0ab6c0d 100644 --- a/src/controllers/create.js +++ b/src/controllers/create.js @@ -2,7 +2,6 @@ const comments = require('../comments'); const core = require('@actions/core'); const git = require('../git'); const heroku = require('../heroku'); -const netrc = require('../netrc'); async function createController(params) { const { pipelineName, pipelineId, appName, refName } = params; @@ -47,9 +46,7 @@ async function createController(params) { `[Step ${currentStep}/${stepCount}] Deploying the app to Heroku for the first time -- this may take a few minutes...\n` ); const credentials = heroku.getCredentials(); - netrc.createNetrc(credentials); - const pushResult = git.push(appName, refName); - netrc.deleteNetrc(); + const pushResult = git.push(credentials, appName, refName); if (pushResult.status !== 0) { throw new Error(`Created Heroku app "${appName}" but ran into errors deploying for the first time.`); } diff --git a/src/controllers/update.js b/src/controllers/update.js index 652ab9d0..4071efd2 100644 --- a/src/controllers/update.js +++ b/src/controllers/update.js @@ -2,7 +2,6 @@ const comments = require('../comments'); const core = require('@actions/core'); const git = require('../git'); const heroku = require('../heroku'); -const netrc = require('../netrc'); async function updateController(params) { const { pipelineName, appName, refName } = params; @@ -23,9 +22,7 @@ async function updateController(params) { core.info(`Deploying to Heroku app "${appName}" -- this may take a few minutes...\n`); const credentials = heroku.getCredentials(); - netrc.createNetrc(credentials); - const pushResult = git.push(appName, refName); - netrc.deleteNetrc(); + const pushResult = git.push(credentials, appName, refName); if (pushResult.status !== 0) { throw new Error(`Ran into errors deploying the app "${appName}"`); } diff --git a/src/git.js b/src/git.js index 92e2c680..e096ef2a 100644 --- a/src/git.js +++ b/src/git.js @@ -35,6 +35,9 @@ module.exports.refExists = ref => { * Returns the result of child_process.spawn(); for documentation see * https://nodejs.org/api/child_process.html. */ -module.exports.push = (appName, ref) => { - return git('push', ['--force', `https://git.heroku.com/${appName}.git`, `${ref}:refs/heads/master`]); +module.exports.push = (credentials, appName, ref) => { + const url = new URL(`https://git.heroku.com/${appName}.git`); + url.username = credentials.email; + url.password = credentials.apiKey; + return git('push', ['--force', url.href, `${ref}:refs/heads/master`]); }; diff --git a/src/heroku.js b/src/heroku.js index d5e85848..825ba167 100644 --- a/src/heroku.js +++ b/src/heroku.js @@ -58,7 +58,7 @@ module.exports.initializeCredentials = function () { }; /* - * Returns the Heroku credentials which are needed for the .netrc file + * Returns the Heroku credentials, used for pushing code to Heroku */ module.exports.getCredentials = function () { return { email: HEROKU_EMAIL, apiKey: HEROKU_API_KEY }; diff --git a/src/main.js b/src/main.js index e13ca97b..8730aa6c 100644 --- a/src/main.js +++ b/src/main.js @@ -4,7 +4,6 @@ const core = require('@actions/core'); const git = require('./git'); const github = require('@actions/github'); const heroku = require('./heroku'); -const netrc = require('./netrc'); const updateController = require('./controllers/update'); /* @@ -82,9 +81,6 @@ async function main() { } } catch (error) { core.setFailed(error.message); - } finally { - // extra cleanup just to make sure we don't leave a .netrc file - netrc.deleteNetrc(); } } diff --git a/src/netrc.js b/src/netrc.js deleted file mode 100644 index 0671b26b..00000000 --- a/src/netrc.js +++ /dev/null @@ -1,32 +0,0 @@ -const core = require('@actions/core'); -const fs = require('fs'); -const path = require('path'); - -module.exports.getNetrcPath = function () { - return path.join(process.env.HOME, '.netrc'); -}; - -module.exports.createNetrc = function (credentials) { - const filename = module.exports.getNetrcPath(); - if (fs.existsSync(filename)) { - throw new Error(`Credentials file ${filename} already exists`); // NOCOMMIT? - } - fs.writeFileSync( - filename, - `machine git.heroku.com\n login ${credentials.email}\n password ${credentials.apiKey}\n` - ); - if (process.env.NODE_ENV !== 'test') { - core.info(`Saved Heroku login credentials to ${filename}`); - } -}; - -module.exports.deleteNetrc = function () { - const filename = module.exports.getNetrcPath(); - if (!fs.existsSync(filename)) { - return; - } - fs.unlinkSync(filename); - if (process.env.NODE_ENV !== 'test') { - core.info(`Deleted Heroku login credentials from ${filename}`); - } -}; diff --git a/test/git.test.js b/test/git.test.js index 78c3d18b..de2776c1 100644 --- a/test/git.test.js +++ b/test/git.test.js @@ -4,6 +4,8 @@ const git = require('../src/git'); const SAMPLE_APP_NAME = 'dr-owlbert-pr-1234'; const SAMPLE_REF = 'refs/heads/dev'; +const SAMPLE_EMAIL = 'jest-test-user@example.com'; +const SAMPLE_API_KEY = '12345678-1234-1234-1234-1234567890ab'; describe('#src/git', () => { afterEach(() => { @@ -48,13 +50,17 @@ describe('#src/git', () => { const EXPECTED_ARGS = [ 'push', '--force', - `https://git.heroku.com/${SAMPLE_APP_NAME}.git`, + `https://${encodeURIComponent(SAMPLE_EMAIL)}:${encodeURIComponent( + SAMPLE_API_KEY + )}@git.heroku.com/${SAMPLE_APP_NAME}.git`, `${SAMPLE_REF}:refs/heads/master`, ]; + const SAMPLE_CREDENTIALS = { email: SAMPLE_EMAIL, apiKey: SAMPLE_API_KEY }; + it('should run "git push" with the correct arguments', () => { const spy = jest.spyOn(childProcess, 'spawnSync').mockReturnValue({ status: 0 }); - expect(git.push(SAMPLE_APP_NAME, SAMPLE_REF)).toStrictEqual({ status: 0 }); + expect(git.push(SAMPLE_CREDENTIALS, SAMPLE_APP_NAME, SAMPLE_REF)).toStrictEqual({ status: 0 }); expect(spy.mock.lastCall[0]).toBe('git'); expect(spy.mock.lastCall[1]).toStrictEqual(EXPECTED_ARGS); }); diff --git a/test/netrc.test.js b/test/netrc.test.js deleted file mode 100644 index 9d270b0c..00000000 --- a/test/netrc.test.js +++ /dev/null @@ -1,55 +0,0 @@ -const fs = require('fs'); -const netrc = require('../src/netrc'); - -const HEROKU_USER = 'jest-heroku-user@readme.io'; -const HEROKU_TOKEN = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'; -const SAMPLE_CREDENTIALS = { email: HEROKU_USER, apiKey: HEROKU_TOKEN }; - -describe('#src/netrc', () => { - const path = netrc.getNetrcPath(); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - describe('createNetrc()', () => { - it('should write the correct contents to a .netrc file', () => { - const spy1 = jest.spyOn(fs, 'existsSync').mockReturnValue(false); - const spy2 = jest.spyOn(fs, 'writeFileSync').mockReturnValue(undefined); - expect(netrc.createNetrc(SAMPLE_CREDENTIALS)).toBeUndefined(); - expect(spy1).toHaveBeenCalledWith(path); - expect(spy2).toHaveBeenCalled(); - expect(spy2.mock.lastCall[0]).toBe(path); - expect(spy2.mock.lastCall[1]).toMatch(HEROKU_USER); - expect(spy2.mock.lastCall[1]).toMatch(HEROKU_TOKEN); - }); - - it('should throw an error if a .netrc file already exists', () => { - const spy1 = jest.spyOn(fs, 'existsSync').mockReturnValue(true); - const spy2 = jest.spyOn(fs, 'writeFileSync').mockReturnValue(undefined); - expect(() => { - netrc.createNetrc(SAMPLE_CREDENTIALS); - }).toThrow(/already exists/); - expect(spy1).toHaveBeenCalledWith(path); - expect(spy2).not.toHaveBeenCalled(); - }); - }); - - describe('deleteNetrc()', () => { - it('should delete the .netrc file', () => { - const spy1 = jest.spyOn(fs, 'existsSync').mockReturnValue(true); - const spy2 = jest.spyOn(fs, 'unlinkSync').mockReturnValue(undefined); - expect(netrc.deleteNetrc()).toBeUndefined(); - expect(spy1).toHaveBeenCalledWith(path); - expect(spy2).toHaveBeenCalledWith(path); - }); - - it('should not do anything if no .netrc file exists', () => { - const spy1 = jest.spyOn(fs, 'existsSync').mockReturnValue(false); - const spy2 = jest.spyOn(fs, 'unlinkSync').mockReturnValue(undefined); - expect(netrc.deleteNetrc()).toBeUndefined(); - expect(spy1).toHaveBeenCalledWith(path); - expect(spy2).not.toHaveBeenCalled(); - }); - }); -});