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

org.technologybrewery.habushu.CleanHabushuMojo 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

The newest version!
package org.technologybrewery.habushu;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.clean.CleanMojo;
import org.apache.maven.plugins.clean.Fileset;
import org.technologybrewery.habushu.exec.PoetryCommandHelper;
import org.technologybrewery.habushu.util.HabushuUtil;

import java.io.File;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * Overrides the default {@link CleanMojo} behavior to additionally delete the
 * virtual environment that is created/managed by Poetry if the
 * {@link #deleteVirtualEnv} option is enabled.
 */
@Mojo(name = "clean-habushu", defaultPhase = LifecyclePhase.CLEAN)
public class CleanHabushuMojo extends CleanMojo {

    /**
     * Base directory in which Poetry projects will be located - should always be
     * the basedir of the encapsulating Maven project.
     */
    @Parameter(defaultValue = "${project.basedir}", readonly = true, required = true)
    protected File workingDirectory;

    /**
     * Directory in which Poetry places generated source and wheel archive
     * distributions.
     */
    @Parameter(defaultValue = "${project.basedir}/dist", readonly = true, required = true)
    protected File distDirectory;

    /**
     * Directory in which Maven places build-time artifacts - should NOT include dist items.
     */
    @Parameter(defaultValue = "${project.basedir}/target", readonly = true, required = true)
    protected File targetDirectory;

    /**
     * The packaging type of the current Maven project. If it is not "habushu", then this mojo will be skipped.
     */
    @Parameter(defaultValue = "${project.packaging}", readonly = true, required = true)
    protected String packaging;

    /**
     * Enables the explicit deletion of the virtual environment that is
     * created/managed by Poetry.
     */
    @Parameter(property = "habushu.deleteVirtualEnv", required = true, defaultValue = "false")
    protected boolean deleteVirtualEnv;

    /**
     * The desired version of Python to use.
     */
    @Parameter(defaultValue = PyenvAndPoetrySetup.PYTHON_DEFAULT_VERSION_REQUIREMENT, property = "habushu.pythonVersion")
    protected String pythonVersion;

    /**
     * Should Habushu use pyenv to manage the utilized version of Python?
     */
    @Parameter(defaultValue = "true", property = "habushu.usePyenv")
    protected boolean usePyenv;

    /**
     * File specifying the location of a generated shell script that will attempt to
     * install the specified version of Python using "pyenv install --patch" with a
     * patch that attempts to resolve the expected compilation error.
     */
    @Parameter(defaultValue = "${project.build.directory}/pyenv-patch-install-python-version.sh", readonly = true)
    private File patchInstallScript;

    /**
     * Indicates whether Habushu should leverage the
     * {@code poetry-monorepo-dependency-plugin} to rewrite any local path
     * dependencies (to other Poetry projects) as versioned packaged dependencies in
     * generated wheel/sdist archives. If {@code true}, Habushu will replace
     * invocations of Poetry's {@code build} and {@code publish} commands in the
     * {@link BuildDeploymentArtifactsMojo} and {@link PublishToPyPiRepoMojo} with
     * the extensions of those commands exposed by the
     * {@code poetry monorepo-dependency-plugin}, which are
     * {@code build-rewrite-path-deps} and {@code publish-rewrite-path-deps}
     * respectively.
     * 

* Typically, this flag will only be {@code true} when deploying/releasing * Habushu modules within a CI environment that are part of a monorepo project * structure which multiple Poetry projects depend on one another. */ @Parameter(defaultValue = "false", property = "habushu.rewriteLocalPathDepsInArchives") protected boolean rewriteLocalPathDepsInArchives; /** * Whether to configure Poetry's {@code virtualenvs.in-project} value for this project. * If configured, virtual environments will be migrated to this approach during the clean phase of the build. *

* While generally easier to find and use for tasks like debugging, having your virtual environment co-located in * your project may be less useful for executions like CI builds where you may want to centrally caches virtual * environments from a central location. */ @Parameter(defaultValue = "true", property = "habushu.useInProjectVirtualEnvironment") protected boolean useInProjectVirtualEnvironment; @Override public void execute() throws MojoExecutionException { if ("habushu".equals(packaging)) { clean(); } else { getLog().info("Skipping execution - packaging type is not 'habushu'"); } } private void clean() throws MojoExecutionException { List filesetsToDelete = new ArrayList<>(); boolean removeVenvManually = false; String virtualEnvFullPath = HabushuUtil.findCurrentVirtualEnvironmentFullPath( pythonVersion, usePyenv, patchInstallScript, workingDirectory, rewriteLocalPathDepsInArchives, getLog()); virtualEnvFullPath = HabushuUtil.getCleanVirtualEnvironmentPath(virtualEnvFullPath); String inVirtualEnvironmentPath = HabushuUtil.getInProjectVirtualEnvironmentPath(this.workingDirectory); File venv = new File(inVirtualEnvironmentPath); if (this.useInProjectVirtualEnvironment) { getLog().debug("`in-project` virtual environment configured for this project"); if (StringUtils.isNotBlank(virtualEnvFullPath) && !inVirtualEnvironmentPath.equals(virtualEnvFullPath)) { getLog().warn("'in-project' virtual environment is configured, but an external virtual environment was found!"); getLog().warn("Deleting external virtual environment: " + virtualEnvFullPath); deleteVirtualEnv = true; } } else { if (venv.exists()) { getLog().warn("'in-project' virtual environment is NOT configured, but an 'in-project' virtual environment was found!"); getLog().warn("Deleting 'in-project' virtual environment: " + virtualEnvFullPath); deleteVirtualEnv = true; } } if (deleteVirtualEnv) { if (StringUtils.isBlank(virtualEnvFullPath)) { getLog().warn("No Poetry virtual environment was detected for deletion."); } else { String virtualEnvName = new File(virtualEnvFullPath).getName(); if (StringUtils.isNotBlank(virtualEnvName)) { PoetryCommandHelper poetryHelper = new PoetryCommandHelper(this.workingDirectory); List arguments = new ArrayList<>(); arguments.add("env"); arguments.add("remove"); if (!".venv".equals(virtualEnvName)) { arguments.add(virtualEnvName); } else { // While Poetry 1.8.3 and lower will unregister the .venv virtual environment, it doesn't // remove it, creating confusion: removeVenvManually = true; } poetryHelper.execute(arguments); } } } try { Fileset distArchivesFileset = createFileset(distDirectory); filesetsToDelete.add(distArchivesFileset); Fileset targetArchivesFileset = createFileset(targetDirectory); filesetsToDelete.add(targetArchivesFileset); if (removeVenvManually) { filesetsToDelete.add(createFileset(venv)); } } catch (IllegalAccessException e) { throw new MojoExecutionException("Could not write to private field in Fileset class.", e); } getLog().info(String.format("Deleting distribution archives at %s", distDirectory)); getLog().info(String.format("Deleting target archives at %s", targetDirectory)); setPrivateParentField("filesets", filesetsToDelete.toArray(new Fileset[0])); super.execute(); } /** * Creates a new {@link Fileset} that may be used to identify a set of files * that are targeted for deletion by the {@link CleanMojo}. * * @param directory directory that is desired for deletion. * @return * @throws IllegalAccessException */ private Fileset createFileset(File directory) throws IllegalAccessException { Fileset fileset = new Fileset(); FieldUtils.writeField(fileset, "directory", directory, true); return fileset; } /** * Sets a given field in the parent {@link CleanMojo} with a provided value. * This method is needed as {@link CleanMojo} is structured in a way that does * not easily facilitate extension. * * @param fieldName the name of the field in {@link CleanMojo} * @param value the field's intended value */ private void setPrivateParentField(String fieldName, Object value) { Field fieldInParentClass; try { fieldInParentClass = this.getClass().getSuperclass().getDeclaredField(fieldName); fieldInParentClass.setAccessible(true); fieldInParentClass.set(this, value); } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { throw new HabushuException("Could not write to field in CleanMojo class.", e); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy