forked from react-native-community/cli
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: install python if missing on Windows (react-native-community#1040)
* feat: install python if missing on Windows Fix react-native-community#979 * feat: add `fetchToTemp` method Add `fetchToTem` method that downloads a given URL into the OS's temp folder. Also bump `node-fetch` to solve an issue with content-length not being correctly streamed. * chore: pr feedback s 52999ec * chore: limit python version * chore: use logger instead of console * chore: tests * simplify Co-authored-by: Michał Pierzchała <[email protected]>
- Loading branch information
Showing
14 changed files
with
268 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
62 changes: 62 additions & 0 deletions
62
packages/cli/src/commands/doctor/healthchecks/__tests__/python.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import execa from 'execa'; | ||
import python from '../python'; | ||
import getEnvironmentInfo from '../../../../tools/envinfo'; | ||
import {EnvironmentInfo} from '../../types'; | ||
import {NoopLoader} from '../../../../tools/loader'; | ||
import * as common from '../common'; | ||
|
||
jest.mock('execa'); | ||
jest.mock('@react-native-community/cli-tools', () => ({ | ||
fetchToTemp: jest.fn(), | ||
})); | ||
|
||
const logSpy = jest.spyOn(common, 'logManualInstallation'); | ||
|
||
describe('python', () => { | ||
let environmentInfo: EnvironmentInfo; | ||
|
||
beforeAll(async () => { | ||
environmentInfo = await getEnvironmentInfo(); | ||
}, 15000); | ||
|
||
afterEach(() => { | ||
jest.resetAllMocks(); | ||
}); | ||
|
||
it('returns a message if Python is not installed', async () => { | ||
environmentInfo.Languages.Python = 'Not Found'; | ||
((execa as unknown) as jest.Mock).mockResolvedValue({stdout: ''}); | ||
const diagnostics = await python.getDiagnostics(environmentInfo); | ||
expect(diagnostics.needsToBeFixed).toBe(true); | ||
}); | ||
|
||
it('returns false if Python version is in range', async () => { | ||
// @ts-ignore | ||
environmentInfo.Languages.Python = { | ||
version: '2.7.17', | ||
}; | ||
|
||
const diagnostics = await python.getDiagnostics(environmentInfo); | ||
expect(diagnostics.needsToBeFixed).toBe(false); | ||
}); | ||
|
||
// envinfo has a special field for reporting Python 3 so no need to check for python 3 | ||
|
||
it('logs manual installation steps to the screen for the default fix', async () => { | ||
const loader = new NoopLoader(); | ||
await python.runAutomaticFix({loader, environmentInfo}); | ||
expect(logSpy).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it('downloads and executes the installation on Windows when missing', async () => { | ||
const loader = new NoopLoader(); | ||
const loaderSucceedSpy = jest.spyOn(loader, 'succeed'); | ||
const loaderFailSpy = jest.spyOn(loader, 'fail'); | ||
|
||
await python.win32AutomaticFix({loader, environmentInfo}); | ||
|
||
expect(loaderFailSpy).toHaveBeenCalledTimes(0); | ||
expect(logSpy).toHaveBeenCalledTimes(0); | ||
expect(loaderSucceedSpy).toBeCalledWith('Python installed successfully'); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import {fetchToTemp} from '@react-native-community/cli-tools'; | ||
import versionRanges from '../versionRanges'; | ||
import {doesSoftwareNeedToBeFixed} from '../checkInstallation'; | ||
import {logManualInstallation} from './common'; | ||
import {HealthCheckInterface} from '../types'; | ||
|
||
import {updateEnvironment} from '../../../tools/environmentVariables'; | ||
import {join} from 'path'; | ||
import {Ora} from 'ora'; | ||
import {executeCommand} from '../../../tools/executeWinCommand'; | ||
|
||
export default { | ||
label: 'Python', | ||
getDiagnostics: async ({Languages}) => ({ | ||
needsToBeFixed: doesSoftwareNeedToBeFixed({ | ||
version: | ||
typeof Languages.Python === 'string' | ||
? Languages.Python | ||
: Languages.Python.version, | ||
versionRange: versionRanges.PYTHON, | ||
}), | ||
|
||
version: | ||
typeof Languages.Python === 'string' | ||
? Languages.Python | ||
: Languages.Python.version, | ||
versionRange: versionRanges.PYTHON, | ||
}), | ||
win32AutomaticFix: async ({loader}: {loader: Ora}) => { | ||
try { | ||
const arch = process.arch === 'x64' ? 'amd64.' : ''; | ||
const installerUrl = `https://www.python.org/ftp/python/2.7.9/python-2.7.9.${arch}msi`; | ||
const installPath = join(process.env.LOCALAPPDATA || '', 'python2'); | ||
|
||
loader.start(`Downloading Python installer from "${installerUrl}"`); | ||
|
||
const installer = await fetchToTemp(installerUrl); | ||
|
||
loader.text = `Installing Python in "${installPath}"`; | ||
const command = `msiexec.exe /i "${installer}" TARGETDIR="${installPath}" /qn /L*P "python-log.txt"`; | ||
|
||
await executeCommand(command); | ||
|
||
loader.text = 'Updating environment variables'; | ||
|
||
await updateEnvironment('PATH', installPath); | ||
await updateEnvironment('PATH', join(installPath, 'scripts')); | ||
|
||
loader.succeed('Python installed successfully'); | ||
} catch (e) { | ||
loader.fail(e); | ||
} | ||
}, | ||
runAutomaticFix: async () => { | ||
/** | ||
* Python is only needed on Windows so this method should never be called. | ||
* Leaving it in case that changes and as an example of how to have a | ||
* fallback. | ||
*/ | ||
logManualInstallation({ | ||
healthcheck: 'Python', | ||
url: 'https://www.python.org/downloads/', | ||
}); | ||
}, | ||
} as HealthCheckInterface; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import {executeCommand} from './executeWinCommand'; | ||
|
||
/** | ||
* Creates a new variable in the user's environment | ||
*/ | ||
const setEnvironment = async (variable: string, value: string) => { | ||
// https://superuser.com/a/601034 | ||
const command = `setx ${variable} "${value}"`; | ||
return executeCommand(command); | ||
}; | ||
|
||
/** | ||
* Prepends the given `value` to the user's environment `variable`. | ||
* @param {string} variable The environment variable to modify | ||
* @param {string} value The value to add to the variable | ||
* @returns {Promise<void>} | ||
*/ | ||
const updateEnvironment = (variable: string, value: string) => { | ||
// https://superuser.com/a/601034 | ||
const command = `for /f "skip=2 tokens=3*" %a in ('reg query HKCU\\Environment /v ${variable}') do @if [%b]==[] ( @setx ${variable} "${value};%~a" ) else ( @setx ${variable} "${value};%~a %~b" ) | ||
`; | ||
|
||
return executeCommand(command); | ||
}; | ||
|
||
export {setEnvironment, updateEnvironment}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import execa from 'execa'; | ||
|
||
/** | ||
* Executes the given `command` on a shell taking care of slicing the parameters | ||
* if needed. | ||
*/ | ||
const executeShellCommand = async (command: string) => { | ||
const args = command.split(' '); | ||
const program = args.shift()!; | ||
|
||
await execa(program, args, {shell: true}); | ||
}; | ||
|
||
export {executeShellCommand as executeCommand}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.