diff --git a/bin/cml.test.js b/bin/cml.test.js index 2af1b3d84..d106953de 100644 --- a/bin/cml.test.js +++ b/bin/cml.test.js @@ -3,7 +3,7 @@ const fetch = require('node-fetch'); describe('command-line interface tests', () => { test('cml --help', async () => { - const output = await exec(`node ./bin/cml.js --help`); + const output = await exec('node', './bin/cml.js', '--help'); expect(output).toMatchInlineSnapshot(` "cml.js diff --git a/bin/cml/asset/publish.e2e.test.js b/bin/cml/asset/publish.e2e.test.js index 2475f710e..f4a43ce31 100644 --- a/bin/cml/asset/publish.e2e.test.js +++ b/bin/cml/asset/publish.e2e.test.js @@ -3,13 +3,24 @@ const { exec } = require('../../../src/utils'); describe('CML e2e', () => { test('cml publish assets/logo.png --md', async () => { - const output = await exec(`node ./bin/cml.js publish assets/logo.png --md`); + const output = await exec( + 'node', + './bin/cml.js', + 'publish', + 'assets/logo.png', + '--md' + ); expect(output.startsWith('![](')).toBe(true); }); test('cml publish assets/logo.png', async () => { - const output = await exec(`node ./bin/cml.js publish assets/logo.png`); + const output = await exec( + 'node', + './bin/cml.js', + 'publish', + 'assets/logo.png' + ); expect(output.startsWith('https://')).toBe(true); }); @@ -17,14 +28,25 @@ describe('CML e2e', () => { test('cml publish assets/logo.pdf --md', async () => { const title = 'this is awesome'; const output = await exec( - `node ./bin/cml.js publish assets/logo.pdf --md --title '${title}'` + 'node', + './bin/cml.js', + 'publish', + 'assets/logo.pdf', + '--md', + '--title', + title ); expect(output.startsWith(`[${title}](`)).toBe(true); }); test('cml publish assets/logo.pdf', async () => { - const output = await exec(`node ./bin/cml.js publish assets/logo.pdf`); + const output = await exec( + 'node', + './bin/cml.js', + 'publish', + 'assets/logo.pdf' + ); expect(output.startsWith('https://')).toBe(true); }); @@ -32,7 +54,13 @@ describe('CML e2e', () => { test('cml publish assets/test.svg --md', async () => { const title = 'this is awesome'; const output = await exec( - `node ./bin/cml.js publish assets/test.svg --md --title '${title}'` + 'node', + './bin/cml.js', + 'publish', + 'assets/test.svg', + '--md', + '--title', + title ); expect(output.startsWith('![](') && output.endsWith(`${title}")`)).toBe( @@ -41,7 +69,12 @@ describe('CML e2e', () => { }); test('cml publish assets/test.svg', async () => { - const output = await exec(`node ./bin/cml.js publish assets/test.svg`); + const output = await exec( + 'node', + './bin/cml.js', + 'publish', + 'assets/test.svg' + ); expect(output.startsWith('https://')).toBe(true); }); @@ -49,7 +82,14 @@ describe('CML e2e', () => { test('cml publish assets/logo.pdf to file', async () => { const file = `cml-publish-test.md`; - await exec(`node ./bin/cml.js publish assets/logo.pdf --file ${file}`); + await exec( + 'node', + './bin/cml.js', + 'publish', + 'assets/logo.pdf', + '--file', + file + ); expect(fs.existsSync(file)).toBe(true); await fs.promises.unlink(file); @@ -57,7 +97,12 @@ describe('CML e2e', () => { test('cml publish assets/vega-lite.json', async () => { const output = await exec( - `node ./bin/cml.js publish --mime-type=application/json assets/vega-lite.json` + 'node', + './bin/cml.js', + 'publish', + '--mime-type', + 'application/json', + 'assets/vega-lite.json' ); expect(output.startsWith('https://')).toBe(true); @@ -68,7 +113,15 @@ describe('CML e2e', () => { const { TEST_GITLAB_REPO: repo, TEST_GITLAB_TOKEN: token } = process.env; const output = await exec( - `node ./bin/cml.js publish --repo=${repo} --token=${token} --gitlab-uploads assets/test.svg` + 'node', + './bin/cml.js', + 'publish', + '--repo', + repo, + '--token', + token, + '--gitlab-uploads', + 'assets/test.svg' ); expect(output.startsWith('https://')).toBe(true); @@ -76,7 +129,7 @@ describe('CML e2e', () => { test('cml publish /nonexistent produces file error', async () => { await expect( - exec('node ./bin/cml.js publish /nonexistent') + exec('node', './bin/cml.js', 'publish', '/nonexistent') ).rejects.toThrowError('ENOENT'); }); }); diff --git a/bin/cml/asset/publish.test.js b/bin/cml/asset/publish.test.js index f210666bf..c5213d9ab 100644 --- a/bin/cml/asset/publish.test.js +++ b/bin/cml/asset/publish.test.js @@ -2,7 +2,7 @@ const { exec } = require('../../../src/utils'); describe('CML cli test', () => { test('cml publish --help', async () => { - const output = await exec(`node ./bin/cml.js publish --help`); + const output = await exec('node', './bin/cml.js', 'publish', '--help'); expect(output).toMatchInlineSnapshot(` "cml.js publish diff --git a/bin/cml/check/create.e2e.test.js b/bin/cml/check/create.e2e.test.js index 67d2a38b1..d90c72c64 100644 --- a/bin/cml/check/create.e2e.test.js +++ b/bin/cml/check/create.e2e.test.js @@ -15,7 +15,7 @@ describe('CML e2e', () => { await fs.writeFile(path, report); process.env.GITHUB_ACTIONS && - (await exec(`node ./bin/cml.js send-github-check ${path}`)); + (await exec('node', './bin/cml.js', 'send-github-check', path)); }); test('cml send-github-check failure with tile "CML neutral test"', async () => { @@ -26,7 +26,14 @@ describe('CML e2e', () => { await fs.writeFile(path, report); process.env.GITHUB_ACTIONS && (await exec( - `node ./bin/cml.js send-github-check ${path} --title "${title}" --conclusion "${conclusion}"` + 'node', + './bin/cml.js', + 'send-github-check', + path, + '--title', + title, + '--conclusion', + conclusion )); }); }); diff --git a/bin/cml/check/create.test.js b/bin/cml/check/create.test.js index 29d2975d5..01f60debf 100644 --- a/bin/cml/check/create.test.js +++ b/bin/cml/check/create.test.js @@ -2,7 +2,12 @@ const { exec } = require('../../../src/utils'); describe('CML e2e', () => { test('cml send-github-check --help', async () => { - const output = await exec(`node ./bin/cml.js send-github-check --help`); + const output = await exec( + 'node', + './bin/cml.js', + 'send-github-check', + '--help' + ); expect(output).toMatchInlineSnapshot(` "cml.js send-github-check diff --git a/bin/cml/comment/create.e2e.test.js b/bin/cml/comment/create.e2e.test.js index 31398bbaf..1951bc4c3 100644 --- a/bin/cml/comment/create.e2e.test.js +++ b/bin/cml/comment/create.e2e.test.js @@ -21,7 +21,16 @@ describe('Comment integration tests', () => { await fs.writeFile(path, report); await exec( - `node ./bin/cml.js send-comment --repo=${repo} --token=${token} --commit-sha=${sha} ${path}` + 'node', + './bin/cml.js', + 'send-comment', + '--repo', + repo, + '--token', + token, + '--commit-sha', + sha, + path ); }); @@ -29,13 +38,13 @@ describe('Comment integration tests', () => { const report = `## Test Comment`; await fs.writeFile(path, report); - await exec(`node ./bin/cml.js send-comment ${path}`); + await exec('node', './bin/cml.js', 'send-comment', path); }); test('cml send-comment --publish to current repo', async () => { const report = `## Test Comment\n![](assets/logo.png)`; await fs.writeFile(path, report); - await exec(`node ./bin/cml.js send-comment --publish ${path}`); + await exec('node', './bin/cml.js', 'send-comment', '--publish', path); }); }); diff --git a/bin/cml/comment/create.test.js b/bin/cml/comment/create.test.js index c4aebf9dc..b52d221b1 100644 --- a/bin/cml/comment/create.test.js +++ b/bin/cml/comment/create.test.js @@ -2,7 +2,7 @@ const { exec } = require('../../../src/utils'); describe('Comment integration tests', () => { test('cml send-comment --help', async () => { - const output = await exec(`node ./bin/cml.js send-comment --help`); + const output = await exec('node', './bin/cml.js', 'send-comment', '--help'); expect(output).toMatchInlineSnapshot(` "cml.js send-comment diff --git a/bin/cml/pr/create.e2e.test.js b/bin/cml/pr/create.e2e.test.js index f2d669799..5d59d8c32 100644 --- a/bin/cml/pr/create.e2e.test.js +++ b/bin/cml/pr/create.e2e.test.js @@ -2,7 +2,7 @@ const { exec } = require('../../../src/utils'); describe('CML e2e', () => { test('cml-pr --help', async () => { - const output = await exec(`echo none | node ./bin/cml.js pr --help`); + const output = await exec('node', './bin/cml.js', 'pr', '--help'); expect(output).toMatchInlineSnapshot(` "cml.js pr diff --git a/bin/cml/repo/prepare.test.js b/bin/cml/repo/prepare.test.js index 6a765847b..994296d5b 100644 --- a/bin/cml/repo/prepare.test.js +++ b/bin/cml/repo/prepare.test.js @@ -2,7 +2,7 @@ const { exec } = require('../../../src/utils'); describe('CML e2e', () => { test('cml-ci --help', async () => { - const output = await exec(`echo none | node ./bin/cml.js ci --help`); + const output = await exec('node', './bin/cml.js', 'ci', '--help'); expect(output).toMatchInlineSnapshot(` "cml.js ci diff --git a/bin/cml/runner/launch.e2e.test.js b/bin/cml/runner/launch.e2e.test.js index 5f4c9f4aa..3235fb41f 100644 --- a/bin/cml/runner/launch.e2e.test.js +++ b/bin/cml/runner/launch.e2e.test.js @@ -15,9 +15,27 @@ const { const launchRunner = async (opts) => { const { cloud, type, repo, token, privateKey, name } = opts; - const command = `node ./bin/cml.js runner --cloud ${cloud} --cloud-type ${type} --repo ${repo} --token ${token} --cloud-ssh-private="${privateKey}" --name ${name} --cloud-spot true --idle-timeout ${IDLE_TIMEOUT}`; - - const output = await exec(command); + const output = await exec( + 'node', + './bin/cml.js', + 'runner', + '--cloud', + cloud, + '--cloud-type', + type, + '--repo', + repo, + '--token', + token, + '--cloud-ssh-private', + privateKey, + '--name', + name, + '--cloud-spot', + 'true', + '--idle-timeout', + IDLE_TIMEOUT + ); const state = JSON.parse(output.split(/\n/).pop()); return state; diff --git a/bin/cml/runner/launch.js b/bin/cml/runner/launch.js index 69595ff43..f194cd898 100755 --- a/bin/cml/runner/launch.js +++ b/bin/cml/runner/launch.js @@ -67,7 +67,13 @@ const shutdown = async (opts) => { try { return await exec( - `leo destroy-runner --cloud=${cloud} --region=${region} ${id}` + 'leo', + 'destroy-runner', + '--cloud', + cloud, + '--region', + region, + id ); } catch (err) { winston.error(`\tFailed destroying with LEO: ${err.message}`); diff --git a/bin/cml/runner/launch.test.js b/bin/cml/runner/launch.test.js index 8034c8b65..8fed32f64 100644 --- a/bin/cml/runner/launch.test.js +++ b/bin/cml/runner/launch.test.js @@ -2,7 +2,7 @@ const { exec } = require('../../../src/utils'); describe('CML e2e', () => { test('cml-runner --help', async () => { - const output = await exec(`echo none | node ./bin/cml.js runner --help`); + const output = await exec('node', './bin/cml.js', 'runner', '--help'); expect(output).toMatchInlineSnapshot(` "cml.js runner diff --git a/bin/cml/tensorboard/connect.e2e.test.js b/bin/cml/tensorboard/connect.e2e.test.js index 1b3f0b40e..1438b7ffa 100644 --- a/bin/cml/tensorboard/connect.e2e.test.js +++ b/bin/cml/tensorboard/connect.e2e.test.js @@ -15,7 +15,7 @@ const isTbRunning = async () => { const rmTbDevExperiment = async (tbOutput) => { const id = /experiment\/([a-zA-Z0-9]{22})/.exec(tbOutput)[1]; - await exec(`tensorboard dev delete --experiment_id ${id}`); + await exec('tensorboard', 'dev', 'delete', '--experiment_id', id); }; describe('tbLink', () => { @@ -56,9 +56,20 @@ describe('CML e2e', () => { const desc = 'Test experiment'; const title = 'go to the experiment'; const output = await exec( - `node ./bin/cml.js tensorboard-dev --credentials '${CREDENTIALS}' \ - --md --title '${title}' \ - --logdir logs --name '${name}' --description '${desc}'` + 'node', + './bin/cml.js', + 'tensorboard-dev', + '--credentials', + CREDENTIALS, + '--md', + '--title', + title, + '--logdir', + 'logs', + '--name', + name, + '--description', + desc ); const isRunning = await isTbRunning(); @@ -71,7 +82,13 @@ describe('CML e2e', () => { test('cml tensorboard-dev invalid creds', async () => { try { - await exec(`node ./bin/cml.js tensorboard-dev --credentials 'invalid'`); + await exec( + 'node', + './bin/cml.js', + 'tensorboard-dev', + '--credentials', + 'invalid' + ); } catch (err) { expect(err.message.includes('json.decoder.JSONDecodeError')).toBe(true); } diff --git a/bin/cml/tensorboard/connect.js b/bin/cml/tensorboard/connect.js index 5f61f5469..8d6aa787b 100644 --- a/bin/cml/tensorboard/connect.js +++ b/bin/cml/tensorboard/connect.js @@ -87,7 +87,7 @@ exports.handler = async (opts) => { await fs.mkdir(path, { recursive: true }); await fs.writeFile(`${path}/uploader-creds.json`, credentials); - const help = await exec('tensorboard dev upload -h'); + const help = await exec('tensorboard', 'dev', 'upload', '-h'); const extraParamsFound = (name || description) && help.indexOf('--description') >= 0; const extraParams = extraParamsFound diff --git a/bin/cml/tensorboard/connect.test.js b/bin/cml/tensorboard/connect.test.js index 66c332805..07a4a6913 100644 --- a/bin/cml/tensorboard/connect.test.js +++ b/bin/cml/tensorboard/connect.test.js @@ -2,7 +2,12 @@ const { exec } = require('../../../src/utils'); describe('CML e2e', () => { test('cml tensorboard-dev --help', async () => { - const output = await exec(`node ./bin/cml.js tensorboard-dev --help`); + const output = await exec( + 'node', + './bin/cml.js', + 'tensorboard-dev', + '--help' + ); expect(output).toMatchInlineSnapshot(` "cml.js tensorboard-dev diff --git a/bin/cml/workflow/rerun.test.js b/bin/cml/workflow/rerun.test.js index 17a335b39..ebd17183d 100644 --- a/bin/cml/workflow/rerun.test.js +++ b/bin/cml/workflow/rerun.test.js @@ -3,7 +3,10 @@ const { exec } = require('../../../src/utils'); describe('CML e2e', () => { test('cml-ci --help', async () => { const output = await exec( - `echo none | node ./bin/cml.js rerun-workflow --help` + 'node', + './bin/cml.js', + 'rerun-workflow', + '--help' ); expect(output).toMatchInlineSnapshot(` diff --git a/bin/legacy/link.e2e.test.js b/bin/legacy/link.e2e.test.js index 891c84d8c..54a58f776 100644 --- a/bin/legacy/link.e2e.test.js +++ b/bin/legacy/link.e2e.test.js @@ -8,8 +8,8 @@ const commands = Object.keys(bin) describe('command-line interface tests', () => { for (const command of commands) test(`legacy cml-${command} behaves as the new cml ${command}`, async () => { - const legacyOutput = await exec(`cml-${command} --help`); - const newOutput = await exec(`cml ${command} --help`); + const legacyOutput = await exec(`cml-${command}`, '--help'); + const newOutput = await exec('cml', command, '--help'); expect(legacyOutput).toBe(newOutput); }); }); diff --git a/src/analytics.js b/src/analytics.js index 163f53e6d..84d2ebe66 100644 --- a/src/analytics.js +++ b/src/analytics.js @@ -95,7 +95,7 @@ const userId = async ({ cml } = {}) => { } else if (ci === 'bitbucket') { rawId = BITBUCKET_STEP_TRIGGERER_UUID; } else { - rawId = await exec(`git log -1 --pretty=format:'%ae'`); + rawId = await exec('git', 'log', '-1', "--pretty=format:'%ae'"); } return await deterministic(rawId); diff --git a/src/cml.js b/src/cml.js index 15bb176ea..0dae5e067 100755 --- a/src/cml.js +++ b/src/cml.js @@ -132,7 +132,7 @@ class CML { async revParse({ ref = 'HEAD' } = {}) { try { - return await exec(`git rev-parse ${ref}`); + return await exec('git', 'rev-parse', ref); } catch (err) { winston.warn( 'Failed to obtain SHA. Perhaps not in the correct git folder' @@ -147,7 +147,7 @@ class CML { async branch() { const { branch } = this.getDriver(); - return branch || (await exec(`git branch --show-current`)); + return branch || (await exec('git', 'branch', '--show-current')); } getDriver() { @@ -504,14 +504,23 @@ class CML { const { fetchDepth = unshallow ? 0 : undefined } = opts; const driver = this.getDriver(); - await exec(await driver.updateGitConfig({ userName, userEmail, remote })); + const commands = await driver.updateGitConfig({ + userName, + userEmail, + remote + }); + for (const command of commands) { + await exec(command); + } if (fetchDepth !== undefined) { if (fetchDepth <= 0) { - if ((await exec('git rev-parse --is-shallow-repository')) === 'true') { - return await exec('git fetch --all --unshallow'); + if ( + (await exec('git', 'rev-parse', '--is-shallow-repository')) === 'true' + ) { + return await exec('git', 'fetch', '--all', '--unshallow'); } } else { - return await exec(`git fetch --all --depth=${fetchDepth}`); + return await exec('git', 'fetch', '--all', `--depth=${fetchDepth}`); } } } @@ -564,7 +573,10 @@ class CML { const branchExists = ( await exec( - `git ls-remote $(git config --get remote.${remote}.url) ${source}` + 'git', + 'ls-remote', + await exec('git', 'config', '--get', `remote.${remote}.url`), + source ) ).includes(source); @@ -578,16 +590,16 @@ class CML { if (url) return renderPr(url); } else { - await exec(`git fetch ${remote} ${sha}`); - await exec(`git checkout -B ${target} ${sha}`); - await exec(`git checkout -b ${source}`); - await exec(`git add ${paths.join(' ')}`); + await exec('git', 'fetch', remote, sha); + await exec('git', 'checkout', '-B', target, sha); + await exec('git', 'checkout', '-b', source); + await exec('git', 'add', paths.join(' ')); let commitMessage = message || `CML PR for ${shaShort}`; if (skipCi || (!message && !(merge || rebase || squash))) { commitMessage += ' [skip ci]'; } - await exec(`git commit -m "${commitMessage}"`); - await exec(`git push --set-upstream ${remote} ${source}`); + await exec('git', 'commit', '-m', commitMessage); + await exec('git', 'push', '--set-upstream', remote, source); } const url = await driver.prCreate({ diff --git a/src/drivers/bitbucket_cloud.e2e.test.js b/src/drivers/bitbucket_cloud.e2e.test.js index 984be5c7d..fb2d94bad 100644 --- a/src/drivers/bitbucket_cloud.e2e.test.js +++ b/src/drivers/bitbucket_cloud.e2e.test.js @@ -50,14 +50,51 @@ describe('Non Enviromental tests', () => { remote: 'origin' }); expect(command).toMatchInlineSnapshot(` - " - git config --unset user.name; - git config --unset user.email; - git config --unset push.default; - git config --unset http.http://bitbucket.org/test/test.proxy; - git config user.name \\"john\\" && - git config user.email \\"john@test.com\\" && - git remote set-url origin \\"https://user:pass@bitbucket.org/test/test\\"" + Array [ + Array [ + "git", + "config", + "--unset", + "user.name", + ], + Array [ + "git", + "config", + "--unset", + "user.email", + ], + Array [ + "git", + "config", + "--unset", + "push.default", + ], + Array [ + "git", + "config", + "--unset", + "http.http://bitbucket.org/test/test.proxy", + ], + Array [ + "git", + "config", + "user.name", + "john", + ], + Array [ + "git", + "config", + "user.email", + "john@test.com", + ], + Array [ + "git", + "remote", + "set-url", + "origin", + "https://user:pass@bitbucket.org/test/test", + ], + ] `); }); }); diff --git a/src/drivers/bitbucket_cloud.js b/src/drivers/bitbucket_cloud.js index 48c774d1a..4fe27206d 100644 --- a/src/drivers/bitbucket_cloud.js +++ b/src/drivers/bitbucket_cloud.js @@ -209,7 +209,7 @@ class BitbucketCloud { throw err; } } finally { - await exec(`docker stop ${name}`); + await exec('docker', 'stop', name); } } @@ -413,16 +413,22 @@ class BitbucketCloud { repo.protocol = 'https'; repo.pathname = repo.pathname.replace('.git', ''); - const command = ` - git config --unset user.name; - git config --unset user.email; - git config --unset push.default; - git config --unset http.http://${repo.host}${repo.pathname}.proxy; - git config user.name "${userName || this.userName}" && - git config user.email "${userEmail || this.userEmail}" && - git remote set-url ${remote} "${repo.toString()}"`; - - return command; + const commands = [ + ['git', 'config', '--unset', 'user.name'], + ['git', 'config', '--unset', 'user.email'], + ['git', 'config', '--unset', 'push.default'], + [ + 'git', + 'config', + '--unset', + `http.http://${repo.host}${repo.pathname}.proxy` + ], + ['git', 'config', 'user.name', userName || this.userName], + ['git', 'config', 'user.email', userEmail || this.userEmail], + ['git', 'remote', 'set-url', remote, repo.toString()] + ]; + + return commands; } warn(message) { diff --git a/src/drivers/github.e2e.test.js b/src/drivers/github.e2e.test.js index 6f3d71d05..0c23cc427 100644 --- a/src/drivers/github.e2e.test.js +++ b/src/drivers/github.e2e.test.js @@ -45,11 +45,33 @@ describe('Non Enviromental tests', () => { }); const command = await client.updateGitConfig({ remote: 'origin' }); expect(command).toMatchInlineSnapshot(` - " - git config --unset http.https://github.com/.extraheader; - git config user.name \\"GitHub Action\\" && - git config user.email \\"action@github.com\\" && - git remote set-url origin \\"https://token:dXNlcjpwYXNz@github.com/test/test.git\\"" + Array [ + Array [ + "git", + "config", + "--unset", + "http.https://github.com/.extraheader", + ], + Array [ + "git", + "config", + "user.name", + "GitHub Action", + ], + Array [ + "git", + "config", + "user.email", + "action@github.com", + ], + Array [ + "git", + "remote", + "set-url", + "origin", + "", + ], + ] `); }); diff --git a/src/drivers/github.js b/src/drivers/github.js index 273fa0a55..2897cfd0c 100644 --- a/src/drivers/github.js +++ b/src/drivers/github.js @@ -273,15 +273,19 @@ class Github { } await exec( - `${resolve( - workdir, - 'config.sh' - )} --unattended --token "${await this.runnerToken()}" --url "${ - this.repo - }" --name "${name}" --labels "${labels}" --work "${resolve( - workdir, - '_work' - )}" ${single ? ' --ephemeral' : ''}` + resolve(workdir, 'config.sh'), + '--unattended', + '--token', + await this.runnerToken(), + '--url', + this.repo, + '--name', + name, + '--labels', + labels, + '--work', + resolve(workdir, '_work'), + ...(single ? ['--ephemeral'] : []) ); return spawn(resolve(workdir, 'run.sh'), { @@ -695,15 +699,20 @@ class Github { repo.password = this.token; repo.username = 'token'; - const command = ` - git config --unset http.https://github.com/.extraheader; - git config user.name "${userName || this.userName}" && - git config user.email "${userEmail || this.userEmail}" && - git remote set-url ${remote} "${repo.toString()}${ - repo.toString().endsWith('.git') ? '' : '.git' - }"`; - - return command; + const commands = [ + ['git', 'config', '--unset', 'http.https://github.com/.extraheader'], + ['git', 'config', 'user.name', userName || this.userName], + ['git', 'config', 'user.email', userEmail || this.userEmail], + [ + 'git', + 'remote', + 'set-url', + remote, + repo.toString() + repo.toString().endsWith('.git') ? '' : '.git' + ] + ]; + + return commands; } get workflowId() { diff --git a/src/drivers/gitlab.e2e.test.js b/src/drivers/gitlab.e2e.test.js index 82f85c41b..941b4fb46 100644 --- a/src/drivers/gitlab.e2e.test.js +++ b/src/drivers/gitlab.e2e.test.js @@ -54,10 +54,27 @@ describe('Non Enviromental tests', () => { remote: 'origin' }); expect(command).toMatchInlineSnapshot(` - " - git config user.name \\"john\\" && - git config user.email \\"john@test.com\\" && - git remote set-url origin \\"https://token:dXNlcjpwYXNz@gitlab.com/test/test.git\\"" + Array [ + Array [ + "git", + "config", + "user.name", + "john", + ], + Array [ + "git", + "config", + "user.email", + "john@test.com", + ], + Array [ + "git", + "remote", + "set-url", + "origin", + "", + ], + ] `); }); }); diff --git a/src/drivers/gitlab.js b/src/drivers/gitlab.js index 2dec2d611..68a4d81d6 100644 --- a/src/drivers/gitlab.js +++ b/src/drivers/gitlab.js @@ -435,14 +435,19 @@ class Gitlab { repo.password = this.token; repo.username = 'token'; - const command = ` - git config user.name "${userName || this.userName}" && - git config user.email "${userEmail || this.userEmail}" && - git remote set-url ${remote} "${repo.toString()}${ - repo.toString().endsWith('.git') ? '' : '.git' - }"`; - - return command; + const commands = [ + ['git', 'config', 'user.name', userName || this.userName], + ['git', 'config', 'user.email', userEmail || this.userEmail], + [ + 'git', + 'remote', + 'set-url', + remote, + repo.toString() + repo.toString().endsWith('.git') ? '' : '.git' + ] + ]; + + return commands; } warn(message) { diff --git a/src/terraform.js b/src/terraform.js index d99fd17f8..0ee207290 100644 --- a/src/terraform.js +++ b/src/terraform.js @@ -7,11 +7,11 @@ const MIN_TF_VER = '0.14.0'; const version = async () => { try { - const output = await exec('terraform version -json'); + const output = await exec('terraform', 'version', '-json'); const { terraform_version: ver } = JSON.parse(output); return ver; } catch (err) { - const output = await exec('terraform version'); + const output = await exec('terraform', 'version'); const matches = output.match(/Terraform v(\d{1,2}\.\d{1,2}\.\d{1,2})/); if (matches.length < 2) throw new Error('Unable to get TF version'); @@ -33,7 +33,7 @@ const saveTfState = async (opts = {}) => { const init = async (opts = {}) => { const { dir = './' } = opts; - return await exec(`terraform -chdir='${dir}' init`); + return await exec('terraform', `-chdir=${dir}`, 'init'); }; const apply = async (opts = {}) => { @@ -60,7 +60,11 @@ const destroy = async (opts = {}) => { const { dir = './', target } = opts; const targetop = target ? `-target=${target}` : ''; return await exec( - `terraform -chdir='${dir}' destroy -auto-approve ${targetop}` + 'terraform', + `-chdir=${dir}`, + 'destroy', + '-auto-approve', + targetop ); }; diff --git a/src/utils.js b/src/utils.js index 9fe41e45d..bbf0a2806 100644 --- a/src/utils.js +++ b/src/utils.js @@ -22,17 +22,19 @@ const getos = async () => { const waitForever = () => new Promise((resolve) => resolve); -const exec = async (command) => { +const exec = async (file, ...args) => { return new Promise((resolve, reject) => { - require('child_process').exec( - command, + require('child_process').execFile( + file, + args, { ...process.env }, (error, stdout, stderr) => { if (!process.stdout.isTTY) { stdout = stripAnsi(stdout); stderr = stripAnsi(stderr); } - if (error) reject(new Error(`${command}\n\t${stdout}\n\t${stderr}`)); + if (error) + reject(new Error(`${[file, ...args]}\n\t${stdout}\n\t${stderr}`)); resolve((stdout || stderr).slice(0, -1)); } diff --git a/src/utils.test.js b/src/utils.test.js index 384b1d37b..59f4cb39f 100644 --- a/src/utils.test.js +++ b/src/utils.test.js @@ -2,7 +2,7 @@ const { exec, upload } = require('./utils'); describe('exec tests', () => { test('exec is await and outputs hello', async () => { - const output = await exec('echo hello'); + const output = await exec('echo', 'hello'); expect(output).toMatch('hello'); });