Skip to content

Commit 092e32b

Browse files
nre-abletonablbot
authored andcommitted
Support pyenv on Windows
1 parent f83f73e commit 092e32b

File tree

3 files changed

+71
-28
lines changed

3 files changed

+71
-28
lines changed

Jenkinsfile

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,11 @@ eventRecorder.timedStage('Integration Test') {
3838
String venvVersion = venv.run(returnStdout: true, script: 'python --version')
3939
assert venvVersion.startsWith('Python 3')
4040

41-
if (isUnix()) {
42-
echo 'Test VirtualEnv.createWithPyenv'
43-
Object pyvenv = pyenv.createVirtualEnv('3.10.3')
44-
String pyvenvVersion =
45-
pyvenv.run(returnStdout: true, script: 'python --version')
46-
echo pyvenvVersion
47-
assert pyvenvVersion.trim() == 'Python 3.10.3'
48-
}
41+
echo 'Test VirtualEnv.createWithPyenv'
42+
Object pyvenv = pyenv.createVirtualEnv('3.10.3')
43+
String pyvenvVersion = pyvenv.run(returnStdout: true, script: 'python --version')
44+
echo pyvenvVersion
45+
assert pyvenvVersion.trim() == 'Python 3.10.3'
4946
}
5047
}
5148
}

src/com/ableton/Pyenv.groovy

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ class Pyenv implements Serializable {
2323

2424
Pyenv(Object script, String pyenvRoot) {
2525
this.script = script
26-
this.pyenvRoot = pyenvRoot
26+
this.pyenvRoot = script.env.OS == 'Windows_NT' ?
27+
pyenvRoot.replace('\\', '/') : pyenvRoot
2728
}
2829

2930
/**
@@ -44,10 +45,6 @@ class Pyenv implements Serializable {
4445

4546
String trimmedPythonVersion = pythonVersion.trim()
4647

47-
if (script.env.OS == 'Windows_NT') {
48-
script.error 'This method is not supported on Windows'
49-
}
50-
5148
if (!versionSupported(trimmedPythonVersion)) {
5249
script.withEnv(["PYENV_ROOT=${pyenvRoot}"]) {
5350
String pyenvVersion = script.sh(
@@ -63,11 +60,21 @@ class Pyenv implements Serializable {
6360
VirtualEnv venv = new VirtualEnv(script, randomSeed)
6461
script.retry(INSTALLATION_RETRIES) {
6562
script.withEnv(["PYENV_VERSION=${trimmedPythonVersion}"]) {
66-
List installCommands = [
67-
"export PYENV_ROOT=${pyenvRoot}",
68-
"export PATH=\$PYENV_ROOT/bin:\$PATH",
69-
'eval "\$(pyenv init --path)"',
70-
'eval "\$(pyenv init -)"',
63+
List installCommands = ["export PYENV_ROOT=${pyenvRoot}"]
64+
if (script.env.OS != 'Windows_NT') {
65+
installCommands += [
66+
"export PATH=\$PYENV_ROOT/bin:\$PATH",
67+
'eval "\$(pyenv init --path)"',
68+
'eval "\$(pyenv init -)"',
69+
]
70+
} else {
71+
String posixPyenvRoot = pyenvRoot[1] == ':' ?
72+
"/${pyenvRoot[0].toLowerCase()}/${pyenvRoot.substring(3)}" : pyenvRoot
73+
installCommands.add(
74+
"export PATH=${posixPyenvRoot}/shims:${posixPyenvRoot}/bin:\$PATH"
75+
)
76+
}
77+
installCommands += [
7178
"pyenv install --skip-existing ${trimmedPythonVersion}",
7279
'pyenv exec pip install virtualenv',
7380
"pyenv exec virtualenv ${venv.venvRootDir}",
@@ -94,10 +101,6 @@ class Pyenv implements Serializable {
94101
assertPyenvRoot()
95102
boolean result = false
96103

97-
if (script.env.OS == 'Windows_NT') {
98-
script.error 'This method is not supported on Windows'
99-
}
100-
101104
script.withEnv(["PYENV_ROOT=${pyenvRoot}"]) {
102105
String allVersions = script.sh(
103106
label: 'Get Python versions supported by Pyenv',

test/com/ableton/PyenvTest.groovy

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,26 @@ class PyenvTest extends BasePipelineTest {
101101

102102
@Test
103103
void createVirtualEnvWindows() {
104+
String pythonVersion = '1.2.3'
105+
String pyenvRoot = 'C:\\mock\\pyenv\\root'
106+
String posixPyenvRoot = '/c/mock/pyenv/root'
107+
String shPyenvRoot = 'C:/mock/pyenv/root'
108+
List shMocks = [
109+
new Tuple(installCommands(
110+
shPyenvRoot, pythonVersion, false, posixPyenvRoot), '', 0
111+
),
112+
new Tuple("${shPyenvRoot}/bin/pyenv install --list", '''Available versions:
113+
1.2.3
114+
''', 0),
115+
]
116+
shMocks.each { mock -> helper.addShMock(mock[0], mock[1], mock[2]) }
117+
helper.registerAllowedMethod('fileExists', [String]) { return true }
104118
script.env['OS'] = 'Windows_NT'
105119

106-
assertThrows(Exception) { new Pyenv(script, 'C:\\pyenv').createVirtualEnv('1.2.3') }
120+
Object venv = new Pyenv(script, pyenvRoot).createVirtualEnv(pythonVersion, 1)
121+
122+
assertEquals("/workspace/.venv/${TEST_RANDOM_NAME}" as String, venv.venvRootDir)
123+
shMocks.each { mock -> assertCallStackContains(mock[0]) }
107124
}
108125

109126
@Test
@@ -140,17 +157,43 @@ class PyenvTest extends BasePipelineTest {
140157

141158
@Test
142159
void versionSupportedWindows() {
160+
// Resembles pyenv's output, at least as of version 2.3.x
161+
String mockPyenvVersions = '''Available versions:
162+
2.1.3
163+
2.2.3
164+
2.3.7
165+
'''
166+
String shPyenvRoot = 'C:/pyenv'
167+
helper.addShMock("${shPyenvRoot}/bin/pyenv install --list", mockPyenvVersions, 0)
168+
helper.registerAllowedMethod('fileExists', [String]) { return true }
143169
script.env['OS'] = 'Windows_NT'
144170

145-
assertThrows(Exception) { new Pyenv(script, 'C:\\pyenv').versionSupported('1.2.3') }
171+
assertTrue(new Pyenv(script, shPyenvRoot).versionSupported('2.1.3'))
172+
assertFalse(new Pyenv(script, shPyenvRoot).versionSupported('2.1.3333'))
173+
assertCallStackContains("${shPyenvRoot}/bin/pyenv install --list")
146174
}
147175

148-
private String installCommands(String pyenvRoot, String pythonVersion) {
176+
private String installCommands(
177+
String pyenvRoot,
178+
String pythonVersion,
179+
boolean isUnix = true,
180+
String posixPyenvRoot = null
181+
) {
149182
List installCommands = [
150183
"export PYENV_ROOT=${pyenvRoot}",
151-
"export PATH=\$PYENV_ROOT/bin:\$PATH",
152-
'eval "\$(pyenv init --path)"',
153-
'eval "\$(pyenv init -)"',
184+
]
185+
if (isUnix) {
186+
installCommands += [
187+
"export PATH=\$PYENV_ROOT/bin:\$PATH",
188+
'eval "\$(pyenv init --path)"',
189+
'eval "\$(pyenv init -)"',
190+
]
191+
} else {
192+
installCommands += [
193+
"export PATH=${posixPyenvRoot}/shims:${posixPyenvRoot}/bin:\$PATH",
194+
]
195+
}
196+
installCommands += [
154197
"pyenv install --skip-existing ${pythonVersion}",
155198
'pyenv exec pip install virtualenv',
156199
"pyenv exec virtualenv /workspace/.venv/${TEST_RANDOM_NAME}",

0 commit comments

Comments
 (0)