In Okinawa, habushu (pronounced HA-BU-SHU) is a sake that is made with venomous snake. The alcohol in the snake assists in dissolving the snake's venom and making it non-poinsonous. In Maven, Habushu allows virtual environment-based Python projects to be included as part a Maven build. This brings some order and consistency to what can otherwise be haphazardly structured projects.
Habushu is implemented as a series of Maven plugins that tie together existing tooling in an opinionated fashion similar to how Maven structures Java projects.
By taking care of manual steps and bringing a predictable order of execution and core naming conventions, Habushu increases time spent on impactful work rather than every developer or data scientist building out scripts that meet their personal preferences.
No one person will agree with all the opinions implemented by Habushu. The value in being able to run entire builds from a single mvn clean install
command regardless of your prior experience with the projects adds substantial value - both locally and in DevSecOps scenarios.
More information about how Habushu can be used and benefit your project can be found in this blog post, which covers how Habushu helps acheive repeatable Python builds in polyglot monorepos.
In order to use Habushu, the following prerequisites must be installed:
- Maven 3.6+
- Maven 3.9+ for use with Maven Build Cache
- Java 11+
- Poetry 1.5+
- Pyenv
Additionally, Habushu may install and manage:
Habushu automates a consistent and predictable build lifecycle by delegating nearly all commands related to dependency management, virtual environment activation, and package publishing to Poetry. As a result, Habushu projects are Poetry projects and are expected to align with the conventions, structure, and configurations utilized by Poetry projects with the src/
packaging layout.
A Poetry project using the src/
packaging layout only needs an appropriately configured pom.xml
within the root level of the project to instrumented through Habushu and participate in a Maven build lifecycle. The following depicts the required folder structure within an example Habushu module named spam-ham-eggs
, including the placement of the required pom.xml
and pyproject.toml
configurations and utilization of behave for automated testing:
spam-ham-eggs
├── pyproject.toml
├── poetry.toml
├── pom.xml
├── src
│ └── spam_ham_eggs
│ └── __init__.py
└── tests
└── features
├── spam_ham_eggs.feature
└── steps
└── spam_ham_eggs_step.py
NOTE: The above includes an optional poetry.toml
which includes additional configuration settings for Poetry. This file is not required but should be included in version control to ensure consistent builds.
Best practices for creating a new Poetry project (possibly based on an existing Python package or older Habushu module) and adding needed Habushu plugin declaration to the module's pom.xml
are described below.
If starting from scratch, use the poetry new --src
command to create a new Poetry project:
$ poetry new spam-ham-eggs --src
Created package spam_ham_eggs in spam-ham-eggs
$ ls spam-ham-eggs
README.rst pyproject.toml src tests
If migrating an existing Python package, consider using poetry init
and use the interactive guide to create the desired pyproject.toml
configuration with the appropriate dependencies.
If migrating an earlier release of Habushu, follow the same process, but note the following required changes:
-
Dependencies specified in
requirements.txt
must be specified inpyproject.toml
- either usepoetry add
or add them interactively viapoetry init
-
Python source and test files must be migrated into the folder structure described above, which aligns with the standard
src/
packaging layout. Assuming that the package name isspam_ham_eggs
,src/main/python/*
from the existing Habushu project must be moved intosrc/spam_ham_eggs
andsrc/test/python/*
from the existing Habushu project must be moved intotests
-
Previously, Habushu 1.x modules depended on each other via Maven
<dependency>
declarations. This approach is deprecated as Habushu 2.x+ expects that other Habushu modules are published to PyPI repositories and consumed as Python packages using Poetry's built-in dependency management capabilties. For Habushu module dependencies within the same Maven multi-module build hierarchy, consider using editable development installs:# pyproject.toml [tool.poetry.dependencies] my-package = {path = "../spam-eggs-ham-dependency", develop = true}
Once you have a valid Poetry project, add the following configurations to your pom.xml
to enable your Poetry project to be managed as a part of Habushu's custom Maven build lifecycle.
Set the <packaging>
type of your module's pom.xml
to habushu
:
<packaging>habushu</packaging>
Add the following plugin definition to your module's pom.xml
<build>
section:
<plugin>
<groupId>org.technologybrewery.habushu</groupId>
<artifactId>habushu-maven-plugin</artifactId>
<version>2.0.0</version>
<extensions>true</extensions>
</plugin>
If publishing packages to or consuming dependencies from a private PyPI repository that requires authentication, add your repository credentials to your Maven's settings.xml
file, usually located under your ~/.m2
folder. See the Configuration section below on how to
<server>
<!-- ID of the PyPI repository - this ID will be used to reference this repository in
the habushu-maven-plugin configuration -->
<id>private-pypi-repo</id>
<!-- Username of the account by which to access the PyPI repository -->
<username>pypi-repo-username</username>
<!-- Password of account by which to access the PyPI repository; should be encrypted as per Maven best practices -->
<password>{encrypted-pypi-repo-password}</password>
</server>
Habushu leverages behave to institute behavior-driven development (BDD) and quickly execute tests that implement Gerkin features.
Gherkin feature files should be added to tests/features
while Python step implementations should be added to tests/features/steps
. As is customary with BDD, once you add your feature, stubbed methods can be created for each step by running the build via mvn test
. Any step that is missing will be listed at the end of the build, allowing you to copy that snippet and then add in the implementation. For example:
[INFO] Failing scenarios:
[INFO] tests/features/example.feature:30 This is an unimplemented test for testing purposes
You can implement step definitions for undefined steps with these snippets:
@given(u'any SSL leveraged within the System')
def step_impl(context):
raise NotImplementedError(u'STEP: Given any SSL leveraged within the System')
"tagged-tests" is an example profile within habushu-mixology-consumer used to specify a tag(s) to test. The variable "tags" is used to specify which tags to test. To run multiple tests, comma separate them. To exclude a test, add "~" in front of the tag.
Ex: mvn clean test -Ptagged-tests -Dtags="one_tag"
In addition to creating a custom Maven lifecycle that automates the execution of a predictable Poetry-based workflow, Habushu exposes a run-command-in-virtual-env
plugin goal that provides developers with the ability to execute any Python command or script within the Poetry project's virtual environment through poetry run
during the desired build phase.
For example, developers may use this feature to bind a Habushu module's compile
phase to the appropriate Python command that generates gRPC/protobuf bindings as an automated part of the build following dependency installation:
<plugin>
<groupId>org.technologybrewery.habushu</groupId>
<artifactId>habushu-maven-plugin</artifactId>
<extensions>true</extensions>
<configuration>
...
</configuration>
<executions>
<execution>
<configuration>
<runCommandArgs>python -m grpc_tools.protoc -I=src
--python_out=src/habushu_mixology/generated src/person.proto</runCommandArgs>
</configuration>
<id>generate-protobuf-bindings</id>
<phase>compile</phase>
<goals>
<goal>run-command-in-virtual-env</goal>
</goals>
</execution>
</executions>
</plugin>
The containerize-dependencies
goal will collect a single habushu
dependency specified in the project's pom.xml
,
including all transitive habushu-packaged dependencies. After collecting the set of necessary dependencies, Habushu will
copy the project files for each dependency to a staging directory, while preserving the original structure of the
dependency modules to ensure that any path-based dependencies can be leveraged as-is. This directory can then be copied
onto a Docker container and used to create a virtual environment capable of running the target Habushu project.
The plugin will automatically inject logic for building and using this virtual environment into a pre-existing
Dockerfile by default. The dockerfile
configuration must be set to the target Dockerfile. To disable the Dockerfile
update altogether, set the updateDockerfile
configuration to false
. Because the virtual environment that is created
is dependent on the platform for which it is built, Habushu defaults to using python:3.11
to build the virtual
environment and python:3.11-slim
as the final image that packages/runs the virtual environment. This can be
customized with the dockerBuilderBase
, dockerFinalBase
, and dockerUser
configurations, but care must be taken to
ensure the builder image platform is sufficiently similar to the final image platform so that the virtual environment is
compatible.
<plugin>
<groupId>org.technologybrewery.habushu</groupId>
<artifactId>habushu-maven-plugin</artifactId>
<executions>
<execution>
<id>containerize-deps</id>
<goals>
<goal>containerize-dependencies</goal>
</goals>
</execution>
</executions>
<configuration>
<dockerfile>${project.basedir}/src/main/docker/Dockerfile</dockerfile>
</configuration>
</plugin>
To control the exact insertion location of the Dockerfile build logic, use the #HABUSHU_BUILDER_STAGE
and
#HABUSHU_FINAL_STAGE
comment tags in the Dockerfile
in the preferred location. The #HABUSHU_BUILDER_STAGE
tag will
be replaced with the builder stage logic to copy the generated dependencies files onto a Docker container and then create
the virtual environment by building the target Habushu project. The #HABUSHU_FINAL_STAGE
tag will be replaced with the
final stage logic to copy over the built virtual environment to the final Docker build stage.
#HABUSHU_BUILDER_STAGE
FROM redhat/ubi9-minimal:latest AS builder
RUN microdnf install -y python3.11
#HABUSHU_FINAL_STAGE
ENTRYPOINT ["/opt/venv/bin/python3.11", "-m", "pets.main", "--enable_docs_url", "True"]
The plugin will only examine dependencies that are of the type habushu
in the dependencies block of the pom.xml
file.
<dependencies>
<dependency>
<groupId>your-group-id</groupId>
<artifactId>your-artifact-id</artifactId>
<version>your-version</version>
<type>habushu</type>
</dependency>
</dependencies>
Any transitive monorepo dependencies should also use this convention to ensure they are captured by the plugin.
Habushu enables support for faster builds via the Maven Build Cache (only available in Maven 3.9+). This functionality is enabled through three mechanisms: Maven build directory customization, the Maven Reactor, and Maven Build Cache Configuration. All require manual action to enable.
Update Maven's build configuration to point to the dist
directory for output in the pom file for each Habushu module:
<build>
<directory>dist</directory>
...
</build>
The Reactor allows Maven to understand
the relative structure and local hierarchy of Habushu/non-Habushu modules in your build. This can be accomplished by
adding a standard Maven dependency to the modules that should be tied together. For instance, the dependency below is
used in Habushu's test modules to create a dependency such that habushu-mixology-consumer
depends on habushu-mixology
.
By adding this, any time a change occurs in habushu-mixology
, all dependent Habushu modules will be rebuilt automatically.
<!-- SNIPPET from habushu-mixology-consumer/pom.xml: -->
<dependency>
<!-- Follow standard Maven GAV (GroupId-ArtifactId-Version) referencing: -->
<groupId>org.technologybrewery.habushu</groupId>
<artifactId>habushu-mixology</artifactId>
<version>2.7.0</version>
<!-- type of habushu needed for habushu modules specifically: -->
<type>habushu</type>
</dependency>
You also need to add the following to your Maven Build Cache maven-build-cache-config.xml
file:
<cache>
<configuration>
...
<attachedOutputs>
<dirNames>
<!-- Critical to enable Maven to process the artifacts created by Habushu: -->
<dirName>../dist</dirName>
</dirNames>
</attachedOutputs>
...
</configuration>
<input>
<global>
<!-- The following glob values should be ADDED to your existing values: -->
<glob>
{*.xml,*.proto,*.properties,*.py,*.txt,*.toml,.env*,*.feature}
</glob>
<includes>
<include>src/</include>
<include>pyproject.toml</include>
</includes>
<excludes>
<exclude>poetry.lock</exclude>
</excludes>
</global>
</input>
...
</cache>
All Habushu configurations may be set either via the habushu-maven-plugin
's <configuration>
definition, Maven POM properties, or -D
on the line and follow a consistent naming pattern for the different configuration approaches. For setting configurations via POM properties or -D
on the command line, all configuration keys may be prepended with habushu.
. For example, pythonVersion
controls the version of Python utilized by Habushu and may be configured using the following approaches:
- Plugin
<configuration>
<plugin>
<groupId>org.technologybrewery.habushu</groupId>
<artifactId>habushu-maven-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<pythonVersion>3.10.4</pythonVersion>
</configuration>
</plugin>
-D
via command line
mvn clean install -Dhabushu.pythonVersion=3.10.4
- POM properties
<properties>
<habushu.pythonVersion>3.10.4</habushu.pythonVersion>
</properties>
NOTE: The above list's order reflects the precedence in which configurations will be applied. For example, configuration values that are specified in the plugin's <configuration>
definition will always take precedence, while system properties via the command line (-D
) will take precedence over <properties>
definitions.
The desired version of Python to use. Habushu delegates to pyenv
for managing versions of Python depending on the configuration usePyenv
.
Default: 3.11.4
If true, Habushu will delegate to pyenv
for managing and (if needed) installing the specified version of Python. If false, Habushu will look for the desired version of Python on the PATH
. If Python is not found or if the version does not match the configured pythonVersion
, the build will fail.
Default: true
Options that should be passed to the behave
command when executing tests. If this value is provided, then behaveExcludeManualTag is ignored.
behave
supports a number of command line options - developers may adjust the default test execution behavior to optimize productivity, such as selectively executing features associated with a specific in-flight tag (mvn clean test -Dhabushu.behaveOptions="--tags wip-feature"
).
Default: None
Allow stdout, stderr and logs to be printed to the console during behave
tests.
Note: If user wants more granular control over logging output, can set disableOutputCapture
to false
and use behaveOptions
:
mvn clean test -Dhabushu.disableOutputCapture=false -Dhabushu.behaveOptions="--no-logcapture --no-capture"
Default: true
Exclude any BDD scenario or feature file tagged with @manual
.
NOTE: If behaveOptions are provided, this property is ignored.
Default: true
Enables the ability to set environment variables when executing the behave tests. The environment variables should be defined in the pom.xml:
<plugin>
<groupId>org.technologybrewery.habushu</groupId>
<artifactId>habushu-maven-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<behaveTestEnvironmentVariables>
<ENV_VAR>VALUE</ENV_VAR>
</behaveTestEnvironmentVariables>
</configuration>
</plugin>
To pass environment variables in via the command line using the -D option, use a property placeholder in the pom.xml:
<plugin>
<groupId>org.technologybrewery.habushu</groupId>
<artifactId>habushu-maven-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<behaveTestEnvironmentVariables>
<ENV_VAR>${habushu.ENV_VAR}</ENV_VAR>
</behaveTestEnvironmentVariables>
</configuration>
</plugin>
Then you can specify the value from the cli: mvn clean install -Dhabushu.ENV_VAR=customValue
. A default value can be
set in the <properties>
tag of the pom.xml.
Default: None
Enables the use of the poetry-monorepo-dependency-plugin to rewrite
any local path dependencies (to other Poetry projects) as versioned packaged dependencies in generated wheel
/sdist
archives. If true
,
Habushu will replace invocations of Poetry's build
and publish
commands with the extensions of those commands exposed by the
poetry-monorepo-dependency-plugin
, which are build-rewrite-path-deps
and publish-rewrite-path-deps
, respectively.
To further ease working with monorepo dependencies, Habushu will specially handle how these dependencies are written to
intermediate requirements.txt
exports. In this case, the dependencies will be excluded from the export unless
rewriteLocalPathDepsInArchives
is set to true. This feature can be useful if installing the exported
requirements.txt
on another machine and/or Docker container.
Typically, this flag will only be 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.
Default: false
Specifies the <id>
of the <server>
element declared within the utilized Maven settings.xml
configuration that represents the PyPI repository
to which this project's archives will be published and/or used as a supplemental repository from which dependencies may be installed. This property is REQUIRED if publishing to or consuming dependencies from a private PyPI repository that requires authentication - it is expected that the relevant <server>
element provides the needed authentication details.
If this property is not specified, this property will default to pypi
and the execution of the deploy
lifecycle phase will publish this package to the official public PyPI repository. Downstream package publishing functionality will use the relevant settings.xml
<server>
declaration with <id>pypi</id>
as credentials for publishing the package to PyPI. If developers want to use PyPI's API tokens instead of username/password credentials, they may do so by manually executing the appropriate Poetry command (poetry config pypi-token.pypi my-token
) in an ad-hoc fashion prior to running deploy
.
This property will typically be specified as a command line option during the deploy
lifecycle phase. For example, given the following configuration in the utilized settings.xml
:
<server>
<id>private-pypi-repo</id>
<username>pypi-repo-username</username>
<password>{encrypted-pypi-repo-password}</password>
</server>
The following command may be utilized to publish the package to the specified private PyPI repository at https://private-pypi-repo-url/repository/pypi-repo/
:
$ mvn deploy -Dhabushu.pypiRepoId=private-pypi-repo -Dhabushu.pypiRepoUrl=https://private-pypi-repo-url/repository/pypi-repo/
Default: pypi
Specifies the URL of the private PyPI repository to which this project's archives will be published and/or used as a supplemental repository from which dependencies may be installed. This property is REQUIRED if publishing to or consuming dependencies from a private PyPI repository.
If the Habushu project depends on internal packages that may only be found on a private PyPI repository, developers should specify this property through the plugin's <configuration>
definition:
<plugin>
<groupId>org.technologybrewery.habushu</groupId>
<artifactId>habushu-maven-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<pypiRepoUrl>https://private-pypi-repo-url/repository/pypi-repo</pypiRepoUrl>
</configuration>
</plugin>
Default: None
Specifies the path to the simple index relative to the pypiRepoUrl. Certain private repository solutions use non-standard paths (ie: devpi uses +simple
).
Default: simple
Specifies the path to the upload index relative to the pypiRepoUrl. Certain private repository solutions use non-standard paths.
Default: None
Specifies whether Habushu should attempt to decrypt the remote server password provided in Maven's settings.xml
file. If false
, the password will be retrieved as-is and assumed to be unencrypted.
Warning: Storage of plain-text passwords is a security risk! This functionality is best used when the password is stored in a safe manner outside of Maven's native credential system, and is decrypted prior to execution (Jenkins credentials, for instance).
Default: true
Instructs deployment to use a development repository rather than a release repository. This is conceptually
similar to Maven's release vs. snapshot repositories, allowing the release repository to only have formal
releases with a separate repository for all 'dev' releases. Works in conjunction with the 'devRepositoryId'
Member and devRepositoryUrl
properties
Default: false
Specifies the number of times a push to the configured PyPI repository will be attempted before stopping (inclusive of the initial attempt). While this defaults to three and is fully configurable, it can be set to zero to never retry or set to any negative number for unlimited retries. Unlimited retries will follow a fibonacci backoff interval.
Default: 3
Specifies the <id>
of the <server>
element declared within the utilized Maven settings.xml
configuration that
represents the development PyPI repository to which this project's archives will be published and/or used as a
supplemental repository from which dependencies may be installed. This property is REQUIRED if publishing to or
consuming dependencies from a private PyPI repository that requires authentication - it is expected that the relevant
<server>
element provides the needed authentication details.
If this property is not specified, this property will default to dev-pypi
and the execution of the deploy
lifecycle phase will publish this package to the official public Test PyPI repository. Downstream package publishing
functionality will use the relevant settings.xml
<server>
declaration with <id>dev-pypi</id>
as credentials for
publishing the package to PyPI. If developers want to use PyPI's API tokens instead of username/password
credentials, they may do so by manually executing the appropriate Poetry command
(poetry config pypi-token.pypi my-token
) in an ad-hoc fashion prior to running deploy
.
This property will typically be specified as a command line option during the deploy
lifecycle phase. For example,
given the following configuration in the utilized settings.xml
:
<server>
<id>dev-pypi</id>
<username>pypi-repo-username</username>
<password>{encrypted-pypi-repo-password}</password>
</server>
The following command may be utilized to publish the package to the specified private PyPI repository at
https://private-pypi-repo-url/repository/pypi-repo/
:
$ mvn deploy -Dhabushu.pypiRepoId=dev-pypi -Dhabushu.pypiRepoUrl=https://private-pypi-repo-url/repository/pypi-repo/
Default: dev-pypi
Specifies the URL of the private development PyPI repository to which this project's archives will be published and/or used as a supplemental repository from which dependencies may be installed. This property is REQUIRED if publishing to or consuming dependencies from a private dev PyPI repository.
If the Habushu project depends on internal packages that may only be found on a private dev PyPI repository,
developers should specify this property through the plugin's <configuration>
definition:
<plugin>
<groupId>org.technologybrewery.habushu</groupId>
<artifactId>habushu-maven-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<devRepositoryUrl>https://private-pypi-repo-url/repository/pypi-repo</devRepositoryUrl>
</configuration>
</plugin>
Default: https://test.pypi.org
Specifies the path to the upload index relative to the devRepositoryUrl. Certain private repository solutions use
non-standard paths (ie: test.pypi.org
uses +legacy/
).
Default: legacy/
Specifies which Poetry dependency groups to include within the installation. Example usage:
mvn clean install -Dhabushu.withGroups=dev,test
or
<plugin>
<groupId>org.technologybrewery.habushu</groupId>
<artifactId>habushu-maven-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<withGroups>
<withGroup>dev</withGroup>
<withGroup>test</withGroup>
</withGroups>
</configuration>
</plugin>
Default: None
Specifies Poetry dependency groups to exclude within the installation. Example usage:
mvn clean install -Dhabushu.withoutGroups=dev,test
or
<plugin>
<groupId>org.technologybrewery.habushu</groupId>
<artifactId>habushu-maven-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<withoutGroups>
<withoutGroup>dev</withoutGroup>
<withoutGroup>test</withoutGroup>
</withoutGroups>
</configuration>
</plugin>
Default: None
A value of true
will result in Poetry installing packages with the --sync
parameter.
Default: false
Only applicable when executing the run-command-in-virtual-env
plugin goal
Whitespace-delimited command arguments that will be provided to poetry run
to execute. For example, the following property configuration will execute poetry run python -V
within the project's virtual environment during the validate
phase of the build:
<plugin>
<groupId>org.technologybrewery.habushu</groupId>
<artifactId>habushu-maven-plugin</artifactId>
<extensions>true</extensions>
<configuration>
...
</configuration>
<executions>
<execution>
<configuration>
<runCommandArgs>python -V</runCommandArgs>
</configuration>
<id>get-python-version</id>
<phase>validate</phase>
<goals>
<goal>run-command-in-virtual-env</goal>
</goals>
</execution>
</executions>
</plugin>
Default: None
Typically enabled when running CI, this configuration enables skipping the update of Poetry's lock file via poetry lock
. If poetry.lock
does not exist, the subsequent execution of poetry install
will create it regardless of this configuration. If poetry.lock
has a mismatch with its pyproject.toml
definition, the build will fail.
Default: false
Enables Poetry's virtualenvs.in-project
for this project. If configured with an existing virtual environment elsewhere,
the 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.
Default: true
Enables the explicit deletion of the virtual environment that is created/managed by Poetry.
Example usage: mvn clean -Dhabushu.deleteVirtualEnv
Default: false
Skips running tests. Using this property is NOT RECOMMENDED but may be convenient on occasion.
Example usage: mvn clean install -Dhabushu.skipTests=true
Default: false
Configures whether a private PyPi repository, if specified via pypiRepoUrl, is automatically added as a package source from which dependencies may be installed. This value is only utilized if a private PyPi repository is specified via pypiRepoUrl. Developers will typically not need to configure this property, but is made available to support the manual configuration of a custom repository in pyproject.toml
if needed.
Default: true
Enables or disables the generation of a requirements.txt file during the package phase.
Default: true
Specifies where the requirements.txt file will be generated to during the package phase.
Default: project-directory/dist
to be with the generated wheels.
Whether or not the requirements.txt file should include source repository urls.
Default: false
so will not add the --without-urls flag and thus include urls
Whether or not the requirements.txt file should include package hashes.
Default: false
so will not add the --without-urls flag and thus export with hashes
Skips the execution of the deploy
phase and does not publish the Poetry package to the configured PyPI repository. This configuration may be useful when individual Habushu modules within a larger multi-module project hierarchy should not be published to PyPI.
Default: false
DateTimeFormatter compliant pattern that configures the numeric portion of SNAPSHOT
Poetry package versions that are published to the configured PyPI repository. By default, the version of SNAPSHOT
published packages align with PEP-440 developmental releases and use a numeric component that corresponds to the number of seconds since the epoch. For example, if the POM version is 1.2.3-SNAPSHOT
, the package may be published by default as 1.2.3.dev1658238063
. If this property is specified, the numeric component will reflect the given date format pattern applied to the current build time. For example, if YYYYMMddHHmm
is provided, 1.2.3.dev202207191002
be published.
Default: Number of seconds since epoch
Specifies whether the version of the encapsulated Poetry package should be automatically managed and overridden where necessary by Habushu. If this property is true
, Habushu may override the pyproject.toml
defined version in the following build phases/mojos:
initialize
: Automatically sets the Poetry package version to the version specified in the POM. If the POM is aSNAPSHOT
, the Poetry package version will be set to the corresponding developmental release version without a numeric component (i.e. POM version of1.2.3-SNAPSHOT
will result in the Poetry package version being set to1.2.3.dev
). If the version is a release candidate (rc
),alpha
, orbeta
version in SemVer 2.0 format then it is translated to the equivalent PEP 440 format.compile
: Automatically set up anymanagedDependency
that is aSNAPSHOT
to instead include.*
..*
is used rather than.dev
so the latest dev version will resolve as.dev
is not a valid version. Same result as what happens in thevalidate
step above, but operating onmanagedDependency
entries instead. This is useful when you are working with multi-module builds where several versions are covered via managed dependencies. SeemanagedDependencies
section below for more context.deploy
: Automatically sets the version of published Poetry packages that areSNAPSHOT
modules to timestamped developmental release versions (i.e. POM version of1.2.3-SNAPSHOT
will result in the published Poetry package version to to1.2.3.dev1658238063
). After the package is published, the version of theSNAPSHOT
module is reverted to its previous value (i.e.1.2.3.dev
)
If this property is set to false
, none of the above automated version management operations will be performed.
CAVEAT: If there is a ^ and/or Poetry version < 1.5.0, the substitution will be -SNAPSHOT
to .dev
only for backwards
compatibility
Default: true
Folder in which Python source files are located - should align with Poetry's project structure conventions. Developers will typically not modify this property but is made available for customization to support unanticipated scenarios.
Default: ${project.basedir}/src
Folder in which Python test files are located - should align with Poetry's project structure conventions. Developers will typically not modify this property but is made available for customization to support unanticipated scenarios.
Default: ${project.basedir}/tests
The cache-wheels
goal has been deprecated
, please see the containerize-dependencies
goal instead.
Enables or Disables the copying of wheels into Poetry cache.
Default: false
The retrieve-wheels
goal has been deprecated
, please see the containerize-dependencies
goal instead.
Optional set of wheel dependencies to retrieve from poetry cache. This allows previously cached external
wheel dependencies to be copied into a given target directory if it exists in poetry cache. This logic
depends on wheels to have first been cached by cacheWheels
habushu-maven-plugin configuration and executes
during the VALIDATE maven phase. Warnings will be logged if the specified wheel isn't found.
<plugin>
<groupId>org.technologybrewery.habushu</groupId>
<artifactId>habushu-maven-plugin</artifactId>
...
<configuration>
<wheelDependencies>
<wheelDependency>
<artifactId>foundation-core-python</artifactId>
<targetDirectory>${project.build.directory}</targetDirectory>
</wheelDependency>
</wheelDependencies>
...
</configuration>
</plugin>
Optional set of dependencies to manage across modules extending a parent pom. This allows packages to be managed to a
specific version, which is often useful to ensure that information assurance patches, common versions, etc. are enforced
across a series of modules. Can be used with the next several variables to control automatic update, logging, or failing
the build when mismatches are found between the managed dependency operator/version and what is currently specified.
Looks at dependencies in [tool.poetry.dependencies]
, [tool.poetry.group.dev.dependencies]
, and any
[tool.poetry.group.<subgroup>]
of your pyproject.toml
.
<plugin>
<groupId>org.technologybrewery.habushu</groupId>
<artifactId>habushu-maven-plugin</artifactId>
...
<configuration>
<managedDependencies>
<packageDefinition>
<packageName>black</packageName>
<operatorAndVersion>^23.3.0</operatorAndVersion>
<!--
Active defaults to true, but can be used to overriden in child pom.xml files to remove or add
managed dependencies at each level:
-->
<active>true</active>
</packageDefinition>
<packageDefinition>
<packageName>some-local-module</packageName>
<!--
Will follow rules in overridePackageVersion:compile to
update to a final version of 1.2.3.* to resolve 1.2.3.dev versions
UNLESS there is a ^ or Poetry version < 1.5.0
-->
<operatorAndVersion>1.2.3-SNAPSHOT</operatorAndVersion>
</packageDefinition>
</managedDependencies>
...
</configuration>
</plugin>
Determines if managed dependency mismatches are automatically updated when encountered.
Default: true
Determines if the build should be failed when managed dependency mismatches are found.
Default: false
Location of the artifact that will be published for this module. Maven wants to install an artifact with the pom file. Habushu is really just interested in publishing the pom file for Maven Reactor use, but pushing an artifact makes this process more smooth. This parameter can be updated to customize the artifact used. A placeholder file is explicitly used as the default to prevent confusion if we published a real artifact (e.g., wheel file) that would never be used in normal circumstances.
Default: ${project.basedir}/target/habushu.placeholder.txt
Controls where the clean plugin will determine where Poetry projects are located - should always be the basedir of the encapsulating Maven project.
Default: ${project.basedir}
Controls where the clean plugin will delete dist artifacts.
Default: ${project.basedir}/dist
Controls where the clean plugin will delete target artifacts.
Default: ${project.basedir}/target
Controls whether locally pathed dependencies should be excluded from the requirements.txt export file.
Default: true
Controls whether the Behave testing framework should export a cucumber.json and corresponding Cucumber-style HTML report instead of the default test output format.
Default: true
Controls whether skipped tests should be completely omitted from test reports rather than showing up as a skip / failure. This mimics the default behavior of Cucumber and will have no effect if outputCucumberStyleTestReports is not set to true
Default: true
Controls if linting is enabled for the source module.
Default: true
Controls the disabled checkers for linting in the source module. By default, checks for Convention (C), Refactor (R), and Warning (W) are disabled.
Default: C,R,W
Controls the enabled checkers for linting in the source module.
Controls whether the build will continue if lint identifies code that violate checkers in the source module.
Default: true
Controls if linting is enabled for the test module.
Default: true
Controls the disabled checkers for linting in the test module. By default, checks for Convention (C), Refactor (R), Warning (W), and function-redefined error (E0102) are disabled.
Default: C,R,W,E0102
Controls the enabled checkers for linting in the test module.
Controls whether the build will continue if lint identifies code that violate checkers in the test module.
Default: true
Controls the location of where containerization files will be copied to as part of the containerize-dependencies
goal.
Default: ${project.build.directory}/containerize-support
Enables the automatic injection of containerization logic to the dockerfile for the containerize-dependencies
goal.
Default: true
The Dockerfile to update with containerization logic during the containerize-dependencies
goal. This must be set if the updateDockerfile
is set to true
.
Default: None
Habushu applies a custom Maven lifecycle that binds Poetry-based DevSecOps workflow commands to the following phases:
Ensures that necessary required tools are installed, specifically Pyenv, Poetry, and any needed Poetry plugins. Additionally, if usePyenv is enabled, Pyenv will install/configure the specified version of Python to be used in all downstream Python/Poetry operations.
Configures the build state needed by Habushu, ensures that the current project is a Poetry project and initializes and aligns POM and pyproject.toml versions. If configured via overridePackageVersion, automatically syncs the Poetry project version with the appropriate version that is derived from the Habushu module's POM.
Installs dependencies defined in the project's pyproject.toml
configuration, specifically by running poetry lock
followed by poetry install
. If a private PyPi repository is defined via pypiRepoUrl, it will be automatically added to the module's pyproject.toml
configuration as a supplemental source of dependencies, if it is not already configured in the pyproject.toml
Leverages the black formatter package to format both source and test Python directories via poetry run
.
Uses behave to execute BDD scenarios that are defined in tests/features
. By default, as per behaveExcludeManualTag, features/scenarios tagged with @manual
are skipped.
Builds the sdist
and wheel
archives of this project using poetry build
. It also generates a requirements.txt
file which is useful when installing the package in a Docker container where you may want to install the dependencies in a specific Docker layer to optimize caching. If the containerize-dependencies
execution is enabled, supporting monorepo dependency source files will be staged.
Publishes the pom.xml
for the module into your local Maven Repository (~/.m2/repository
).
Publishes the generated package archives to the specified PyPI repository (defined via pypiRepoId and pypiRepoUrl).
If the current Habushu module is a SNAPSHOT
version, temporarily set the version of the package to the appropriate
developmental version, publish it to the specified PyPI repository, and then reset the version to the original value.
Also publishes the pom.xml
for the module into the configured Maven Repository.
Deletes the folder in which archives generated by the package phase are placed (dist
) and if deleteVirtualEnv is set to true
, deletes the project's virtual environment.
Optionally, Habushu supports partial builds via the Maven Reactor. This allows functionality such as -rf
(resume from)
by publishing Habushu POM files as part of the standard Maven install
and deploy
lifecylces. No artifacts (e.g.,
wheel files) are managed by Maven - those are solely handled via normal PyPI lookups. To leverage this functionality,
add in Habushu dependencies in your project's pom.xml
. You can find an example in habushu-mixology-consumer/pom.xml
where it references a dependency to habushu-mixology
for Maven Reactor benefits.
Pyenv is utilized to install and use the specified version of Python, while Poetry (which uses the Pyenv-managed version of Python) is utilized for all underlying build commands. Both Pyenv and Poetry MUST be installed. If you encounter an error indicating that either tool is not installed or on the PATH
, you will be prompted with installation guidance.
If you encounter the following error, please see the "Building Habushu" section for details on how to use the bootstrap
profile to appropriately build the habushu-maven-plugin
and its associated custom Maven lifecycle. This error will typically only occur when attempting to use an in-flight SNAPSHOT
version of Habushu that is not yet published to the Maven Central repository.
[WARNING] The POM for org.technologybrewery.habushu:habushu-maven-plugin:jar:0.0.1-SNAPSHOT is missing, no dependency information available
[ERROR] [ERROR] Some problems were encountered while processing the POMs:
[ERROR] Unresolveable build extension: Plugin org.technologybrewery.habushu:habushu-maven-plugin:0.0.1-SNAPSHOT or one of its dependencies could not be resolved: Could not find artifact org.technologybrewery.habushu:habushu-maven-plugin:jar:0.0.1-SNAPSHOT @
[ERROR] Unknown packaging: habushu @ line 15, column 13
If you are working on Habushu, please be aware of some nuances in working with a plugin that defines a custom Maven build lifecycle and packaging. habushu-mixology
and habushu-mixology-consumer
are utilized to immediately test the habushu-maven-plugin
and associated habushu
lifecycle. If the habushu-maven-plugin
has not been previously built or there are unbuilt changes to the habusu
lifecycle, developers must manually build the habushu-maven-plugin
and then execute another, separate build of habushu-mixology
(and any other habushu
module) to use the updated habushu-maven-plugin
and habushu
lifecycle. Developers are not able to build updates to the habushu
lifecycle and test their application in habushu-mixology
within the same build. That said, if developers do not update the habushu
lifecycle and simply make updates to existing Mojo
s defined in the habushu-maven-plugin
, a single build may be used to build habushu-maven-plugin
and apply the updates to habushu-mixology
. To assist, there are two profiles available in the build:
mvn clean install -Pbootstrap
: Builds thehabushu-maven-plugin
such that the customhabushu
lifecycle may be utilized within subsequent builds.- NOTE: If updates are made to the
habushu
lifecycle (i.e. updates to thehabushu
lifecycle mapping configuration made inhabushu-maven-plugin/src/main/resources/META-INF/plexus/components.xml
), developers MUST changes require two builds to test - one to build the lifecycle, then a second to use that updated lifecycle. Code changes toMojo
classes within the existinghabushu
lifecycle work via normal builds without the need for a second pass. mvn clean install -Pdefault
: (ACTIVE BY DEFAULT --Pdefault
does not need to be specified) builds all modules. Developers may use this profile to build and apply changes to existinghabushu-maven-plugin
Mojo
classes