Skip to content

Commit

Permalink
fix: installation Pyenv (Python and Pipenv) on Windows (#188)
Browse files Browse the repository at this point in the history
  • Loading branch information
theoludwig authored Jul 8, 2022
1 parent c292af6 commit 4772052
Show file tree
Hide file tree
Showing 17 changed files with 1,312 additions and 1,316 deletions.
1 change: 1 addition & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,4 @@ jobs:

- name: 'End To End (e2e) Test'
run: 'npm run test:e2e-classic'
shell: 'bash'
2,281 changes: 1,164 additions & 1,117 deletions package-lock.json

Large diffs are not rendered by default.

22 changes: 10 additions & 12 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,15 @@
"release": "semantic-release"
},
"dependencies": {
"@sinclair/typebox": "0.23.5",
"adm-zip": "0.5.9",
"axios": "0.27.2",
"@sinclair/typebox": "0.24.11",
"axios": "0.26.1",
"chalk": "5.0.1",
"clipanion": "3.1.0",
"conf": "10.1.2",
"date-and-time": "2.4.0",
"execa": "6.1.0",
"extract-zip": "2.0.1",
"ora": "6.1.1",
"ora": "6.1.2",
"read-pkg": "7.1.0",
"semver": "7.3.7",
"simple-git": "3.10.0",
Expand All @@ -78,22 +77,21 @@
"@commitlint/cli": "17.0.3",
"@commitlint/config-conventional": "17.0.3",
"@swc/cli": "0.1.57",
"@swc/core": "1.2.205",
"@types/adm-zip": "0.5.0",
"@swc/core": "1.2.210",
"@types/mock-fs": "4.13.1",
"@types/node": "18.0.0",
"@types/node": "18.0.3",
"@types/semver": "7.3.10",
"@types/sinon": "10.0.11",
"@types/sinon": "10.0.12",
"@types/tap": "15.0.7",
"@types/update-notifier": "5.1.0",
"@types/update-notifier": "6.0.1",
"@types/wait-on": "5.3.1",
"@typescript-eslint/eslint-plugin": "5.29.0",
"@typescript-eslint/eslint-plugin": "5.30.5",
"editorconfig-checker": "4.0.2",
"eslint": "8.18.0",
"eslint": "8.19.0",
"eslint-config-conventions": "2.0.0",
"eslint-config-prettier": "8.5.0",
"eslint-plugin-import": "2.26.0",
"eslint-plugin-prettier": "4.0.0",
"eslint-plugin-prettier": "4.2.1",
"eslint-plugin-promise": "6.0.0",
"eslint-plugin-unicorn": "42.0.0",
"markdownlint-cli": "0.31.1",
Expand Down
6 changes: 3 additions & 3 deletions src/commands/__test__/delete.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
LeonInstance,
LeonInstanceOptions
} from '../../services/LeonInstance.js'
import { isExistingFile } from '../../utils/isExistingFile.js'
import { isExistingPath } from '../../utils/isExistingPath.js'
import { Log } from '../../services/Log.js'

await tap.test('leon delete', async (t) => {
Expand Down Expand Up @@ -42,12 +42,12 @@ await tap.test('leon delete', async (t) => {
[config.path]: JSON.stringify(configData),
[leonInstance.path]: {}
})
t.equal(await isExistingFile(leonInstance.path), true)
t.equal(await isExistingPath(leonInstance.path), true)
const command = cli.process(['delete', '--yes'])
const exitCode = await command.execute()
const instances = config.get('instances', [])
t.equal(exitCode, 0)
t.equal(await isExistingFile(leonInstance.path), false)
t.equal(await isExistingPath(leonInstance.path), false)
t.strictSame(instances, [])
t.equal(
consoleLogSpy.calledWith(`Leon instance "${leonInstance.name}" deleted.`),
Expand Down
4 changes: 3 additions & 1 deletion src/commands/create/birth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,11 @@ export class CreateBirthCommand extends Command {
})
await leon.createBirth()
console.log(`\n${chalk.bold.green('Success:')} Leon is born! 🎉`)
console.log('You can start your leon instance using:')
console.log('You can start your leon instance:')
if (isLinux || isMacOS) {
console.log(`${chalk.cyan('exec $SHELL')}`)
} else {
console.log(`First, restart your command prompt.`)
}
console.log(`${chalk.cyan('leon start')}`)
return 0
Expand Down
12 changes: 6 additions & 6 deletions src/e2e/tests/1-create-birth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import tap from 'tap'
import { execa } from 'execa'

import { Leon } from '../../services/Leon.js'
import { isExistingFile } from '../../utils/isExistingFile.js'
import { isExistingPath } from '../../utils/isExistingPath.js'

interface Options {
useDocker?: boolean
Expand All @@ -17,27 +17,27 @@ export const test1CreateBirth = async (

await tap.test('leon create birth', async (t) => {
const commandOptions = useDocker ? ['--docker'] : []
t.equal(await isExistingFile(Leon.DEFAULT_BIRTH_PATH), false)
t.equal(await isExistingPath(Leon.DEFAULT_BIRTH_PATH), false)
const result = await execa(
'leon',
['create', 'birth', '--yes', ...commandOptions],
{ stdio: 'inherit' }
)
t.equal(result.exitCode, 0)
t.equal(await isExistingFile(Leon.DEFAULT_BIRTH_PATH), true)
t.equal(await isExistingPath(Leon.DEFAULT_BIRTH_PATH), true)
t.equal(
await isExistingFile(path.join(Leon.DEFAULT_BIRTH_PATH, 'package.json')),
await isExistingPath(path.join(Leon.DEFAULT_BIRTH_PATH, 'package.json')),
true
)
if (!useDocker) {
t.equal(
await isExistingFile(
await isExistingPath(
path.join(Leon.DEFAULT_BIRTH_PATH, 'node_modules')
),
true
)
t.equal(
await isExistingFile(
await isExistingPath(
path.join(Leon.DEFAULT_BIRTH_PATH, 'server', 'dist')
),
true
Expand Down
10 changes: 6 additions & 4 deletions src/services/Leon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
createTemporaryEmptyFolder,
TEMPORARY_PATH
} from '../utils/createTemporaryEmptyFolder.js'
import { isExistingFile } from '../utils/isExistingFile.js'
import { isExistingPath } from '../utils/isExistingPath.js'
import { LeonInstance } from './LeonInstance.js'
import { LogError } from '../utils/LogError.js'
import { copyDirectory } from '../utils/copyDirectory.js'
Expand Down Expand Up @@ -86,7 +86,8 @@ export class Leon implements LeonOptions {
public async getSourceCode(): Promise<string> {
const loader = ora(`Downloading Leon source code`).start()
try {
let sourceCodePath = await this.download()
await createTemporaryEmptyFolder()
let sourceCodePath = ''
const hasGitInstalled = await requirements.checkGit()
if (hasGitInstalled && this.useGit) {
sourceCodePath = path.join(TEMPORARY_PATH, 'leon-ai-git')
Expand All @@ -99,6 +100,8 @@ export class Leon implements LeonOptions {
} else {
await git.checkout('master')
}
} else {
sourceCodePath = await this.download()
}
loader.succeed()
return sourceCodePath
Expand All @@ -118,7 +121,6 @@ export class Leon implements LeonOptions {
TEMPORARY_PATH,
sourceCodeInformation.folderName
)
await createTemporaryEmptyFolder()
const { data } = await axios.get(sourceCodeInformation.url, {
responseType: 'arraybuffer'
})
Expand All @@ -137,7 +139,7 @@ export class Leon implements LeonOptions {
}

public async createBirth(): Promise<void> {
if (await isExistingFile(this.birthPath)) {
if (await isExistingPath(this.birthPath)) {
throw new LogError({
message: `${this.birthPath} already exists, please provide another path.`
})
Expand Down
4 changes: 2 additions & 2 deletions src/services/LeonInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import chalk from 'chalk'

import { config } from './Config.js'
import { LogError } from '../utils/LogError.js'
import { isExistingFile } from '../utils/isExistingFile.js'
import { isExistingPath } from '../utils/isExistingPath.js'
import { Leon } from './Leon.js'

export type InstanceType = 'classic' | 'docker'
Expand Down Expand Up @@ -67,7 +67,7 @@ export class LeonInstance implements LeonInstanceOptions {
public async startClassic(LEON_PORT: string): Promise<void> {
if (this.startCount === 1) {
const dotenvPath = path.join(this.path, '.env')
if (await isExistingFile(dotenvPath)) {
if (await isExistingPath(dotenvPath)) {
await fs.promises.rm(dotenvPath)
}
await this.install()
Expand Down
5 changes: 2 additions & 3 deletions src/services/Requirements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ class Requirements {
const commandPath = path.join(scriptsPath, ...scriptCommand)
try {
if (sudo && !isMacOS) {
if (process.env.NODE_ENV !== 'test') {
try {
await sudoExec(commandPath)
} else {
} catch {
await execaCommand(`sudo --non-interactive ${commandPath}`)
}
} else {
Expand Down Expand Up @@ -185,7 +185,6 @@ class Requirements {
})
} else if (isWindows) {
await pipenvWindows.install()
await pipenvWindows.addToPath()
} else {
throw new LogError({
message: UNSUPPORTED_OS_MESSAGE,
Expand Down
46 changes: 16 additions & 30 deletions src/services/Windows/PipenvWindows.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import path from 'node:path'

import { execa } from 'execa'
import { execaCommand } from 'execa'
import ora from 'ora'

import { LogError } from '../../utils/LogError.js'
Expand All @@ -9,14 +9,16 @@ import {
getPythonSiteString,
getPythonVersionString
} from '../../utils/pythonUtils.js'
import { addToPath } from '../../utils/pathUtils.js'
import { requirements } from '../Requirements.js'
import { addToPathOnWindows } from '../../utils/pathUtils.js'

class PipenvWindows {
public async install(): Promise<void> {
const pipenvLoader = ora('Installing pipenv').start()
try {
await execa('pip install --user pipenv')
await execaCommand('pip install --user pipenv', {
shell: 'powershell.exe'
})
await this.addToPath()
pipenvLoader.succeed()
} catch (error: any) {
pipenvLoader.fail()
Expand All @@ -27,35 +29,19 @@ class PipenvWindows {
}
}

private async addToWindowsPath(): Promise<void> {
const pythonSitePath = await getPythonSiteString()
const formattedPythonVersion = extractVersionForPath(
await getPythonVersionString()
)
const fullPathToPythonSite = path.join(
pythonSitePath,
`Python${formattedPythonVersion}`,
'Scripts'
)
if (
!requirements.checkIfEnvironmentVariableContains(
'PATH',
fullPathToPythonSite
)
) {
await addToPath(fullPathToPythonSite)
}
}

public async addToPath(): Promise<void> {
const registeringPipenvLoader = ora('Registering pipenv to path').start()
try {
if (process.platform === 'win32') {
await this.addToWindowsPath()
}
registeringPipenvLoader.succeed()
const pythonSitePath = await getPythonSiteString()
const formattedPythonVersion = extractVersionForPath(
await getPythonVersionString()
)
const fullPathToPythonSite = path.join(
pythonSitePath,
`Python${formattedPythonVersion}`,
'Scripts'
)
await addToPathOnWindows(fullPathToPythonSite)
} catch (error: any) {
registeringPipenvLoader.fail()
throw new LogError({
message: 'Impossible to register Pipenv environment variables',
logFileMessage: error.toString()
Expand Down
Loading

0 comments on commit 4772052

Please sign in to comment.