Skip to content

Commit

Permalink
Added functinality to upgrade the poetry-core version required by the…
Browse files Browse the repository at this point in the history
… Habushu
  • Loading branch information
Satish Manda committed Dec 5, 2023
1 parent e70be20 commit 248a384
Show file tree
Hide file tree
Showing 11 changed files with 460 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import org.technologybrewery.habushu.exec.PoetryCommandHelper;
import org.technologybrewery.habushu.exec.PyenvCommandHelper;
import org.technologybrewery.habushu.exec.PythonVersionHelper;
import org.technologybrewery.habushu.util.PoetryUtil;

import java.io.File;
import java.util.ArrayList;
Expand All @@ -34,12 +35,6 @@ class PyenvAndPoetrySetup {
*/
static final String PYTHON_DEFAULT_VERSION_REQUIREMENT = "3.11.4";

/**
* Specifies the semver compliant requirement for the version of Poetry that
* must be installed and available for Habushu to use.
*/
protected static final String POETRY_VERSION_REQUIREMENT = "^1.5.0";

/**
* The desired version of Python to use.
*/
Expand Down Expand Up @@ -129,10 +124,10 @@ public void execute() throws MojoExecutionException, MojoFailureException {
} else {

Semver poetryVersionSemver = new Semver(poetryInstallStatusAndVersion.getRight(), SemverType.NPM);
if (!poetryVersionSemver.satisfies(POETRY_VERSION_REQUIREMENT)) {
if (!poetryVersionSemver.satisfies(PoetryUtil.POETRY_VERSION_REQUIREMENT)) {
missingRequiredToolMsgs.add(String.format(
"Poetry version %s was installed - Habushu requires that installed version of Poetry satisfies %s. Please update Poetry by executing 'poetry self update' or visit https://python-poetry.org/docs/#installation for more information",
poetryInstallStatusAndVersion.getRight(), POETRY_VERSION_REQUIREMENT));
poetryInstallStatusAndVersion.getRight(), PoetryUtil.POETRY_VERSION_REQUIREMENT));
} else {
log.info("Found Poetry " + poetryInstallStatusAndVersion.getRight());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package org.technologybrewery.habushu.migration;

import com.electronwill.nightconfig.core.CommentedConfig;
import com.electronwill.nightconfig.core.Config;
import com.electronwill.nightconfig.core.file.FileConfig;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.technologybrewery.baton.AbstractMigration;
import org.technologybrewery.baton.BatonException;
import org.technologybrewery.habushu.HabushuException;
import org.technologybrewery.habushu.util.TomlReplacementTuple;
import org.technologybrewery.habushu.util.TomlUtils;
import org.technologybrewery.habushu.util.PoetryUtil;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;


/**
* Automatically migrates Poetry-core version in the [build-system] group to the
* version where the major version and minor version are inline with the
* Poetry-core version required by Habushu defined by the POETRY_CORE_VERSION_REQUIREMENT in the
* PoetryUtil. As noted in the project's README.md, this prevents these dependencies from causing issues
* when Poetry projects are exported in development releases.
*/
public class CustomPoetrycoreVersionMigration extends AbstractMigration {

public static final Logger logger = LoggerFactory.getLogger(CustomPoetrycoreVersionMigration.class);

protected Map<String, TomlReplacementTuple> replacements = new HashMap<>();

private boolean isMajorVersionUpdateRequired;
private boolean isMinorVersionUpdateRequired;

@Override
protected boolean shouldExecuteOnFile(File file) {
try (FileConfig tomlFileConfig = FileConfig.of(file)) {
tomlFileConfig.load();
Optional<Config> toolBuildSystem = tomlFileConfig.getOptional(TomlUtils.BUILD_SYSTEM);
if (toolBuildSystem.isPresent()) {
Config buildSystem = toolBuildSystem.get();
Map<String, Object> dependencyMap = buildSystem.valueMap();
for (Map.Entry<String, Object> dependency : dependencyMap.entrySet()) {
isMajorVersionUpdateRequired = false;
isMinorVersionUpdateRequired = false;
// check if we need to upgrade the poetry-core version.
if(isPoetrycoreUpgradeRequired(dependency)) {
return true;
}
}
}
}
return false;
}

@Override
protected boolean performMigration(File pyProjectTomlFile) {
String fileContent = StringUtils.EMPTY;
try (BufferedReader reader = new BufferedReader(new FileReader(pyProjectTomlFile))) {
String line = reader.readLine();
while (line != null) {
boolean isEmptyLine = line.isBlank();

if (line.contains(StringUtils.SPACE) && line.contains(TomlUtils.EQUALS)) {
String key = line.substring(0, line.indexOf(StringUtils.SPACE));
if (key == null) {
key = line.substring(0, line.indexOf(TomlUtils.EQUALS));
}

if (key != null) {
key = key.strip();

TomlReplacementTuple matchedTuple = replacements.get(key);
if ((matchedTuple != null) && (line.contains(TomlUtils.POETRY_CORE)) && (line.contains(TomlUtils.EQUALS))) {
// update the poetry-core major version upgrade if required.
StringBuilder stringBuilder = new StringBuilder(line);
if(isMajorVersionUpdateRequired){
int majorIndex = line.lastIndexOf(TomlUtils.EQUALS) + 1;
int endMajorIndex = line.indexOf(TomlUtils.DOT);
String poetryCoreReqMajorVer = TomlUtils.getMajorReqVersion(PoetryUtil.POETRY_CORE_VERSION_REQUIREMENT);
stringBuilder = stringBuilder.replace(majorIndex, endMajorIndex, poetryCoreReqMajorVer);
line = stringBuilder.toString();
logger.debug("Updating the Poetry-core major version to {}.", poetryCoreReqMajorVer);
}

// update the poetry-core minor version if required.
if (isMinorVersionUpdateRequired) {
int minorVersionIndex = line.indexOf(TomlUtils.DOT) + 1;
int endMinorVersionIndex = line.lastIndexOf(TomlUtils.DOT);
String poetryCoreReqMinorVersion = TomlUtils.getMinorReqVersion(PoetryUtil.POETRY_CORE_VERSION_REQUIREMENT);
stringBuilder = stringBuilder.replace(minorVersionIndex, endMinorVersionIndex, poetryCoreReqMinorVersion);
line = stringBuilder.toString();
logger.debug("Updating the Poetry-core minor version to {}.", poetryCoreReqMinorVersion);
}
logger.info("Updated the Poetry-core version to {} required by Habushu.", PoetryUtil.POETRY_CORE_VERSION_REQUIREMENT);
}
}
}
fileContent += line + "\n";
line = reader.readLine();
}

} catch (IOException e) {
throw new HabushuException("Problem reading pyproject.toml while updating the build-sytem's Poetry-core version!", e);
}

try {
TomlUtils.writeTomlFile(pyProjectTomlFile, fileContent);
} catch (IOException e) {
throw new BatonException("Problem while writing dependencies to TomlFile while updating the build-system's Poetry-core version!", e);
}
return true;
}

private boolean isPoetrycoreUpgradeRequired(Map.Entry<String, Object> dependency) {
replacements.clear();
String packageName = dependency.getKey();

if (packageName.equals(TomlUtils.REQUIRES)) {
String dependencyVal = dependency.getValue().toString();
if (dependencyVal.contains(TomlUtils.POETRY_CORE) && dependencyVal.contains(TomlUtils.EQUALS)) {
String poetrycoreVer = dependencyVal.substring(TomlUtils.getIndexOfFirstDigit(dependencyVal), TomlUtils.getIndexOfLastDigit(dependencyVal));
int endMajorVerIndex = poetrycoreVer.indexOf(TomlUtils.DOT);
int poetryCoreMajorVer = Integer.parseInt(poetrycoreVer.substring(0, endMajorVerIndex));
int poetryCoreReqMajorVersion = Integer.parseInt(TomlUtils.getMajorReqVersion(PoetryUtil.POETRY_CORE_VERSION_REQUIREMENT));

// if the major version is less than required major version then return true.
if(poetryCoreMajorVer < poetryCoreReqMajorVersion) {
isMajorVersionUpdateRequired = true;
isMinorVersionUpdateRequired = true;
}

// Now check for the minor version. If the major version is equal to the required
// major version but if the minor version is less than the required minor version
// then upgrade for minor version is needed.
int minorVerIndex = poetrycoreVer.indexOf(TomlUtils.DOT) + 1;
int endMinorVerIndex = poetrycoreVer.lastIndexOf(TomlUtils.DOT);
int poetryCoreMinorVer = Integer.parseInt(poetrycoreVer.substring(minorVerIndex, endMinorVerIndex));
int poetryCoreReqMinorVersion = Integer.parseInt(TomlUtils.getMinorReqVersion(PoetryUtil.POETRY_CORE_VERSION_REQUIREMENT));

if ((poetryCoreMajorVer == poetryCoreReqMajorVersion && poetryCoreMinorVer < poetryCoreReqMinorVersion)) {
isMinorVersionUpdateRequired = true;
}

// Return true if major or minor version upgarde is required.
if(isMajorVersionUpdateRequired || isMinorVersionUpdateRequired) {
TomlReplacementTuple replacementTuple = new TomlReplacementTuple(packageName, poetrycoreVer, "poetry-core>=1.6.0");
replacements.put(packageName, replacementTuple);

This comment has been minimized.

Copy link
@d-ryan-ashcraft

d-ryan-ashcraft Dec 5, 2023

Contributor

S: one more log message I happened to notice with the "poetry-core version dependency" langauge we update above. Would be good to clarify here too.

logger.debug("Found Poetry-core dependency with version : {} which is less than the required version of 1.6.0 or higher for Habushu. Poetry-core version dependency will be updated to 1.6.0.", poetrycoreVer);
return true;
}
}
}
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.technologybrewery.habushu.util;

/**
* Common utility methods for handling TOML Poetry.
*/
public final class PoetryUtil {
/**
* Specifies the semver compliant requirement for the version of Poetry that
* must be installed and available for Habushu to use.
*/
public static final String POETRY_VERSION_REQUIREMENT = "^1.5.0";

/**
* Specifies the semver compliant requirement for the version of Poetry-core that
* must be installed and available for Habushu to use.
*/
public static final String POETRY_CORE_VERSION_REQUIREMENT = "^1.6.0";
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
import java.io.Writer;
import java.util.List;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* Common utility methods for handling TOML files.
*/
Expand All @@ -23,6 +26,12 @@ public final class TomlUtils {
public static final String DEVELOP = "develop";
public static final String EXTRAS = "extras";

public static final String BUILD_SYSTEM = "build-system";
public static final String REQUIRES = "requires";
public static final String POETRY_CORE ="poetry-core";
public static final String DOT = ".";


protected TomlUtils() {
// prevent instantiation of all static class
}
Expand Down Expand Up @@ -107,4 +116,58 @@ private static void addCommaBetweenValues(int valuesRemaining, StringBuilder sb)
}
}

/**
* Finds and returns the index of the first digit in a given string.
*
* @param input string
* @return index of the first digit. If not found then it will return -1.
*/
public static int getIndexOfFirstDigit(String input) {
Pattern pattern = Pattern.compile("\\d");
Matcher matcher = pattern.matcher(input);
if(matcher.find()){
return matcher.start();
}
return -1;
}

/**
* Finds and returns the index of the last digit in a given string.
*
* @param input string
* @return index of the last digit. If not found then it will return -1.
*/
public static int getIndexOfLastDigit(String input) {
Pattern pattern = Pattern.compile("\\d");
Matcher matcher = pattern.matcher(input);
int lastDigitIndex = -1;
while(matcher.find()){
lastDigitIndex = matcher.end();
}
return lastDigitIndex;
}

/**
* Finds and returns the minor version in a given version string.
*
* @param input version string
* @return the minor version.
*/
public static String getMinorReqVersion(String version) {
int minorVerReqIndex = version.indexOf(TomlUtils.DOT) + 1;
int endMinorVerReqIndex = version.lastIndexOf(TomlUtils.DOT);
return version.substring(minorVerReqIndex, endMinorVerReqIndex);
}

/**
* Finds and returns the major version in a given version string.
*
* @param input version string
* @return the major version.
*/
public static String getMajorReqVersion(String version) {
int majorVerReqIndex = getIndexOfFirstDigit(version);
int endMajorVerReqIndex = version.indexOf(TomlUtils.DOT);
return version.substring(majorVerReqIndex, endMajorVerReqIndex);
}
}
9 changes: 9 additions & 0 deletions habushu-maven-plugin/src/main/resources/migrations.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,14 @@
"includes": ["pyproject.toml"]
}
]
},
{
"name": "custom-poetry-core-version-migration",
"implementation": "org.technologybrewery.habushu.migration.CustomPoetrycoreVersionMigration",
"fileSets": [
{
"includes": ["pyproject.toml"]
}
]
}
]
Loading

0 comments on commit 248a384

Please sign in to comment.