diff --git a/Jenkinsfile b/Jenkinsfile index ee657098..1ec56d27 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -38,14 +38,11 @@ eventRecorder.timedStage('Integration Test') { String venvVersion = venv.run(returnStdout: true, script: 'python --version') assert venvVersion.startsWith('Python 3') - if (isUnix()) { - echo 'Test VirtualEnv.createWithPyenv' - Object pyvenv = pyenv.createVirtualEnv('3.10.3') - String pyvenvVersion = - pyvenv.run(returnStdout: true, script: 'python --version') - echo pyvenvVersion - assert pyvenvVersion.trim() == 'Python 3.10.3' - } + echo 'Test VirtualEnv.createWithPyenv' + Object pyvenv = pyenv.createVirtualEnv('3.10.3') + String pyvenvVersion = pyvenv.run(returnStdout: true, script: 'python --version') + echo pyvenvVersion + assert pyvenvVersion.trim() == 'Python 3.10.3' } } } diff --git a/VERSION b/VERSION index 3393b5fd..a5510516 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.14.4 +0.15.0 diff --git a/src/com/ableton/Pyenv.groovy b/src/com/ableton/Pyenv.groovy index 21ef396c..840e8c56 100644 --- a/src/com/ableton/Pyenv.groovy +++ b/src/com/ableton/Pyenv.groovy @@ -23,7 +23,8 @@ class Pyenv implements Serializable { Pyenv(Object script, String pyenvRoot) { this.script = script - this.pyenvRoot = pyenvRoot + this.pyenvRoot = script.env.OS == 'Windows_NT' ? + pyenvRoot.replace('\\', '/') : pyenvRoot } /** @@ -44,10 +45,6 @@ class Pyenv implements Serializable { String trimmedPythonVersion = pythonVersion.trim() - if (script.env.OS == 'Windows_NT') { - script.error 'This method is not supported on Windows' - } - if (!versionSupported(trimmedPythonVersion)) { script.withEnv(["PYENV_ROOT=${pyenvRoot}"]) { String pyenvVersion = script.sh( @@ -62,20 +59,31 @@ class Pyenv implements Serializable { VirtualEnv venv = new VirtualEnv(script, randomSeed) script.retry(INSTALLATION_RETRIES) { - List installCommands = [ - "export PYENV_ROOT=${pyenvRoot}", - "export PATH=\$PYENV_ROOT/bin:\$PATH", - 'eval "\$(pyenv init --path)"', - 'eval "\$(pyenv init -)"', - "pyenv install --skip-existing ${trimmedPythonVersion}", - "pyenv shell ${trimmedPythonVersion}", - 'pip install virtualenv', - "virtualenv ${venv.venvRootDir}", - ] - venv.script.sh( - label: "Install Python version ${trimmedPythonVersion} with pyenv", - script: installCommands.join('\n') + '\n', - ) + script.withEnv(["PYENV_VERSION=${trimmedPythonVersion}"]) { + List installCommands = ["export PYENV_ROOT=${pyenvRoot}"] + if (script.env.OS != 'Windows_NT') { + installCommands += [ + "export PATH=\$PYENV_ROOT/bin:\$PATH", + 'eval "\$(pyenv init --path)"', + 'eval "\$(pyenv init -)"', + ] + } else { + String posixPyenvRoot = pyenvRoot[1] == ':' ? + "/${pyenvRoot[0].toLowerCase()}/${pyenvRoot.substring(3)}" : pyenvRoot + installCommands.add( + "export PATH=${posixPyenvRoot}/shims:${posixPyenvRoot}/bin:\$PATH" + ) + } + installCommands += [ + "pyenv install --skip-existing ${trimmedPythonVersion}", + 'pyenv exec pip install virtualenv', + "pyenv exec virtualenv ${venv.venvRootDir}", + ] + venv.script.sh( + label: "Install Python version ${trimmedPythonVersion} with pyenv", + script: installCommands.join('\n') + '\n', + ) + } } return venv @@ -93,10 +101,6 @@ class Pyenv implements Serializable { assertPyenvRoot() boolean result = false - if (script.env.OS == 'Windows_NT') { - script.error 'This method is not supported on Windows' - } - script.withEnv(["PYENV_ROOT=${pyenvRoot}"]) { String allVersions = script.sh( label: 'Get Python versions supported by Pyenv', diff --git a/test/com/ableton/PyenvTest.groovy b/test/com/ableton/PyenvTest.groovy index 1da53626..3c9f6b88 100644 --- a/test/com/ableton/PyenvTest.groovy +++ b/test/com/ableton/PyenvTest.groovy @@ -101,9 +101,26 @@ class PyenvTest extends BasePipelineTest { @Test void createVirtualEnvWindows() { + String pythonVersion = '1.2.3' + String pyenvRoot = 'C:\\mock\\pyenv\\root' + String posixPyenvRoot = '/c/mock/pyenv/root' + String shPyenvRoot = 'C:/mock/pyenv/root' + List shMocks = [ + new Tuple(installCommands( + shPyenvRoot, pythonVersion, false, posixPyenvRoot), '', 0 + ), + new Tuple("${shPyenvRoot}/bin/pyenv install --list", '''Available versions: + 1.2.3 +''', 0), + ] + shMocks.each { mock -> helper.addShMock(mock[0], mock[1], mock[2]) } + helper.registerAllowedMethod('fileExists', [String]) { return true } script.env['OS'] = 'Windows_NT' - assertThrows(Exception) { new Pyenv(script, 'C:\\pyenv').createVirtualEnv('1.2.3') } + Object venv = new Pyenv(script, pyenvRoot).createVirtualEnv(pythonVersion, 1) + + assertEquals("/workspace/.venv/${TEST_RANDOM_NAME}" as String, venv.venvRootDir) + shMocks.each { mock -> assertCallStackContains(mock[0]) } } @Test @@ -135,25 +152,51 @@ class PyenvTest extends BasePipelineTest { assertTrue(new Pyenv(script, pyenvRoot).versionSupported('2.1.3')) assertFalse(new Pyenv(script, pyenvRoot).versionSupported('2.1.3333')) + assertCallStackContains("${pyenvRoot}/bin/pyenv install --list") } @Test void versionSupportedWindows() { + // Resembles pyenv's output, at least as of version 2.3.x + String mockPyenvVersions = '''Available versions: + 2.1.3 + 2.2.3 + 2.3.7 +''' + String shPyenvRoot = 'C:/pyenv' + helper.addShMock("${shPyenvRoot}/bin/pyenv install --list", mockPyenvVersions, 0) + helper.registerAllowedMethod('fileExists', [String]) { return true } script.env['OS'] = 'Windows_NT' - assertThrows(Exception) { new Pyenv(script, 'C:\\pyenv').versionSupported('1.2.3') } + assertTrue(new Pyenv(script, shPyenvRoot).versionSupported('2.1.3')) + assertFalse(new Pyenv(script, shPyenvRoot).versionSupported('2.1.3333')) + assertCallStackContains("${shPyenvRoot}/bin/pyenv install --list") } - private String installCommands(String pyenvRoot, String pythonVersion) { + private String installCommands( + String pyenvRoot, + String pythonVersion, + boolean isUnix = true, + String posixPyenvRoot = null + ) { List installCommands = [ "export PYENV_ROOT=${pyenvRoot}", - "export PATH=\$PYENV_ROOT/bin:\$PATH", - 'eval "\$(pyenv init --path)"', - 'eval "\$(pyenv init -)"', + ] + if (isUnix) { + installCommands += [ + "export PATH=\$PYENV_ROOT/bin:\$PATH", + 'eval "\$(pyenv init --path)"', + 'eval "\$(pyenv init -)"', + ] + } else { + installCommands += [ + "export PATH=${posixPyenvRoot}/shims:${posixPyenvRoot}/bin:\$PATH", + ] + } + installCommands += [ "pyenv install --skip-existing ${pythonVersion}", - "pyenv shell ${pythonVersion}", - 'pip install virtualenv', - "virtualenv /workspace/.venv/${TEST_RANDOM_NAME}", + 'pyenv exec pip install virtualenv', + "pyenv exec virtualenv /workspace/.venv/${TEST_RANDOM_NAME}", ] return installCommands.join('\n') + '\n'