From 6061bd403811760ac31a1774f25365ec88ad9b9c Mon Sep 17 00:00:00 2001 From: Dennis Henry Date: Thu, 20 Jun 2024 20:09:28 -0400 Subject: [PATCH 1/5] fix: remove git ssh override --- lib/opts.js | 8 ++------ test/opts.js | 32 -------------------------------- 2 files changed, 2 insertions(+), 38 deletions(-) diff --git a/lib/opts.js b/lib/opts.js index 3119af1..e26ceea 100644 --- a/lib/opts.js +++ b/lib/opts.js @@ -1,12 +1,8 @@ // Values we want to set if they're not already defined by the end user -// This defaults to accepting new ssh host key fingerprints -const gitEnv = { - GIT_ASKPASS: 'echo', - GIT_SSH_COMMAND: 'ssh -oStrictHostKeyChecking=accept-new', -} +const defaultEnv = {} module.exports = (opts = {}) => ({ stdioString: true, ...opts, shell: false, - env: opts.env || { ...gitEnv, ...process.env }, + env: opts.env || { ...defaultEnv, ...process.env }, }) diff --git a/test/opts.js b/test/opts.js index 6d21c84..278b2c4 100644 --- a/test/opts.js +++ b/test/opts.js @@ -1,38 +1,6 @@ const t = require('tap') const gitOpts = require('../lib/opts.js') -t.test('defaults', t => { - const { GIT_ASKPASS, GIT_SSH_COMMAND } = process.env - t.teardown(() => { - process.env.GIT_ASKPASS = GIT_ASKPASS - process.env.GIT_SSH_COMMAND = GIT_SSH_COMMAND - }) - delete process.env.GIT_ASKPASS - delete process.env.GIT_SSH_COMMAND - t.match(gitOpts().env, { - GIT_ASKPASS: 'echo', - GIT_SSH_COMMAND: 'ssh -oStrictHostKeyChecking=accept-new', - }, 'got the git defaults we want') - t.equal(gitOpts().shell, false, 'shell defaults to false') - t.equal(gitOpts({ shell: '/bin/bash' }).shell, false, 'shell cannot be overridden') - t.end() -}) - -t.test('does not override', t => { - const { GIT_ASKPASS, GIT_SSH_COMMAND } = process.env - t.teardown(() => { - process.env.GIT_ASKPASS = GIT_ASKPASS - process.env.GIT_SSH_COMMAND = GIT_SSH_COMMAND - }) - process.env.GIT_ASKPASS = 'test_askpass' - process.env.GIT_SSH_COMMAND = 'test_ssh_command' - t.match(gitOpts().env, { - GIT_ASKPASS: 'test_askpass', - GIT_SSH_COMMAND: 'test_ssh_command', - }, 'values already in process.env remain') - t.end() -}) - t.test('as non-root', t => { process.getuid = () => 999 t.match(gitOpts({ From 5f5810475774d72c5c56ac98cd08e5ad1708fc69 Mon Sep 17 00:00:00 2001 From: pacotedev Date: Thu, 27 Jun 2024 16:46:09 -0400 Subject: [PATCH 2/5] update logic to only overwrite when git config is also not set --- lib/opts.js | 65 ++++++++++++++++++++--- package.json | 2 + test/opts.js | 144 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 203 insertions(+), 8 deletions(-) diff --git a/lib/opts.js b/lib/opts.js index e26ceea..ee17149 100644 --- a/lib/opts.js +++ b/lib/opts.js @@ -1,8 +1,57 @@ -// Values we want to set if they're not already defined by the end user -const defaultEnv = {} -module.exports = (opts = {}) => ({ - stdioString: true, - ...opts, - shell: false, - env: opts.env || { ...defaultEnv, ...process.env }, -}) +const fs = require('fs') +const os = require('os') +const path = require('path') +const ini = require('ini') + +const gitConfigPath = path.join(os.homedir(), '.gitconfig') + +// Function to check if sshCommand is set in the git config +const isGitSshCommandSetInConfig = () => { + try { + if (fs.existsSync(gitConfigPath)) { + const config = ini.parse(fs.readFileSync(gitConfigPath, 'utf-8')) + return config.core && config.core.sshCommand !== undefined + } + } catch (error) { + return false + } + return false +} + +// Function to check if askpass is set in the git config +const isGitAskPassSetInConfig = () => { + try { + if (fs.existsSync(gitConfigPath)) { + const config = ini.parse(fs.readFileSync(gitConfigPath, 'utf-8')) + return config.core && config.core.askpass !== undefined + } + } catch (error) { + return false + } + return false +} + +module.exports = (opts = {}) => { + const sshCommandSetInEnv = process.env.GIT_SSH_COMMAND !== undefined + const sshCommandSetInConfig = isGitSshCommandSetInConfig() + const askPassSetInEnv = process.env.GIT_ASKPASS !== undefined + const askPassSetInConfig = isGitAskPassSetInConfig() + + // Values we want to set if they're not already defined by the end user + // This defaults to accepting new ssh host key fingerprints + const finalGitEnv = { + ...(askPassSetInEnv || askPassSetInConfig ? {} : { + GIT_ASKPASS: 'echo', + }), + ...(sshCommandSetInEnv || sshCommandSetInConfig ? {} : { + GIT_SSH_COMMAND: 'ssh -oStrictHostKeyChecking=accept-new', + }), + } + + return { + stdioString: true, + ...opts, + shell: false, + env: opts.env || { ...finalGitEnv, ...process.env }, + } +} diff --git a/package.json b/package.json index cc641d2..1a07c60 100644 --- a/package.json +++ b/package.json @@ -38,11 +38,13 @@ }, "dependencies": { "@npmcli/promise-spawn": "^7.0.0", + "ini": "^4.1.3", "lru-cache": "^10.0.1", "npm-pick-manifest": "^9.0.0", "proc-log": "^4.0.0", "promise-inflight": "^1.0.1", "promise-retry": "^2.0.1", + "proxyquire": "^2.1.3", "semver": "^7.3.5", "which": "^4.0.0" }, diff --git a/test/opts.js b/test/opts.js index 278b2c4..204f4b2 100644 --- a/test/opts.js +++ b/test/opts.js @@ -1,8 +1,125 @@ const t = require('tap') +const proxyquire = require('proxyquire') const gitOpts = require('../lib/opts.js') +const fs = require('fs') +const os = require('os') +const path = require('path') + +const gitConfigPath = path.join(os.homedir(), '.gitconfig') + +const mockFs = { + existsSync: () => false, + readFileSync: () => '', +} + +// Utility function to backup and restore gitconfig +const backupGitConfig = () => { + const backupPath = `${gitConfigPath}.backup` + if (fs.existsSync(gitConfigPath)) { + fs.copyFileSync(gitConfigPath, backupPath) + fs.unlinkSync(gitConfigPath) + } + return backupPath +} + +const restoreGitConfig = (backupPath) => { + if (fs.existsSync(backupPath)) { + fs.copyFileSync(backupPath, gitConfigPath) + fs.unlinkSync(backupPath) + } else if (fs.existsSync(gitConfigPath)) { + fs.unlinkSync(gitConfigPath) + } +} + +const writeGitConfig = (content) => { + fs.writeFileSync(gitConfigPath, content) +} + +t.test('handle case when fs.existsSync throws an error', t => { + const { GIT_ASKPASS, GIT_SSH_COMMAND } = process.env + t.teardown(() => { + process.env.GIT_ASKPASS = GIT_ASKPASS + process.env.GIT_SSH_COMMAND = GIT_SSH_COMMAND + }) + + // Mocking fs.existsSync to throw an error + const gitOptsWithMockFs = proxyquire('../lib/opts.js', { + fs: { + ...mockFs, + existsSync: () => { + throw new Error('Mocked error') + }, + }, + }) + + t.match(gitOptsWithMockFs(), { + env: { + GIT_ASKPASS: 'echo', + GIT_SSH_COMMAND: 'ssh -oStrictHostKeyChecking=accept-new', + }, + shell: false, + }, 'should apply defaults when fs.existsSync throws an error') + + t.end() +}) + +t.test('defaults', t => { + const backupPath = backupGitConfig() + const { GIT_ASKPASS, GIT_SSH_COMMAND } = process.env + t.teardown(() => { + restoreGitConfig(backupPath) + process.env.GIT_ASKPASS = GIT_ASKPASS + process.env.GIT_SSH_COMMAND = GIT_SSH_COMMAND + }) + + delete process.env.GIT_ASKPASS + delete process.env.GIT_SSH_COMMAND + + t.match(gitOpts(), { + env: { + GIT_ASKPASS: 'echo', + GIT_SSH_COMMAND: 'ssh -oStrictHostKeyChecking=accept-new', + }, + shell: false, + }, 'got the git defaults we want') + + t.end() +}) + +t.test('does not override when sshCommand is set in env', t => { + const backupPath = backupGitConfig() + const { GIT_ASKPASS, GIT_SSH_COMMAND } = process.env + t.teardown(() => { + restoreGitConfig(backupPath) + process.env.GIT_ASKPASS = GIT_ASKPASS + process.env.GIT_SSH_COMMAND = GIT_SSH_COMMAND + }) + + process.env.GIT_ASKPASS = 'test_askpass' + process.env.GIT_SSH_COMMAND = 'test_ssh_command' + + t.match(gitOpts(), { + env: { + GIT_ASKPASS: 'test_askpass', + GIT_SSH_COMMAND: 'test_ssh_command', + }, + shell: false, + }, 'values already in process.env remain') + + t.end() +}) t.test('as non-root', t => { + const backupPath = backupGitConfig() + const { GIT_ASKPASS, GIT_SSH_COMMAND } = process.env + t.teardown(() => { + restoreGitConfig(backupPath) + process.env.GIT_ASKPASS = GIT_ASKPASS + process.env.GIT_SSH_COMMAND = GIT_SSH_COMMAND + }) + process.getuid = () => 999 + t.match(gitOpts({ foo: 'bar', env: { override: 'for some reason' }, @@ -17,5 +134,32 @@ t.test('as non-root', t => { gid: undefined, abc: undefined, }, 'do not set uid/gid as non-root') + + t.end() +}) + +t.test('does not override when sshCommand is set in git config', t => { + const backupPath = backupGitConfig() + const { GIT_ASKPASS, GIT_SSH_COMMAND } = process.env + t.teardown(() => { + restoreGitConfig(backupPath) + process.env.GIT_ASKPASS = GIT_ASKPASS + process.env.GIT_SSH_COMMAND = GIT_SSH_COMMAND + }) + + writeGitConfig(` +[core] + askpass = echo + sshCommand = custom_ssh_command +`) + + t.match(gitOpts(), { + env: { + GIT_ASKPASS: 'undefined', + GIT_SSH_COMMAND: 'undefined', + }, + shell: false, + }, 'sshCommand in git config remains') + t.end() }) From fcd6c9fe0f37e34199ed8508257d8d7321402d04 Mon Sep 17 00:00:00 2001 From: pacotedev Date: Thu, 27 Jun 2024 20:07:06 -0400 Subject: [PATCH 3/5] add requested changes to cache config, as well as namespace node includes and move dependancy --- lib/opts.js | 47 +++++++++++++++++++++++++---------------------- package.json | 4 ++-- test/opts.js | 8 ++++---- 3 files changed, 31 insertions(+), 28 deletions(-) diff --git a/lib/opts.js b/lib/opts.js index ee17149..79d4249 100644 --- a/lib/opts.js +++ b/lib/opts.js @@ -1,34 +1,37 @@ -const fs = require('fs') -const os = require('os') -const path = require('path') +const fs = require('node:fs') +const os = require('node:os') +const path = require('node:path') const ini = require('ini') const gitConfigPath = path.join(os.homedir(), '.gitconfig') -// Function to check if sshCommand is set in the git config -const isGitSshCommandSetInConfig = () => { - try { - if (fs.existsSync(gitConfigPath)) { - const config = ini.parse(fs.readFileSync(gitConfigPath, 'utf-8')) - return config.core && config.core.sshCommand !== undefined +let cachedConfig = null + +// Function to load and cache the git config +const loadGitConfig = () => { + if (cachedConfig === null) { + try { + if (fs.existsSync(gitConfigPath)) { + const configContent = fs.readFileSync(gitConfigPath, 'utf-8') + cachedConfig = ini.parse(configContent) + } else { + cachedConfig = {} + } + } catch (error) { + cachedConfig = {} } - } catch (error) { - return false } - return false + return cachedConfig +} + +const isGitSshCommandSetInConfig = () => { + const config = loadGitConfig() + return config.core && config.core.sshCommand !== undefined } -// Function to check if askpass is set in the git config const isGitAskPassSetInConfig = () => { - try { - if (fs.existsSync(gitConfigPath)) { - const config = ini.parse(fs.readFileSync(gitConfigPath, 'utf-8')) - return config.core && config.core.askpass !== undefined - } - } catch (error) { - return false - } - return false + const config = loadGitConfig() + return config.core && config.core.askpass !== undefined } module.exports = (opts = {}) => { diff --git a/package.json b/package.json index 1a07c60..e4ba710 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,8 @@ "@npmcli/template-oss": "4.22.0", "npm-package-arg": "^11.0.0", "slash": "^3.0.0", - "tap": "^16.0.1" + "tap": "^16.0.1", + "proxyquire": "^2.1.3" }, "dependencies": { "@npmcli/promise-spawn": "^7.0.0", @@ -44,7 +45,6 @@ "proc-log": "^4.0.0", "promise-inflight": "^1.0.1", "promise-retry": "^2.0.1", - "proxyquire": "^2.1.3", "semver": "^7.3.5", "which": "^4.0.0" }, diff --git a/test/opts.js b/test/opts.js index 204f4b2..e8bc2c6 100644 --- a/test/opts.js +++ b/test/opts.js @@ -1,9 +1,9 @@ const t = require('tap') const proxyquire = require('proxyquire') const gitOpts = require('../lib/opts.js') -const fs = require('fs') -const os = require('os') -const path = require('path') +const fs = require('node:fs') +const os = require('node:os') +const path = require('node:path') const gitConfigPath = path.join(os.homedir(), '.gitconfig') @@ -44,7 +44,7 @@ t.test('handle case when fs.existsSync throws an error', t => { // Mocking fs.existsSync to throw an error const gitOptsWithMockFs = proxyquire('../lib/opts.js', { - fs: { + 'node:fs': { ...mockFs, existsSync: () => { throw new Error('Mocked error') From 60964aa2120a4a930a1f3e9aa2355367ccfba3a7 Mon Sep 17 00:00:00 2001 From: pacotedev Date: Fri, 28 Jun 2024 12:06:07 -0400 Subject: [PATCH 4/5] making suggested changes to mock testdir and file rather then use real files --- lib/opts.js | 54 ++++++++++----------- package.json | 3 +- test/opts.js | 133 ++++++++++++++++++++++----------------------------- 3 files changed, 84 insertions(+), 106 deletions(-) diff --git a/lib/opts.js b/lib/opts.js index 79d4249..0128680 100644 --- a/lib/opts.js +++ b/lib/opts.js @@ -9,6 +9,7 @@ let cachedConfig = null // Function to load and cache the git config const loadGitConfig = () => { + /* istanbul ignore next */ if (cachedConfig === null) { try { if (fs.existsSync(gitConfigPath)) { @@ -24,37 +25,36 @@ const loadGitConfig = () => { return cachedConfig } -const isGitSshCommandSetInConfig = () => { +const checkGitConfigs = () => { const config = loadGitConfig() - return config.core && config.core.sshCommand !== undefined + return { + sshCommandSetInConfig: config.core?.sshCommand !== undefined, + askPassSetInConfig: config.core?.askpass !== undefined, + } } -const isGitAskPassSetInConfig = () => { - const config = loadGitConfig() - return config.core && config.core.askpass !== undefined +const sshCommandSetInEnv = process.env.GIT_SSH_COMMAND !== undefined +const askPassSetInEnv = process.env.GIT_ASKPASS !== undefined +const { sshCommandSetInConfig, askPassSetInConfig } = checkGitConfigs() + +// Values we want to set if they're not already defined by the end user +// This defaults to accepting new ssh host key fingerprints +const finalGitEnv = { + ...(askPassSetInEnv || askPassSetInConfig ? {} : { + GIT_ASKPASS: 'echo', + }), + ...(sshCommandSetInEnv || sshCommandSetInConfig ? {} : { + GIT_SSH_COMMAND: 'ssh -oStrictHostKeyChecking=accept-new', + }), } -module.exports = (opts = {}) => { - const sshCommandSetInEnv = process.env.GIT_SSH_COMMAND !== undefined - const sshCommandSetInConfig = isGitSshCommandSetInConfig() - const askPassSetInEnv = process.env.GIT_ASKPASS !== undefined - const askPassSetInConfig = isGitAskPassSetInConfig() - - // Values we want to set if they're not already defined by the end user - // This defaults to accepting new ssh host key fingerprints - const finalGitEnv = { - ...(askPassSetInEnv || askPassSetInConfig ? {} : { - GIT_ASKPASS: 'echo', - }), - ...(sshCommandSetInEnv || sshCommandSetInConfig ? {} : { - GIT_SSH_COMMAND: 'ssh -oStrictHostKeyChecking=accept-new', - }), - } +module.exports = (opts = {}) => ({ + stdioString: true, + ...opts, + shell: false, + env: opts.env || { ...finalGitEnv, ...process.env }, +}) - return { - stdioString: true, - ...opts, - shell: false, - env: opts.env || { ...finalGitEnv, ...process.env }, - } +module.exports._resetCachedConfig = () => { + cachedConfig = null } diff --git a/package.json b/package.json index e4ba710..b3d752c 100644 --- a/package.json +++ b/package.json @@ -34,8 +34,7 @@ "@npmcli/template-oss": "4.22.0", "npm-package-arg": "^11.0.0", "slash": "^3.0.0", - "tap": "^16.0.1", - "proxyquire": "^2.1.3" + "tap": "^16.0.1" }, "dependencies": { "@npmcli/promise-spawn": "^7.0.0", diff --git a/test/opts.js b/test/opts.js index e8bc2c6..0ce3160 100644 --- a/test/opts.js +++ b/test/opts.js @@ -1,49 +1,38 @@ const t = require('tap') -const proxyquire = require('proxyquire') -const gitOpts = require('../lib/opts.js') -const fs = require('node:fs') -const os = require('node:os') -const path = require('node:path') - -const gitConfigPath = path.join(os.homedir(), '.gitconfig') +let [GIT_ASKPASS, GIT_SSH_COMMAND] = ['', ''] const mockFs = { existsSync: () => false, readFileSync: () => '', } -// Utility function to backup and restore gitconfig -const backupGitConfig = () => { - const backupPath = `${gitConfigPath}.backup` - if (fs.existsSync(gitConfigPath)) { - fs.copyFileSync(gitConfigPath, backupPath) - fs.unlinkSync(gitConfigPath) - } - return backupPath -} +const gitOpts = t.mock('../lib/opts.js', { + 'node:fs': mockFs, +}) -const restoreGitConfig = (backupPath) => { - if (fs.existsSync(backupPath)) { - fs.copyFileSync(backupPath, gitConfigPath) - fs.unlinkSync(backupPath) - } else if (fs.existsSync(gitConfigPath)) { - fs.unlinkSync(gitConfigPath) - } -} +t.beforeEach(() => { + gitOpts._resetCachedConfig() + backupEnv() +}) -const writeGitConfig = (content) => { - fs.writeFileSync(gitConfigPath, content) -} +t.afterEach(() => { + restoreEnv() +}) -t.test('handle case when fs.existsSync throws an error', t => { - const { GIT_ASKPASS, GIT_SSH_COMMAND } = process.env - t.teardown(() => { - process.env.GIT_ASKPASS = GIT_ASKPASS - process.env.GIT_SSH_COMMAND = GIT_SSH_COMMAND - }) +t.test('defaults', t => { + t.match(gitOpts(), { + env: { + GIT_ASKPASS: 'echo', + GIT_SSH_COMMAND: 'ssh -oStrictHostKeyChecking=accept-new', + }, + shell: false, + }, 'got the git defaults we want') + + t.end() +}) - // Mocking fs.existsSync to throw an error - const gitOptsWithMockFs = proxyquire('../lib/opts.js', { +t.test('handle case when fs.existsSync throws an error', t => { + const gitOptsWithMockFs = t.mock('../lib/opts.js', { 'node:fs': { ...mockFs, existsSync: () => { @@ -63,38 +52,26 @@ t.test('handle case when fs.existsSync throws an error', t => { t.end() }) -t.test('defaults', t => { - const backupPath = backupGitConfig() - const { GIT_ASKPASS, GIT_SSH_COMMAND } = process.env - t.teardown(() => { - restoreGitConfig(backupPath) - process.env.GIT_ASKPASS = GIT_ASKPASS - process.env.GIT_SSH_COMMAND = GIT_SSH_COMMAND +t.test('handle case when git config does not exist', t => { + const gitOptsWithMockFs = t.mock('../lib/opts.js', { + 'node:fs': { + ...mockFs, + existsSync: () => false, + }, }) - delete process.env.GIT_ASKPASS - delete process.env.GIT_SSH_COMMAND - - t.match(gitOpts(), { + t.match(gitOptsWithMockFs(), { env: { GIT_ASKPASS: 'echo', GIT_SSH_COMMAND: 'ssh -oStrictHostKeyChecking=accept-new', }, shell: false, - }, 'got the git defaults we want') + }, 'should apply defaults when git config does not exist') t.end() }) t.test('does not override when sshCommand is set in env', t => { - const backupPath = backupGitConfig() - const { GIT_ASKPASS, GIT_SSH_COMMAND } = process.env - t.teardown(() => { - restoreGitConfig(backupPath) - process.env.GIT_ASKPASS = GIT_ASKPASS - process.env.GIT_SSH_COMMAND = GIT_SSH_COMMAND - }) - process.env.GIT_ASKPASS = 'test_askpass' process.env.GIT_SSH_COMMAND = 'test_ssh_command' @@ -110,14 +87,6 @@ t.test('does not override when sshCommand is set in env', t => { }) t.test('as non-root', t => { - const backupPath = backupGitConfig() - const { GIT_ASKPASS, GIT_SSH_COMMAND } = process.env - t.teardown(() => { - restoreGitConfig(backupPath) - process.env.GIT_ASKPASS = GIT_ASKPASS - process.env.GIT_SSH_COMMAND = GIT_SSH_COMMAND - }) - process.getuid = () => 999 t.match(gitOpts({ @@ -139,27 +108,37 @@ t.test('as non-root', t => { }) t.test('does not override when sshCommand is set in git config', t => { - const backupPath = backupGitConfig() - const { GIT_ASKPASS, GIT_SSH_COMMAND } = process.env - t.teardown(() => { - restoreGitConfig(backupPath) - process.env.GIT_ASKPASS = GIT_ASKPASS - process.env.GIT_SSH_COMMAND = GIT_SSH_COMMAND - }) - - writeGitConfig(` -[core] + const gitConfigContent = `[core] askpass = echo sshCommand = custom_ssh_command -`) +` + const gitOptsWithMockFs = t.mock('../lib/opts.js', { + 'node:fs': { + ...mockFs, + existsSync: () => true, + readFileSync: () => gitConfigContent, + }, + }) - t.match(gitOpts(), { + t.match(gitOptsWithMockFs(), { env: { - GIT_ASKPASS: 'undefined', - GIT_SSH_COMMAND: 'undefined', + GIT_ASKPASS: null, + GIT_SSH_COMMAND: null, }, shell: false, }, 'sshCommand in git config remains') t.end() }) + +function backupEnv () { + GIT_ASKPASS = process.env.GIT_ASKPASS + GIT_SSH_COMMAND = process.env.GIT_SSH_COMMAND + delete process.env.GIT_ASKPASS + delete process.env.GIT_SSH_COMMAND +} + +function restoreEnv () { + process.env.GIT_ASKPASS = GIT_ASKPASS + process.env.GIT_SSH_COMMAND = GIT_SSH_COMMAND +} From 6072489377ac68f7ae7b7befbe09d707456481dd Mon Sep 17 00:00:00 2001 From: pacotedev Date: Fri, 28 Jun 2024 15:18:51 -0400 Subject: [PATCH 5/5] removing clear cache and instead testing loadGitConfig --- lib/opts.js | 13 +++++-------- test/opts.js | 40 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/lib/opts.js b/lib/opts.js index 0128680..1e80e9e 100644 --- a/lib/opts.js +++ b/lib/opts.js @@ -9,14 +9,12 @@ let cachedConfig = null // Function to load and cache the git config const loadGitConfig = () => { - /* istanbul ignore next */ if (cachedConfig === null) { try { + cachedConfig = {} if (fs.existsSync(gitConfigPath)) { const configContent = fs.readFileSync(gitConfigPath, 'utf-8') cachedConfig = ini.parse(configContent) - } else { - cachedConfig = {} } } catch (error) { cachedConfig = {} @@ -28,8 +26,8 @@ const loadGitConfig = () => { const checkGitConfigs = () => { const config = loadGitConfig() return { - sshCommandSetInConfig: config.core?.sshCommand !== undefined, - askPassSetInConfig: config.core?.askpass !== undefined, + sshCommandSetInConfig: config?.core?.sshCommand !== undefined, + askPassSetInConfig: config?.core?.askpass !== undefined, } } @@ -55,6 +53,5 @@ module.exports = (opts = {}) => ({ env: opts.env || { ...finalGitEnv, ...process.env }, }) -module.exports._resetCachedConfig = () => { - cachedConfig = null -} +// Export the loadGitConfig function for testing +module.exports.loadGitConfig = loadGitConfig diff --git a/test/opts.js b/test/opts.js index 0ce3160..12f1fbb 100644 --- a/test/opts.js +++ b/test/opts.js @@ -1,4 +1,5 @@ const t = require('tap') +const ini = require('ini') let [GIT_ASKPASS, GIT_SSH_COMMAND] = ['', ''] const mockFs = { @@ -11,7 +12,6 @@ const gitOpts = t.mock('../lib/opts.js', { }) t.beforeEach(() => { - gitOpts._resetCachedConfig() backupEnv() }) @@ -131,6 +131,44 @@ t.test('does not override when sshCommand is set in git config', t => { t.end() }) +t.test('does not override when sshCommand is set in git config', t => { + const gitConfigContent = `[core] + askpass = echo + sshCommand = custom_ssh_command +` + + const { loadGitConfig } = t.mock('../lib/opts.js', { + 'node:fs': { + ...mockFs, + existsSync: () => true, + readFileSync: () => gitConfigContent, + }, + }) + + t.match(loadGitConfig(), + ini.parse(gitConfigContent), + 'cachedConfig should be populated with git config' + ) + + const gitOptsWithMockFs = t.mock('../lib/opts.js', { + 'node:fs': { + ...mockFs, + existsSync: () => true, + readFileSync: () => gitConfigContent, + }, + }) + + t.match(gitOptsWithMockFs(), { + env: { + GIT_ASKPASS: null, + GIT_SSH_COMMAND: null, + }, + shell: false, + }, 'sshCommand in git config remains') + + t.end() +}) + function backupEnv () { GIT_ASKPASS = process.env.GIT_ASKPASS GIT_SSH_COMMAND = process.env.GIT_SSH_COMMAND