Skip to content

Commit

Permalink
fix: log errors to log file only when necessary
Browse files Browse the repository at this point in the history
  • Loading branch information
theoludwig committed Sep 9, 2022
1 parent e7a035f commit af71292
Show file tree
Hide file tree
Showing 8 changed files with 84 additions and 48 deletions.
6 changes: 0 additions & 6 deletions src/commands/__test__/delete.test.ts
Expand Up @@ -70,11 +70,5 @@ await tap.test('leon delete', async (t) => {
),
true
)
t.equal(
consoleErrorSpy.calledWith(
`For further information, look at the log file located at ${Log.errorsConfig.path}`
),
true
)
})
})
6 changes: 0 additions & 6 deletions src/commands/__test__/info.test.ts
Expand Up @@ -113,11 +113,5 @@ await tap.test('leon info', async (t) => {
),
true
)
t.equal(
consoleErrorSpy.calledWith(
`For further information, look at the log file located at ${Log.errorsConfig.path}`
),
true
)
})
})
5 changes: 2 additions & 3 deletions src/services/Log.ts
Expand Up @@ -43,12 +43,11 @@ export class Log {
const { commandPath, error } = options
const message = error instanceof Error ? error.message : 'Fatal'
console.error(`${chalk.red('Error:')} ${message}`)
if (error instanceof LogError) {
const logFileMessage = error.logFileMessage ?? ''
if (error instanceof LogError && error.logFileMessage != null) {
const dateString = `[${new Date().toString()}]`
const commandString =
commandPath != null ? `[${Leon.NAME} ${commandPath}]` : ''
const data = `${dateString} ${commandString} ${error.message}\n${logFileMessage}\n\n`
const data = `${dateString} ${commandString} ${error.message}\n${error.logFileMessage}\n\n`
console.error(
`For further information, look at the log file located at ${Log.errorsConfig.path}`
)
Expand Down
3 changes: 3 additions & 0 deletions src/services/Prompt.ts
@@ -1,5 +1,8 @@
import readline from 'node:readline'

/**
* Prompt Singleton Class.
*/
export class Prompt {
static readonly POSITIVE_ANSWERS = ['y', 'yes']
static readonly NEGATIVE_ANSWERS = ['n', 'no']
Expand Down
41 changes: 25 additions & 16 deletions src/services/Requirements.ts
Expand Up @@ -12,8 +12,6 @@ import { PyenvWindows } from './Windows/PyenvWindows.js'
import { PipenvWindows } from './Windows/PipenvWindows.js'
import { isGNULinux, isMacOS, isWindows } from '../utils/operatingSystem.js'

const UNSUPPORTED_OS_MESSAGE = `Your OS (Operating System) is not supported.\nSupported OSes: GNU/Linux, macOS and Windows.`

export interface ExecuteScriptOptions {
loader: {
message: string
Expand All @@ -27,6 +25,21 @@ export interface ExecuteScriptOptions {
* Requirements Singleton Class.
*/
export class Requirements {
static readonly PYTHON_VERSION = '3.9.10'
static readonly PIPENV_VERSION = '2020.11.15'
static readonly PACKAGE_MANAGERS = [
'apk',
'apt',
'brew',
'dnf',
'pacman',
'yum'
]
static readonly UNSUPPORTED_PACKAGE_MANAGER_MESSAGE = `Your package manager is not supported.\nSupported: ${Requirements.PACKAGE_MANAGERS.join(
', '
)}.`
static readonly UNSUPPORTED_OS_MESSAGE = `Your OS (Operating System) is not supported.\nSupported OSes: GNU/Linux, macOS and Windows.`

private static instance: Requirements

private constructor() {}
Expand Down Expand Up @@ -61,7 +74,7 @@ export class Requirements {
try {
const { stdout } = await execaCommand('python --version')
const [, actualVersion] = stdout.split(' ')
return semver.eq(actualVersion, '3.9.10')
return semver.eq(actualVersion, Requirements.PYTHON_VERSION)
} catch {
return false
}
Expand All @@ -71,7 +84,7 @@ export class Requirements {
try {
const { stdout } = await execaCommand('pipenv --version')
const [, , actualVersion] = stdout.split(' ')
return this.checkVersion(actualVersion, '2020.11.15')
return this.checkVersion(actualVersion, Requirements.PIPENV_VERSION)
} catch {
return false
}
Expand All @@ -80,7 +93,8 @@ export class Requirements {
public async checkSoftware(software: string): Promise<boolean> {
try {
const { exitCode } = await execaCommand(`${software} --version`)
return exitCode === 0
const EXIT_CODE_SUCCESS = 0
return exitCode === EXIT_CODE_SUCCESS
} catch {
return false
}
Expand Down Expand Up @@ -118,8 +132,7 @@ export class Requirements {

public async installPackages(): Promise<void> {
let packageManager: string | null = null
const packageManagers = ['apk', 'apt', 'brew', 'dnf', 'pacman', 'yum']
for (const manager of packageManagers) {
for (const manager of Requirements.PACKAGE_MANAGERS) {
if (await this.checkSoftware(manager)) {
packageManager = manager
break
Expand All @@ -139,11 +152,8 @@ export class Requirements {
}
})
} else {
const packageManagersString = packageManagers.join(', ')
const message = `Your package manager is not supported.\nSupported: ${packageManagersString}.`
throw new LogError({
message,
logFileMessage: message
message: Requirements.UNSUPPORTED_PACKAGE_MANAGER_MESSAGE
})
}
}
Expand All @@ -166,9 +176,10 @@ export class Requirements {
const hasPipenv = await this.checkPipenv()
const hasPyenv = await this.checkSoftware('pyenv')
let shouldInstallPipenvAfterPython = true
const pythonVersionString = `Python v${Requirements.PYTHON_VERSION}`
if (!hasPython) {
if (
(interactive && (await prompt.shouldInstall('Python v3.9.10'))) ||
(interactive && (await prompt.shouldInstall(pythonVersionString))) ||
!interactive
) {
if (isGNULinux || isMacOS) {
Expand All @@ -187,8 +198,7 @@ export class Requirements {
await pyenvWindows.install()
} else {
throw new LogError({
message: UNSUPPORTED_OS_MESSAGE,
logFileMessage: UNSUPPORTED_OS_MESSAGE
message: Requirements.UNSUPPORTED_OS_MESSAGE
})
}
}
Expand All @@ -212,8 +222,7 @@ export class Requirements {
await pipenvWindows.install()
} else {
throw new LogError({
message: UNSUPPORTED_OS_MESSAGE,
logFileMessage: UNSUPPORTED_OS_MESSAGE
message: Requirements.UNSUPPORTED_OS_MESSAGE
})
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/services/Windows/PipenvWindows.ts
Expand Up @@ -11,6 +11,9 @@ import {
} from '../../utils/pythonUtils.js'
import { addToPathOnWindows } from '../../utils/pathUtils.js'

/**
* PipenvWindows Singleton Class.
*/
export class PipenvWindows {
private static instance: PipenvWindows

Expand Down
3 changes: 3 additions & 0 deletions src/services/Windows/PyenvWindows.ts
Expand Up @@ -21,6 +21,9 @@ import {
import { isExistingPath } from '../../utils/isExistingPath.js'
import { Requirements } from '../Requirements.js'

/**
* PyenvWindows Singleton Class.
*/
export class PyenvWindows {
static NAME = 'pyenv-win'
static GITHUB_URL = `https://github.com/${PyenvWindows.NAME}/${PyenvWindows.NAME}`
Expand Down
65 changes: 48 additions & 17 deletions src/services/__test__/Log.test.ts
Expand Up @@ -3,34 +3,65 @@ import fs from 'node:fs'
import tap from 'tap'
import sinon from 'sinon'
import fsMock from 'mock-fs'
import chalk from 'chalk'

import { LogError } from '../../utils/LogError.js'
import { Log } from '../Log.js'

await tap.test('services/Log', async (t) => {
const message = 'Error occured'
const log = Log.getInstance()

t.afterEach(() => {
fsMock.restore()
sinon.restore()
})

await t.test('should write to file the error data', async (t) => {
fsMock({
[Log.errorsConfig.path]: ''
})
sinon.stub(console, 'error').value(() => {})
const consoleErrorSpy = sinon.spy(console, 'error')
let fileContent = await fs.promises.readFile(Log.errorsConfig.path, {
encoding: 'utf-8'
})
t.equal(fileContent.length, 0)
log.error({ error: new LogError({ message }) })
t.equal(consoleErrorSpy.called, true)
fileContent = await fs.promises.readFile(Log.errorsConfig.path, {
encoding: 'utf-8'
})
t.equal(fileContent.includes(message), true)
await t.test('error', async (t) => {
await t.test(
'should only display the `message` with `logFileMessage` not defined',
async (t) => {
sinon.stub(console, 'error').value(() => {})
const message = 'message'
const consoleErrorSpy = sinon.spy(console, 'error')
log.error({ error: new LogError({ message }) })
t.equal(
consoleErrorSpy.calledWith(`${chalk.red('Error:')} ${message}`),
true
)
}
)

await t.test(
'should display the `message` and write to log file the `logFileMessage`',
async (t) => {
fsMock({
[Log.errorsConfig.path]: ''
})
sinon.stub(console, 'error').value(() => {})
const message = 'message'
const logFileMessage = 'logFileMessage'
const consoleErrorSpy = sinon.spy(console, 'error')
let fileContent = await fs.promises.readFile(Log.errorsConfig.path, {
encoding: 'utf-8'
})
t.equal(fileContent.length, 0)
log.error({ error: new LogError({ message, logFileMessage }) })
t.equal(
consoleErrorSpy.calledWith(`${chalk.red('Error:')} ${message}`),
true
)
t.equal(
consoleErrorSpy.calledWith(
`For further information, look at the log file located at ${Log.errorsConfig.path}`
),
true
)
fileContent = await fs.promises.readFile(Log.errorsConfig.path, {
encoding: 'utf-8'
})
t.equal(fileContent.includes(message), true)
t.equal(fileContent.includes(logFileMessage), true)
}
)
})
})

0 comments on commit af71292

Please sign in to comment.