All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.technologybrewery.habushu.exec.PyenvCommandHelper Maven / Gradle / Ivy

Go to download

Leverages Poetry and Pyenv to provide an automated, predictable order of execution of build commands that apply DevOps and configuration management best practices

There is a newer version: 2.17.0
Show newest version
package org.technologybrewery.habushu.exec;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.Log;
import org.slf4j.event.Level;
import org.technologybrewery.habushu.HabushuException;
import org.technologybrewery.habushu.util.HabushuUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Facilitates the execution of pyenv commands related to managing, selecting,
 * and installation desired Python versions.
 */
public class PyenvCommandHelper {

    private static final String PYENV_COMMAND = "pyenv";
    private static final Logger logger = LoggerFactory.getLogger(PyenvCommandHelper.class);

    private File workingDirectory;

    public PyenvCommandHelper(File workingDirectory) {
        this.workingDirectory = workingDirectory;
    }

    /**
     * Returns a boolean value indicating whether pyenv is installed.
     */
    public boolean isPyenvInstalled() {
        try {
            String foundVersion = executeWithDebugLogging(Arrays.asList("--version"));
            logger.debug("Found " + foundVersion);
        } catch (Throwable e) {
            return false;
        }

        return true;
    }

    /**
     * Retrieves the version of Python that is set for the configured working
     * directory.
     *
     * @return
     */
    public String getCurrentPythonVersion() throws MojoExecutionException {
        return executeWithDebugLogging(Arrays.asList("version-name"));
    }

    /**
     * Updates Python processes launched at the configured working directory to use
     * the specified version of Python by executing the following steps:
     * 
    *
  1. Checks pyenv for the currently installed Python versions
  2. *
  3. If the specified version of Python isn't installed, attempt to install it * using "pyenv install {@literal }"
  4. *
  5. If installing the target Python version using "pyenv install * {@literal }" fails, try to install the target version via "pyenv * install --patch" through {@link #installPythonVersionViaPatch(String)}
  6. *
  7. Finally, set the locally used version of Python (relative to the * configured working directory) to use the target version
  8. *
* it. * * @param targetVersion desired version of Python to use * @param patchInstallScript if installing the specified Python version via * "pyenv install {@literal }" fails, path to * the {@link File} that will be */ public void updatePythonVersion(String targetVersion, File patchInstallScript) throws MojoExecutionException { List installedPythonVersions = getInstalledPythonVersions(); if (!installedPythonVersions.contains(targetVersion)) { logger.info("Could not find Python version {} in following versions [{}] that are installed via pyenv. Installing version {} now...", targetVersion, StringUtils.join(installedPythonVersions, ", "), targetVersion); installPythonVersion(targetVersion, patchInstallScript); } execute(Arrays.asList("local", targetVersion)); } /** * Retrieves a list of the locally installed versions of Python that are managed * by pyenv. * * @return */ private List getInstalledPythonVersions() throws MojoExecutionException { String versionsResult = execute(Arrays.asList("versions", "--bare")); if (StringUtils.isNotEmpty(versionsResult)) { return Arrays.asList(StringUtils.split(versionsResult)); } else { return Collections.emptyList(); } } /** * Installs Python using "pyenv install {@literal }". If this fails, we * try to patch what may be the issue. * * @param targetVersion desired Python version to install * @param patchInstallScript if the initially executed "pyenv install * {@literal }" command fails, write a shell * script to this file which will attempt to patch * what may be the issue. */ private void installPythonVersion(String targetVersion, File patchInstallScript) { try { execute(Arrays.asList("install", targetVersion)); } catch (Throwable t) { logger.warn("Could not install Python {} via normal install, attempting install with patch...", targetVersion); installPythonVersionViaPatch(targetVersion, patchInstallScript); } } /** * Writes the necessary bash script commands to the given {@link File} to * install Python through pyenv with a patch that attempts to fix a compilation * error. After the bash script is written and its permissions appropriately * updated, the script will be executed. * * @param pythonVersion desired Python version to install * @param patchInstallScript target file to which the script will be written and * executed from */ private void installPythonVersionViaPatch(String pythonVersion, File patchInstallScript) { HabushuUtil.createFileAndGivePermissions(patchInstallScript); StringBuilder commandList = new StringBuilder(); commandList.append("#!/bin/bash" + "\n"); commandList.append("pyenv install --patch "); commandList.append(pythonVersion); commandList.append(" < <(curl -sSL https://github.com/python/cpython/commit/8ea6353.patch\\?full_index\\=1) " + "\n"); HabushuUtil.writeLinesToFile(commandList.toString(), patchInstallScript.getAbsolutePath()); try { HabushuUtil.runBashScript(patchInstallScript.getAbsolutePath()); } catch (Throwable t) { throw new HabushuException(String.format("Failed to install Python version %s with patch", pythonVersion), t); } } /** * Executes a pyenv command with the given arguments, logs the executed command at the info level, * and returns the resultant process output as a string. * * @param arguments * @return * @throws MojoExecutionException */ protected String execute(List arguments) throws MojoExecutionException { return execute(arguments, Level.INFO); } /** * Executes a pyenv command with the given arguments, logs the executed command at the debug level, * and returns the resultant process output as a string. * * @param arguments * @return * @throws MojoExecutionException */ protected String executeWithDebugLogging(List arguments) throws MojoExecutionException { return execute(arguments, Level.DEBUG); } private String execute(List arguments, Level logLevel) { ProcessExecutor executor = createPyenvExecutor(arguments); if (Level.DEBUG.equals(logLevel) && logger.isDebugEnabled()) { logger.debug("Executing pyenv command: {} {}", PYENV_COMMAND, StringUtils.join(arguments, " ")); } else if (Level.INFO.equals(logLevel) && logger.isInfoEnabled()) { logger.info("Executing pyenv command: {} {}", PYENV_COMMAND, StringUtils.join(arguments, " ")); } return executor.executeAndGetResult(logger); } protected ProcessExecutor createPyenvExecutor(List arguments) { List fullCommandArgs = new ArrayList<>(); fullCommandArgs.add(PYENV_COMMAND); fullCommandArgs.addAll(arguments); return new ProcessExecutor(workingDirectory, fullCommandArgs, Platform.guess(), null); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy