Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update the Poetry Core version in the Habushu project to the required version #65

Merged
merged 1 commit into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;


/**
d-ryan-ashcraft marked this conversation as resolved.
Show resolved Hide resolved
* 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);
logger.debug("Found build-system's Poetry-core version : {} less than the required version for Habushu. It will be updated to the required version of {}.", poetrycoreVer, PoetryUtil.POETRY_CORE_VERSION_REQUIREMENT);
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
Loading