Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(command-lm): add uninstall command #1879

Merged
merged 2 commits into from
Feb 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions src/commands/lm/uninstall.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const Command = require('../../utils/command')
const { uninstall } = require('../../utils/lm/install')

class LmUninstallCommand extends Command {
async run() {
await this.config.runHook('analytics', {
eventName: 'command',
payload: {
command: 'lm:uninstall',
},
})

await uninstall()
}
}

LmUninstallCommand.aliases = ['lm:remove']
LmUninstallCommand.hidden = true

LmUninstallCommand.description =
'Uninstalls Netlify git credentials helper and cleans up any related configuration changes made by the install command.'

module.exports = LmUninstallCommand
77 changes: 61 additions & 16 deletions src/utils/lm/install.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,21 @@ const Listr = require('listr')
const pathKey = require('path-key')

const { shouldFetchLatestVersion, fetchLatestVersion } = require('../../lib/exec-fetcher')
const { fileExistsAsync, writeFileAsync, readFileAsync, appendFileAsync, copyFileAsync } = require('../../lib/fs')
const {
fileExistsAsync,
writeFileAsync,
readFileAsync,
appendFileAsync,
copyFileAsync,
rmdirRecursiveAsync,
} = require('../../lib/fs')
const { getPathInHome, getLegacyPathInHome } = require('../../lib/settings')

const PACKAGE_NAME = 'netlify-credential-helper'
const EXEC_NAME = 'git-credential-netlify'

const GIT_CONFIG = '.gitconfig'

const { checkGitVersionStep, checkGitLFSVersionStep, checkLFSFiltersStep } = require('./steps')

const SUPPORTED_PLATFORMS = {
Expand Down Expand Up @@ -126,16 +135,18 @@ const setupWindowsPath = async function () {
)
}

const getInitContent = (incFilePath) => `
# The next line updates PATH for Netlify's Git Credential Helper.
if [ -f '${incFilePath}' ]; then source '${incFilePath}'; fi
`

const setupUnixPath = async () => {
if (isBinInPath()) {
return true
}

const { shell, incFilePath, configFile } = shellVariables()
const initContent = `
# The next line updates PATH for Netlify's Git Credential Helper.
if [ -f '${incFilePath}' ]; then source '${incFilePath}'; fi
`
const initContent = getInitContent(incFilePath)

switch (shell) {
case 'bash':
Expand Down Expand Up @@ -181,6 +192,13 @@ const getCurrentCredentials = async () => {
}
}

// Git expects the config path to always use / even on Windows
const getGitConfigContent = (gitConfigPath) => `
# This next lines include Netlify's Git Credential Helper configuration in your Git configuration.
[include]
path = ${path.posix.normalize(gitConfigPath)}
Copy link
Contributor

@ehmicky ehmicky Feb 15, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might not work as expected.
path.posix.normalize() actually returns backslashes on Windows (which is confusing).

Is the intent to only use forward slashes? If so, I have personally used this unmaintained package: https://github.com/jonschlinkert/unixify (we use it in zip-it-and-ship-it). If you find a better package, I'd be curious to learn about it too :)

Edit: I am realizing this change was not introduced by the current PR, so please feel free to ignore for this PR.

`

const configureGitConfig = async function () {
const currentCredentials = await getCurrentCredentials()

Expand Down Expand Up @@ -211,17 +229,10 @@ const configureGitConfig = async function () {
})
}

const helperPath = getHelperPath()
await writeFileAsync(path.join(helperPath, 'git-config'), helperConfig)
const gitConfigPath = getGitConfigPath()
await writeFileAsync(gitConfigPath, helperConfig)

// Git expects the config path to always use / even on Windows
const gitConfigPath = path.posix.normalize(`${helperPath}/git-config`)
const gitConfigContent = `
# This next lines include Netlify's Git Credential Helper configuration in your Git configuration.
[include]
path = ${gitConfigPath}
`
return writeConfig('.gitconfig', gitConfigContent)
return writeConfig(GIT_CONFIG, getGitConfigContent(gitConfigPath))
}

const getHelperPath = function () {
Expand All @@ -232,6 +243,10 @@ const getBinPath = function () {
return path.join(getHelperPath(), 'bin')
}

const getGitConfigPath = function () {
return path.join(getHelperPath(), 'git-config')
}

const getLegacyBinPath = function () {
return path.join(getLegacyPathInHome(['helper', 'bin']))
}
Expand All @@ -255,4 +270,34 @@ const shellVariables = function () {
}
}

module.exports = { installPlatform, isBinInPath, shellVariables }
const cleanupShell = async function () {
try {
const { configFile, incFilePath } = shellVariables()
if (configFile === undefined) {
return
}

await removeConfig(configFile, getInitContent(incFilePath))
} catch (_) {}
}

const uninstall = async function () {
await Promise.all([
rmdirRecursiveAsync(getHelperPath()),
removeConfig(GIT_CONFIG, getGitConfigContent(getGitConfigPath())),
cleanupShell(),
])
}

const removeConfig = async function (name, toRemove) {
const configPath = path.join(os.homedir(), name)

if (!(await fileExistsAsync(configPath))) {
return
}

const content = await readFileAsync(configPath, 'utf8')
return await writeFileAsync(configPath, content.replace(toRemove, ''))
}

module.exports = { installPlatform, isBinInPath, shellVariables, uninstall }
3 changes: 3 additions & 0 deletions tests/command.lm.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ if (process.env.IS_FORK !== 'true') {
t.context.builder = builder
t.context.mockApi = mockApi
t.context.apiUrl = `http://localhost:${mockApi.address().port}/api/v1`

await callCli(['lm:uninstall'], t.context.execOptions)
})

test.serial('netlify lm:info', async (t) => {
Expand Down Expand Up @@ -91,6 +93,7 @@ if (process.env.IS_FORK !== 'true') {
test.after('cleanup', async (t) => {
const { execOptions, builder, mockApi } = t.context
console.log('Performing cleanup')
await callCli(['lm:uninstall'], t.context.execOptions)
console.log(`deleting test site "${siteName}". ${execOptions.env.NETLIFY_SITE_ID}`)
await callCli(['sites:delete', execOptions.env.NETLIFY_SITE_ID, '--force'], execOptions)

Expand Down