Skip to content

Commit

Permalink
#515: Increment version
Browse files Browse the repository at this point in the history
  • Loading branch information
kaklakariada committed Jan 29, 2024
1 parent 6029d61 commit 92f7943
Show file tree
Hide file tree
Showing 19 changed files with 616 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public class ProjectKeeperLauncher {
private static final Logger LOGGER = Logger.getLogger(ProjectKeeperLauncher.class.getName());
private static final String GOAL_VERIFY = "verify";
private static final String GOAL_FIX = "fix";
private static final String GOAL_UPGRADE_DEPENDENCIES = "update-dependencies";

private final Path currentWorkingDir;

Expand Down Expand Up @@ -60,7 +61,21 @@ void start(final String[] args) {

private void runProjectKeeper(final String goal) {
final ProjectKeeper projectKeeper = createProjectKeeper();
final boolean success = goal.equals(GOAL_FIX) ? projectKeeper.fix() : projectKeeper.verify();
final boolean success;
switch (goal) {
case GOAL_FIX:
success = projectKeeper.fix();
break;
case GOAL_VERIFY:
success = projectKeeper.verify();
break;
case GOAL_UPGRADE_DEPENDENCIES:
success = projectKeeper.updateDependencies();
break;
default:
success = false;
break;
}
if (!success) {
throw new IllegalStateException(
ExaError.messageBuilder("E-PK-CLI-1").message("Failed to run project keeper {{goal}}", goal)
Expand All @@ -73,10 +88,12 @@ private ProjectKeeper createProjectKeeper() {
}

private void verifyCommandLineArguments(final String[] args) {
if ((args == null) || (args.length != 1) || !(GOAL_FIX.equals(args[0]) || GOAL_VERIFY.equals(args[0]))) {
if ((args == null) || (args.length != 1) || !(GOAL_FIX.equals(args[0]) || GOAL_VERIFY.equals(args[0])
|| GOAL_UPGRADE_DEPENDENCIES.equals(args[0]))) {
throw new IllegalArgumentException(ExaError.messageBuilder("E-PK-CLI-2")
.message("Got no or invalid command line argument {{arguments}}.", Arrays.toString(args))
.mitigation("Please only specify arguments '" + GOAL_VERIFY + "' or '" + GOAL_FIX + "'.")
.mitigation("Please only specify arguments '" + GOAL_VERIFY + "', '" + GOAL_FIX + "' or '"
+ GOAL_UPGRADE_DEPENDENCIES + "'.")
.toString());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import com.exasol.projectkeeper.ProjectKeeper;

/**
* Entry point for the fix goal.
* Entry point for the {@code fix} goal.
* <p>
* Run using {@code mvn project-keeper:fix}
* </p>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.exasol.projectkeeper.plugin;

import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;

import com.exasol.projectkeeper.ProjectKeeper;

/**
* Entry point for the {@code update-dependencies} goal.
* <p>
* Run using {@code mvn project-keeper:update-dependencies}
* </p>
*/
@Mojo(name = "update-dependencies")
public class ProjectKeeperUpdateDependenciesMojo extends AbstractProjectKeeperMojo {

@Override
protected void runProjectKeeper(final ProjectKeeper projectKeeper) throws MojoFailureException {
final boolean success = projectKeeper.updateDependencies();
if (!success) {
throw new MojoFailureException(
"project-keeper:update-dependencies failed. See log messages above for details");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import com.exasol.projectkeeper.ProjectKeeper;

/**
* Entry point for the verify goal.
* Entry point for the {code verify} goal.
* <p>
* Run using {@code mvn project-keeper:verify}
* </p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,22 @@

import static com.exasol.projectkeeper.plugin.TestEnvBuilder.CURRENT_VERSION;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.io.FileMatchers.anExistingFile;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.io.IOException;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDate;
import java.util.List;

import org.apache.maven.it.VerificationException;
import org.apache.maven.it.Verifier;
import org.apache.maven.model.Model;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.junit.jupiter.api.*;
Expand Down Expand Up @@ -72,8 +77,52 @@ void testJacocoAgentIsExtracted() throws VerificationException, IOException {
anExistingFile());
}

@Test
void testUpgradeDependencies() throws VerificationException, IOException {
final PrintStream out = System.out;
Files.writeString(this.projectDir.resolve(".project-keeper.yml"), //
"sources:\n" + //
" - type: maven\n" + //
" path: pom.xml\n");
final Verifier verifier = getVerifier();
verifier.executeGoal("project-keeper:fix");
assertThat("original version", readPom().getVersion(), equalTo("0.1.0"));

updateReleaseDate("0.1.0", "2023-01-01");
verifier.verify(true);

verifier.executeGoal("project-keeper:update-dependencies");
verifier.verify(true);

final List<String> lines = verifier.loadFile(verifier.getBasedir(), verifier.getLogFileName(), false);
out.println("Got " + lines.size() + " lines:");
lines.forEach(out::println);
assertThat("incremented version", readPom().getVersion(), equalTo("0.1.1"));
}

private void updateReleaseDate(final String changeLogVersion, final String newReleaseDate) {
final Path changelogPath = projectDir.resolve("doc/changes/changes_" + changeLogVersion + ".md");
try {
final String content = Files.readString(changelogPath);
final String updatedContent = content.replace(LocalDate.now().getYear() + "-??-??", newReleaseDate);
assertThat(updatedContent, not(equalTo(content)));
Files.writeString(changelogPath, updatedContent);
} catch (final IOException exception) {
throw new UncheckedIOException("Error updating release date in " + changelogPath, exception);
}
}

private Model readPom() {
final Path path = projectDir.resolve("pom.xml");
try {
return new MavenXpp3Reader().read(Files.newBufferedReader(path));
} catch (IOException | XmlPullParserException exception) {
throw new IllegalStateException("failed to parse " + path + ": " + exception.getMessage(), exception);
}
}

@ParameterizedTest
@ValueSource(strings = { "verify", "fix" })
@ValueSource(strings = { "verify", "fix", "update-dependencies" })
void testSkip(final String phase) throws IOException, VerificationException {
Files.writeString(this.projectDir.resolve(".project-keeper.yml"), //
"sources:\n" + //
Expand Down
2 changes: 1 addition & 1 deletion project-keeper/error_code_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ error-tags:
PK-CORE:
packages:
- com.exasol.projectkeeper
highest-index: 170
highest-index: 174
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.exasol.errorreporting.ExaError;
import com.exasol.projectkeeper.ValidationPhase.Provision;
import com.exasol.projectkeeper.config.ProjectKeeperConfigReader;
import com.exasol.projectkeeper.dependencyupdate.DependencyUpdater;
import com.exasol.projectkeeper.shared.config.*;
import com.exasol.projectkeeper.sources.AnalyzedSource;
import com.exasol.projectkeeper.sources.SourceAnalyzer;
Expand Down Expand Up @@ -280,4 +281,28 @@ private interface PhaseResultHandler {
*/
boolean handlePhaseResult(final List<ValidationFinding> findings);
}

/**
* Verify the project and return validation provisions.
*
* @return Validation provisions if the validation succeeded
* @throws IllegalStateException if validation fails
*/
private Provision getValidationProvision() {
Provision provision = null;
for (final Function<Provision, ValidationPhase> phaseSupplier : getValidationPhases()) {
final ValidationPhase phase = phaseSupplier.apply(provision);
provision = phase.provision();
final List<ValidationFinding> findings = runValidation(phase.validators());
if (!handleVerifyFindings(findings)) {
throw new IllegalStateException(ExaError.messageBuilder("").message("").toString());
}
}
return provision;
}

public boolean updateDependencies() {
final Provision provision = getValidationProvision();
return DependencyUpdater.create(logger, projectDir, provision.projectVersion()).updateDependencies();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.exasol.projectkeeper.dependencyupdate;

import java.nio.file.Path;

import com.exasol.projectkeeper.Logger;

/**
* This class runs the dependency update process.
*/
public class DependencyUpdater {

private final Logger logger;
private final ProjectVersionIncrementor projectVersionIncrementor;

DependencyUpdater(final Logger logger, final ProjectVersionIncrementor projectVersionIncrementor) {
this.logger = logger;
this.projectVersionIncrementor = projectVersionIncrementor;

}

public static DependencyUpdater create(final Logger logger, final Path projectDir,
final String currentProjectVersion) {
return new DependencyUpdater(logger, new ProjectVersionIncrementor(logger, projectDir, currentProjectVersion));
}

/**
* Runs the dependency update process. This includes the following steps:
* <ol>
* <li>Increment project patch version if necessary</li>
* <li>Update all dependencies to their latest versions</li>
* <li>Run project-keeper fix</li>
* <li>If available: add information about fixed vulnerabilities to changelog</li>
* </ol>
*
* @return {@code true} if the process succeeded.
*/
public boolean updateDependencies() {
incrementProjectVersion();
updateDependencyVersions();
runProjectKeeperFix();
updateChangelog();
return true;
}

private void incrementProjectVersion() {
if (projectVersionIncrementor.isCurrentVersionReleased()) {
logger.info("Current version was already released: increment version");
projectVersionIncrementor.incrementProjectVersion();
} else {
logger.info("Current version was not yet released: no need to increment");
}
}

private void updateDependencyVersions() {
// TODO Auto-generated method stub
}

private void runProjectKeeperFix() {
// TODO Auto-generated method stub
}

private void updateChangelog() {
// TODO Auto-generated method stub
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package com.exasol.projectkeeper.dependencyupdate;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.*;
import java.util.Objects;
import java.util.Optional;

import org.apache.maven.model.Model;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;

import com.exasol.errorreporting.ExaError;
import com.exasol.projectkeeper.Logger;
import com.exasol.projectkeeper.validators.changesfile.ChangesFile;
import com.exasol.projectkeeper.validators.changesfile.ChangesFileIO;
import com.vdurmont.semver4j.Semver;

class ProjectVersionIncrementor {
private static final ZoneId UTC_ZONE = ZoneId.of("UTC");
private final String currentProjectVersion;
private final ChangesFileIO changesFileIO;
private final Clock clock;
private final Path projectDir;
private final Logger logger;

ProjectVersionIncrementor(final Logger logger, final Path projectDir, final String currentProjectVersion) {
this(logger, projectDir, currentProjectVersion, new ChangesFileIO(), Clock.systemUTC());
}

ProjectVersionIncrementor(final Logger logger, final Path projectDir, final String currentProjectVersion,
final ChangesFileIO changesFileIO, final Clock clock) {
this.logger = logger;
this.projectDir = projectDir;
this.changesFileIO = changesFileIO;
this.clock = clock;
this.currentProjectVersion = Objects.requireNonNull(currentProjectVersion, "currentProjectVersion");
}

/**
* Check if the current version was released, i.e. has a changelog release date in the past or today.
*
* @return {@code true} if the current version was released (i.e. has a release date) or not (i.e. has no release
* date or a future date)
*/
boolean isCurrentVersionReleased() {
final Path changesFilePath = projectDir.resolve(ChangesFile.getPathForVersion(currentProjectVersion));
final ChangesFile changesFile = changesFileIO.read(changesFilePath);
final Optional<LocalDate> releaseDate = changesFile.getParsedReleaseDate();
if (releaseDate.isEmpty()) {
logger.info("Found invalid date '" + changesFile.getReleaseDate() + "' in changelog " + changesFilePath
+ ": version " + currentProjectVersion + " was not yet released");
return false;
}
final boolean released = releaseDate.get().isBefore(today());
if (released) {
logger.info("Version " + currentProjectVersion + " was released on " + changesFile.getReleaseDate()
+ " according to " + changesFilePath);
}
return released;
}

private LocalDate today() {
return LocalDate.ofInstant(clock.instant(), UTC_ZONE);
}

void incrementProjectVersion() {
final Path path = getPomPath();
final Model pom = readPom(path);
if (!this.currentProjectVersion.equals(pom.getVersion())) {
throw new IllegalStateException(ExaError.messageBuilder("E-PK-CORE-174").message(
"Inconsistent project version {{version in pom file}} found in pom {{pom file path}}, expected {{expected version}}",
pom.getVersion(), path, currentProjectVersion).toString());
}
final String nextVersion = getIncrementedVersion(currentProjectVersion);
System.out.println("#### Incremeing to " + nextVersion);
logger.info("Incrementing version from " + currentProjectVersion + " to " + nextVersion + " in POM " + path);
pom.setVersion(nextVersion);
writePom(path, pom);
}

static String getIncrementedVersion(final String version) {
final Semver current = new Semver(version);
return current.nextPatch().toString();
}

private Model readPom(final Path path) {
try {
return new MavenXpp3Reader().read(Files.newBufferedReader(path));
} catch (IOException | XmlPullParserException exception) {
throw new IllegalStateException(ExaError.messageBuilder("E-PK-CORE-172")
.message("Failed to read pom {{pom file path}}", path).toString(), exception);
}
}

private void writePom(final Path path, final Model pom) {
try {
new MavenXpp3Writer().write(Files.newOutputStream(getPomPath()), pom);
} catch (final IOException exception) {
throw new UncheckedIOException(ExaError.messageBuilder("E-PK-CORE-173")
.message("Failed to write pom {{pom file path}}", path).toString(), exception);
}
}

private Path getPomPath() {
return projectDir.resolve("pom.xml");
}
}
Loading

0 comments on commit 92f7943

Please sign in to comment.