diff --git a/release-tools/Spring Data Release Tools.sonargraph b/release-tools/Spring Data Release Tools.sonargraph deleted file mode 100644 index ef89de8..0000000 --- a/release-tools/Spring Data Release Tools.sonargraph +++ /dev/null @@ -1,149 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/release-tools/application-local.template b/release-tools/application-local.template deleted file mode 100644 index 7f47f84..0000000 --- a/release-tools/application-local.template +++ /dev/null @@ -1,32 +0,0 @@ -# Git -git.username= -git.author= -git.email= -git.password= -github.api.url=https://api.github.com - -# IO - -# Optionally set the JavaHome path used for exeuting maven goals (eg. /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/) -# If the property does not exist, is empty or points to an invalid directory the OS default JavaHome will be used. -#io.javaHome= - -# Maven -maven.mavenHome= -maven.console-logger=false -maven.parallelize=true - -# Deployment -# Must be always the encrypted password taken from the Artifactory GUI/Profile view -deployment.username= -deployment.password= -deployment.api-key= - -# GPG -gpg.keyname= -gpg.password= -gpg.executable=/usr/local/bin/gpg2 - -# A GitHub token with user:email, read:user and read:org scopes. -# User needs to be part of the Spring team on GitHub as well. -sagan.key= diff --git a/release-tools/lombok.config b/release-tools/lombok.config deleted file mode 100644 index 255addb..0000000 --- a/release-tools/lombok.config +++ /dev/null @@ -1,2 +0,0 @@ -lombok.nonNull.exceptionType = IllegalArgumentException -lombok.anyConstructor.addConstructorProperties = true diff --git a/release-tools/pom.xml b/release-tools/pom.xml deleted file mode 100644 index 206722d..0000000 --- a/release-tools/pom.xml +++ /dev/null @@ -1,163 +0,0 @@ - - 4.0.0 - org.springframework.data.build - spring-data-release-cli - 1.0.0.BUILD-SNAPSHOT - - - org.springframework.boot - spring-boot-starter-parent - 2.3.4.RELEASE - - - - 2.0.0.RELEASE - 1.18.18 - - - - - - org.springframework.boot - spring-boot-starter - - - - org.springframework.boot - spring-boot-starter-logging - - - - org.springframework - spring-web - - - - org.springframework.data - spring-data-commons - - - - com.fasterxml.jackson.core - jackson-databind - - - - com.fasterxml.jackson.module - jackson-module-parameter-names - - - - joda-time - joda-time - 2.10.8 - - - - org.springframework.shell - spring-shell - 1.2.0.RELEASE - - - - org.xmlbeam - xmlprojector - 1.4.7 - - - - com.googlecode.plist - dd-plist - 1.23 - - - - org.apache.commons - commons-exec - 1.3 - - - - org.apache.httpcomponents - httpclient - - - - org.projectlombok - lombok - provided - - - - org.springframework.plugin - spring-plugin-core - ${spring-plugin.version} - - - - org.springframework.boot - spring-boot-starter-test - - - - org.eclipse.jgit - org.eclipse.jgit - 5.6.0.201912101111-r - - - - org.springframework.boot - spring-boot-configuration-processor - true - - - - org.apache.maven.shared - maven-invoker - 3.0.1 - - - - org.jgrapht - jgrapht-core - 0.9.1 - - - - com.github.tomakehurst - wiremock - 2.26.3 - test - - - - com.google.guava - guava - 29.0-jre - test - - - - javax.servlet - javax.servlet-api - test - - - - - - ${project.name} - - - - org.springframework.boot - spring-boot-maven-plugin - - - - - - - diff --git a/release-tools/readme.md b/release-tools/readme.md deleted file mode 100644 index 1696e55..0000000 --- a/release-tools/readme.md +++ /dev/null @@ -1,99 +0,0 @@ -## General Notes - -* Use the command `help` to get a list of all commands in the release tools. -* After fixing a problem use `workspace cleanup` to cleanup any mess left behind by the previous step. - -## One Time Setup - -### Infrastructure requirements - -- Ensure you have the credentials for `buildmaster` accounts on https://repo.spring.io. -- Ensure yoiu have the credentials for https://oss.sonatype.org (to deploy and promote GA and service releases, need deployment permissions for `org.springframework.data`) in `settings.xml` for server with id `sonatype`. - -Both are available in the Spring/Pivotal Last Pass repository. - -### Prepare local configuration and credentials - -Add an `application-local.properties` to the project root and add the following properties: - -- `git.username` - Your GitHub username. -- `git.password` - Your GitHub Password (or API key with scopes: `public_repo, read:org, repo:status, repo_deployment, user` when using 2FA). -- `git.author` - Your full name (used for preparing commits). -- `git.email` - Your email (used for preparing commits). -- `maven.mavenHome` - Pointing to the location of your Maven installation. -- `deployment.username` - Your Artifactory user. -- `deployment.api-key` - The Artifactory API key to use for artifact promotion. -- `deployment.password` - The encrypted Artifactory password.. -- `gpg.keyname` - The GPG key name. -- `gpg.password` - The password of your GPG key. -- `gpg.executable` - Path to your GPG executable, typically `/usr/local/MacGPG2/bin/gpg2` - or `/usr/local/bin/gpg`. -- `sagan.key` - Sagan authentication token. Must be a valid GitHub token. Can be the same - as `git.password` when using a GitHub token as password. - -After that, run the `verify` command (`$ verify`) to verify your settings (authentication, -correct Maven, Java, and GPG setup). - -See `application-local.template` for details. - -## The release process - - -| Action | Command | -|--------|---------| -| Build and execute the release shell | `mvn package && java -jar target/spring-data-release-cli.jar` | -| | *All following commands are run in the release shell* | -| **Pre-release checks** | | -| Ensure all work on CVEs potentially contained in the release is done (incl. backports etc.) | N.A. | -| Upgrade dependencies in Spring Data Build parent pom (mind minor/major version rules) | N.A. | -| All release tickets are present | `$ tracker releasetickets $trainIteration` | -| Review open tickets for release | N.A. | -| Self-assign release tickets | `$ tracker prepare $trainIteration` | -| Announce release preparations to mailing list (https://groups.google.com/forum/#!forum/spring-data-dev) | N.A. | -| **Release the binaries** || -| | `$ release prepare $trainIteration` | -| Build the artefacts and push them to the apropriate maven repository | `$ release build $trainIteration` | -| |`$ release conclude $trainIteration` | -| Push the created commits to GitHub |`$ github push $trainIteration` | -| Push new maintanance branches if the release version was a GA release (`X.Y.0` version)|`$ git push $trainIteration.next`| -| Distribute documentation and static resources from tag |`$ release distribute $trainIteration`| -| **Post-release tasks** || -|Close JIRA tickets and GitHub release tickets.|`$ tracker close $trainIteration`| -|Create new release versions and tickets for upcoming version|`$ tracker setup-next $trainIteration.next`| -| Update versions in Sagan. `$targets` is given as comma separated lists of code names, without spaces. E.g. `Moore,Neumann` | `$ sagan update $releasetrains`| -| Create list of docs for release announcements | `$ announcement $trainIteration`| -| Announce release (Blog, Twitter) and notify downstream dependency projects as needed. | N.A. | - -### Utilities - -#### GitHub Labels - -`ProjectLabelConfiguration` contains a per-project configuration which labels should be present in a project. To apply that configuration (create or update), use: - -``` -$ github update labels $project -``` - -#### Dependency Upgrade - -`ProjectDependencies` contains a per-project configuration of dependencies. - -Workflow: - -* Check for dependency upgrades `$ dependency check $trainIteration` - -Reports upgradable dependencies for Build and Modules and -creates `dependency-upgrade-build.properties` file. -Edit `dependency-upgrade-build.properties` to specify the dependency version to upgrade. -Removing a line will omit that dependency upgrade. - -* Apply dependency upgrade with `$ dependency upgrade $trainIteration`. Applies dependency - upgrades currently only to Spring Data Build. -* Report store-specific dependencies to Spring Boot's current upgrade - ticket ([sample](https://github.com/spring-projects/spring-boot/issues/24036)) `$ dependency report $trainIteration` - -#### CI Properties Distribution - -To distribute `ci/pipeline.properties` across all modules use: - -`$ infra distribute ci-properties $trainIteration` diff --git a/release-tools/src/main/java/org/springframework/data/release/Application.java b/release-tools/src/main/java/org/springframework/data/release/Application.java deleted file mode 100644 index 6bd62f0..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/Application.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2015-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release; - -import java.util.logging.Logger; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.shell.support.logging.HandlerUtils; - -/** - * @author Oliver Gierke - */ -@SpringBootApplication -public class Application { - - public static void main(String[] args) { - - SpringApplication application = new SpringApplication(Application.class); - application.setAdditionalProfiles("local"); - - try { - BootShim bs = new BootShim(args, application.run(args)); - bs.run(); - } catch (RuntimeException e) { - throw e; - } finally { - HandlerUtils.flushAllHandlers(Logger.getLogger("")); - } - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/BootShim.java b/release-tools/src/main/java/org/springframework/data/release/BootShim.java deleted file mode 100644 index aec0a2c..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/BootShim.java +++ /dev/null @@ -1,113 +0,0 @@ -package org.springframework.data.release; - -import java.io.IOException; - -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; -import org.springframework.context.support.GenericApplicationContext; -import org.springframework.shell.CommandLine; -import org.springframework.shell.ShellException; -import org.springframework.shell.SimpleShellCommandLineOptions; -import org.springframework.shell.core.ExitShellRequest; -import org.springframework.shell.core.JLineShellComponent; -import org.springframework.util.StopWatch; - -public class BootShim { - - private static StopWatch sw = new StopWatch("Spring Shell"); - private static CommandLine commandLine; - private ConfigurableApplicationContext ctx; - - public BootShim(String[] args, ConfigurableApplicationContext context) { - this.ctx = context; - - try { - commandLine = SimpleShellCommandLineOptions.parseCommandLine(args); - } catch (IOException var5) { - throw new ShellException(var5.getMessage(), var5); - } - - this.configureApplicationContext(this.ctx); - ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner((BeanDefinitionRegistry) this.ctx); - if (commandLine.getDisableInternalCommands()) { - scanner.scan(new String[] { "org.springframework.shell.converters", "org.springframework.shell.plugin.support" }); - } else { - scanner.scan(new String[] { "org.springframework.shell.commands", "org.springframework.shell.converters", - "org.springframework.shell.plugin.support" }); - } - - } - - public ApplicationContext getApplicationContext() { - return this.ctx; - } - - private void configureApplicationContext(ConfigurableApplicationContext annctx) { - this.createAndRegisterBeanDefinition(annctx, CustomShellComponent.class, "shell"); - annctx.getBeanFactory().registerSingleton("commandLine", commandLine); - } - - protected void createAndRegisterBeanDefinition(GenericApplicationContext annctx, Class clazz) { - this.createAndRegisterBeanDefinition(annctx, clazz, (String) null); - } - - protected void createAndRegisterBeanDefinition(ConfigurableApplicationContext annctx, Class clazz, String name) { - RootBeanDefinition rbd = new RootBeanDefinition(); - rbd.setBeanClass(clazz); - DefaultListableBeanFactory bf = (DefaultListableBeanFactory) annctx.getBeanFactory(); - if (name != null) { - bf.registerBeanDefinition(name, rbd); - } else { - bf.registerBeanDefinition(clazz.getSimpleName(), rbd); - } - } - - public ExitShellRequest run() { - sw.start(); - String[] commandsToExecuteAndThenQuit = commandLine.getShellCommandsToExecute(); - JLineShellComponent shell = (JLineShellComponent) this.ctx.getBean("shell", JLineShellComponent.class); - ExitShellRequest exitShellRequest; - if (null != commandsToExecuteAndThenQuit) { - boolean successful = false; - exitShellRequest = ExitShellRequest.FATAL_EXIT; - String[] arr$ = commandsToExecuteAndThenQuit; - int len$ = commandsToExecuteAndThenQuit.length; - - for (int i$ = 0; i$ < len$; ++i$) { - String cmd = arr$[i$]; - successful = shell.executeCommand(cmd).isSuccess(); - if (!successful) { - break; - } - } - - if (successful) { - exitShellRequest = ExitShellRequest.NORMAL_EXIT; - } - } else { - shell.start(); - shell.promptLoop(); - exitShellRequest = shell.getExitShellRequest(); - if (exitShellRequest == null) { - exitShellRequest = ExitShellRequest.NORMAL_EXIT; - } - - shell.waitForComplete(); - } - - sw.stop(); - if (shell.isDevelopmentMode()) { - System.out.println("Total execution time: " + sw.getLastTaskTimeMillis() + " ms"); - } - - return exitShellRequest; - } - - public JLineShellComponent getJLineShellComponent() { - return (JLineShellComponent) this.ctx.getBean("shell", JLineShellComponent.class); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/CliComponent.java b/release-tools/src/main/java/org/springframework/data/release/CliComponent.java deleted file mode 100644 index e5b01ef..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/CliComponent.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2015-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.springframework.stereotype.Component; - -/** - * @author Oliver Gierke - */ -@Component -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -public @interface CliComponent { - -} diff --git a/release-tools/src/main/java/org/springframework/data/release/CustomShellComponent.java b/release-tools/src/main/java/org/springframework/data/release/CustomShellComponent.java deleted file mode 100644 index bbd4efa..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/CustomShellComponent.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2016-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release; - -import java.lang.reflect.Method; -import java.util.logging.Logger; - -import org.springframework.shell.core.ExecutionProcessor; -import org.springframework.shell.core.ExecutionStrategy; -import org.springframework.shell.core.JLineShellComponent; -import org.springframework.shell.core.SimpleExecutionStrategy; -import org.springframework.shell.event.ParseResult; -import org.springframework.shell.support.logging.HandlerUtils; -import org.springframework.util.Assert; -import org.springframework.util.ReflectionUtils; - -/** - * Extension of {@link JLineShellComponent} to customize the {@link ExecutionStrategy} to one that can deal with package - * protected command classes. - * - * @author Oliver Gierke - * @see https://github.com/spring-projects/spring-shell/pull/93 - */ -class CustomShellComponent extends JLineShellComponent { - - private final ExecutionStrategy executionStrategy = new CustomExecutionStrategy(); - - /* - * (non-Javadoc) - * @see org.springframework.shell.core.JLineShellComponent#getExecutionStrategy() - */ - @Override - protected ExecutionStrategy getExecutionStrategy() { - return executionStrategy; - } - - /** - * Effectively a copy of {@link SimpleExecutionStrategy} but with the tweaks provided in PR 93 for Spring shell to - * enable execution of package protected command classes. - * - * @author Oliver Gierke - * @see https://github.com/spring-projects/spring-shell/pull/93 - */ - static class CustomExecutionStrategy implements ExecutionStrategy { - - private static final Logger logger = HandlerUtils.getLogger(SimpleExecutionStrategy.class); - - private final Class mutex = SimpleExecutionStrategy.class; - - public Object execute(ParseResult parseResult) throws RuntimeException { - Assert.notNull(parseResult, "Parse result required"); - synchronized (mutex) { - Assert.isTrue(isReadyForCommands(), "SimpleExecutionStrategy not yet ready for commands"); - Object target = parseResult.getInstance(); - if (target instanceof ExecutionProcessor) { - ExecutionProcessor processor = ((ExecutionProcessor) target); - parseResult = processor.beforeInvocation(parseResult); - try { - Object result = invoke(parseResult); - processor.afterReturningInvocation(parseResult, result); - return result; - } catch (Throwable th) { - processor.afterThrowingInvocation(parseResult, th); - return handleThrowable(th); - } - } else { - return invoke(parseResult); - } - } - } - - private Object invoke(ParseResult parseResult) { - try { - Method method = parseResult.getMethod(); - ReflectionUtils.makeAccessible(method); - return ReflectionUtils.invokeMethod(method, parseResult.getInstance(), parseResult.getArguments()); - } catch (Throwable th) { - logger.severe("Command failed"); - return handleThrowable(th); - } - } - - private Object handleThrowable(Throwable th) { - if (th instanceof Error) { - throw ((Error) th); - } - if (th instanceof RuntimeException) { - throw ((RuntimeException) th); - } - throw new RuntimeException(th); - } - - public boolean isReadyForCommands() { - return true; - } - - public void terminate() {} - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/TimedCommand.java b/release-tools/src/main/java/org/springframework/data/release/TimedCommand.java deleted file mode 100644 index 0dfd75d..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/TimedCommand.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2016-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release; - -import org.springframework.shell.core.CommandMarker; -import org.springframework.shell.core.ExecutionProcessor; -import org.springframework.shell.event.ParseResult; -import org.springframework.util.StopWatch; - -/** - * Base class for command implementations who want to get their execution time logged. - * - * @author Oliver Gierke - */ -public abstract class TimedCommand implements ExecutionProcessor, CommandMarker { - - private StopWatch watch; - - /* - * (non-Javadoc) - * @see org.springframework.shell.core.ExecutionProcessor#beforeInvocation(org.springframework.shell.event.ParseResult) - */ - @Override - public ParseResult beforeInvocation(ParseResult invocationContext) { - - watch = new StopWatch(); - watch.start(); - - return invocationContext; - } - - /* - * (non-Javadoc) - * @see org.springframework.shell.core.ExecutionProcessor#afterReturningInvocation(org.springframework.shell.event.ParseResult, java.lang.Object) - */ - @Override - public void afterReturningInvocation(ParseResult invocationContext, Object result) { - stopAndLog(); - } - - /* - * (non-Javadoc) - * @see org.springframework.shell.core.ExecutionProcessor#afterThrowingInvocation(org.springframework.shell.event.ParseResult, java.lang.Throwable) - */ - @Override - public void afterThrowingInvocation(ParseResult invocationContext, Throwable thrown) { - stopAndLog(); - } - - private void stopAndLog() { - - watch.stop(); - System.out.println(String.format("Took: %.2f sec.", watch.getTotalTimeSeconds())); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/announcement/AnnouncementCommands.java b/release-tools/src/main/java/org/springframework/data/release/announcement/AnnouncementCommands.java deleted file mode 100644 index 1b2d84a..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/announcement/AnnouncementCommands.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2015-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.announcement; - -import lombok.NonNull; -import lombok.RequiredArgsConstructor; - -import org.springframework.data.release.CliComponent; -import org.springframework.data.release.TimedCommand; -import org.springframework.data.release.model.TrainIteration; -import org.springframework.shell.core.annotation.CliCommand; -import org.springframework.shell.core.annotation.CliOption; - -/** - * Commands to create markup to be used in announcing blog posts. - * - * @author Oliver Gierke - */ -@CliComponent -@RequiredArgsConstructor -class AnnouncementCommands extends TimedCommand { - - private final @NonNull AnnouncementOperations operations; - - @CliCommand("announcement") - public void announce(@CliOption(key = "", mandatory = true) TrainIteration iteration) throws Exception { - System.out.println(operations.getProjectBulletpoints(iteration)); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/announcement/AnnouncementOperations.java b/release-tools/src/main/java/org/springframework/data/release/announcement/AnnouncementOperations.java deleted file mode 100644 index 269783f..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/announcement/AnnouncementOperations.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.announcement; - -import static org.springframework.data.release.model.Projects.*; - -import org.springframework.data.release.build.MavenArtifact; -import org.springframework.data.release.cli.StaticResources; -import org.springframework.data.release.model.Project; -import org.springframework.data.release.model.TrainIteration; -import org.springframework.stereotype.Component; -import org.springframework.util.Assert; - -/** - * @author Oliver Gierke - */ -@Component -public class AnnouncementOperations { - - /** - * Returns the project list and links to be included in the release announcement for the given {@link TrainIteration}. - * - * @param iteration must not be {@literal null}. - * @return - */ - public String getProjectBulletpoints(TrainIteration iteration) { - - Assert.notNull(iteration, "Iteration must not be null!"); - - StringBuilder builder = new StringBuilder(); - - iteration.getModulesExcept(BUILD, BOM).forEach(module -> { - - Project project = module.getProject(); - - builder.append("* "); - builder.append(project.getFullName()).append(" "); - builder.append("`"); - builder.append(module.getShortVersionString()); - builder.append("`"); - builder.append(" - "); - - MavenArtifact artifact = new MavenArtifact(module); - - builder.append(getMarkDownLink("Artifacts", artifact.getRootUrl())); - builder.append(" - "); - - StaticResources resources = new StaticResources(module); - - builder.append(getMarkDownLink("Javadoc", resources.getJavaDocUrl())).append(" - "); - builder.append(getMarkDownLink("Documentation", resources.getDocumentationUrl())).append(" - "); - builder.append(getMarkDownLink("Changelog", resources.getChangelogUrl())); - - builder.append("\n"); - }); - - return builder.toString(); - } - - private static String getMarkDownLink(String name, String url) { - return String.format("[%s](%s)", name, url); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/build/BuildCommands.java b/release-tools/src/main/java/org/springframework/data/release/build/BuildCommands.java deleted file mode 100644 index 194a278..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/build/BuildCommands.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2016-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.build; - -import lombok.AccessLevel; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import lombok.experimental.FieldDefaults; - -import java.io.IOException; -import java.util.Optional; - -import org.springframework.data.release.CliComponent; -import org.springframework.data.release.TimedCommand; -import org.springframework.data.release.cli.TrainIterationConverter; -import org.springframework.data.release.git.GitOperations; -import org.springframework.data.release.io.Workspace; -import org.springframework.data.release.model.Project; -import org.springframework.data.release.model.Projects; -import org.springframework.data.release.model.ReleaseTrains; -import org.springframework.data.release.model.Train; -import org.springframework.data.release.model.TrainIteration; -import org.springframework.data.release.utils.Logger; -import org.springframework.shell.core.annotation.CliCommand; -import org.springframework.shell.core.annotation.CliOption; -import org.springframework.util.Assert; - -/** - * @author Oliver Gierke - * @author Mark Paluch - */ -@CliComponent -@RequiredArgsConstructor -@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) -class BuildCommands extends TimedCommand { - - @NonNull BuildOperations build; - @NonNull Workspace workspace; - @NonNull GitOperations git; - @NonNull Logger logger; - - /** - * Removes all Spring Data artifacts from the local repository. - * - * @throws IOException - */ - @CliCommand("build purge artifacts") - public void purge() throws IOException { - - logger.log("Workspace", "Cleaning up workspace directory at %s.", - workspace.getWorkingDirectory().getAbsolutePath()); - - workspace.purge(build.getLocalRepository(), - path -> build.getLocalRepository().relativize(path).startsWith("org/springframework/data")); - } - - /** - * Triggers a build for all modules of the given {@link TrainIteration}. - * - * @param iteration must not be {@literal null}. - * @param projectKey can be {@literal null} or empty. - */ - @CliCommand("build") - public void build(@CliOption(key = "", mandatory = true) TrainIteration iteration, // - @CliOption(key = "module") String projectKey) { - - Assert.notNull(iteration, "Train iteration must not be null!"); - Optional project = Projects.byName(projectKey); - - project.ifPresent(it -> build.triggerBuild(iteration.getModule(it))); - - if (!project.isPresent()) { - git.prepare(iteration); - iteration.forEach(build::triggerBuild); - } - } - - /** - * @param trainOrIteration must not be {@literal null}. Accepts release train names and train iterations. - */ - @CliCommand("build-distribute") - public void buildDistribute(@CliOption(key = "", mandatory = true) String trainOrIteration) { - - Assert.hasText(trainOrIteration, "Train or iteration must not be null or empty!"); - - if (trainOrIteration.contains(" ")) { - - TrainIteration trainIteration = new TrainIterationConverter().convertFromText(trainOrIteration, - TrainIteration.class, null); - - Assert.notNull(trainIteration, "TrainIteration must not be null!"); - git.prepare(trainIteration); - build.distributeResources(trainIteration); - - return; - } - - Train train = ReleaseTrains.getTrainByName(trainOrIteration); - - Assert.notNull(train, "Train must not be null!"); - - git.checkout(train); - build.distributeResources(train); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/build/BuildConfiguration.java b/release-tools/src/main/java/org/springframework/data/release/build/BuildConfiguration.java deleted file mode 100644 index 17eb9b0..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/build/BuildConfiguration.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2016-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.build; - -import java.util.List; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.release.model.Project; -import org.springframework.plugin.core.OrderAwarePluginRegistry; -import org.springframework.plugin.core.PluginRegistry; - -import org.xmlbeam.XBProjector; -import org.xmlbeam.XBProjector.Flags; -import org.xmlbeam.config.DefaultXMLFactoriesConfig; -import org.xmlbeam.config.DefaultXMLFactoriesConfig.NamespacePhilosophy; - -/** - * Spring configuration for build related components. - * - * @author Oliver Gierke - */ -@Configuration(proxyBeanMethods = false) -class BuildConfiguration { - - @Bean - public PluginRegistry buildSystems(List buildSystems) { - return OrderAwarePluginRegistry.create(buildSystems); - } - - @Bean - public XBProjector projectionFactory() { - - DefaultXMLFactoriesConfig config = new DefaultXMLFactoriesConfig(); - config.setNamespacePhilosophy(NamespacePhilosophy.AGNOSTIC); - config.setOmitXMLDeclaration(false); - config.setPrettyPrinting(false); - - return new XBProjector(config, Flags.TO_STRING_RENDERS_XML); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/build/BuildExecutor.java b/release-tools/src/main/java/org/springframework/data/release/build/BuildExecutor.java deleted file mode 100644 index e91b211..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/build/BuildExecutor.java +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Copyright 2019-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.build; - -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; - -import java.io.File; -import java.io.FileInputStream; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.function.BiFunction; -import java.util.function.Supplier; -import java.util.stream.Collector; -import java.util.stream.Collectors; - -import javax.annotation.PreDestroy; - -import org.apache.commons.io.IOUtils; - -import org.springframework.data.release.infra.InfrastructureOperations; -import org.springframework.data.release.io.Workspace; -import org.springframework.data.release.model.JavaVersion; -import org.springframework.data.release.model.Project; -import org.springframework.data.release.model.ProjectAware; -import org.springframework.data.release.utils.ListWrapperCollector; -import org.springframework.data.util.Streamable; -import org.springframework.plugin.core.PluginRegistry; -import org.springframework.stereotype.Component; -import org.springframework.util.Assert; - -/** - * Build executor service. - * - * @author Mark Paluch - */ -@Component -@RequiredArgsConstructor -class BuildExecutor { - - private final @NonNull PluginRegistry buildSystems; - private final MavenProperties mavenProperties; - private final ExecutorService executor; - private final Workspace workspace; - - @PreDestroy - public void shutdown() { - executor.shutdown(); - } - - /** - * Selects the build system for each module contained in the given iteration and executes the given function for it - * considering pre-requites, honoring the order. - * - * @param iteration must not be {@literal null}. - * @param function must not be {@literal null}. - * @return - */ - public Summary doWithBuildSystemOrdered(Streamable iteration, - BiFunction function) { - return doWithBuildSystem(iteration, function, true); - } - - /** - * Selects the build system for each module contained in the given iteration and executes the given function for it - * considering pre-requites, without considering the execution order. - * - * @param iteration must not be {@literal null}. - * @param function must not be {@literal null}. - * @return - */ - public Summary doWithBuildSystemAnyOrder(Streamable iteration, - BiFunction function) { - return doWithBuildSystem(iteration, function, false); - } - - private Summary doWithBuildSystem(Streamable iteration, - BiFunction function, boolean considerDependencyOrder) { - - Map> results = new ConcurrentHashMap<>(); - - // Add here projects that should be skipped because of a partial deployment to e.g. Sonatype. - Set skip = new HashSet<>(Arrays.asList()); - - skip.forEach(it -> results.put(it, CompletableFuture.completedFuture(null))); - - for (M moduleIteration : iteration) { - - if (skip.contains(moduleIteration.getProject())) { - continue; - } - - if (considerDependencyOrder) { - Set dependencies = moduleIteration.getProject().getDependencies(); - for (Project dependency : dependencies) { - - CompletableFuture futureResult = results.get(dependency); - - if (futureResult == null) { - - if (!iteration.stream().map(ProjectAware::getProject).anyMatch(project -> project.equals(dependency))) { - throw new IllegalStateException(moduleIteration.getProject().getName() + " requires " - + dependency.getName() + " which is not part of the Iteration. Please fix Projects/Iterations setup"); - } - - throw new IllegalStateException("No future result for " + dependency.getName() + ", required by " - + moduleIteration.getProject().getName()); - } - - futureResult.join(); - } - } - - CompletableFuture result = run(moduleIteration, function); - results.put(moduleIteration.getProject(), result); - } - - return iteration.stream()// - .map(module -> { - - CompletableFuture future = results.get(module.getProject()); - - try { - return new ExecutionResult(module.getProject(), future.get()); - } - - catch (InterruptedException | ExecutionException e) { - return new ExecutionResult(module.getProject(), e.getCause()); - } - - }) // - .collect(toSummaryCollector()); - } - - private CompletableFuture run(M module, BiFunction function) { - - Assert.notNull(module, "Module must not be null!"); - - CompletableFuture result = new CompletableFuture<>(); - Supplier exception = () -> new IllegalStateException( - String.format("No build system plugin found for project %s!", module.getProject())); - - BuildSystem buildSystem = buildSystems.getPluginFor(module.getProject(), exception) - .withJavaVersion(detectJavaVersion(module.getProject())); - - Runnable runnable = () -> { - - try { - - result.complete(function.apply(buildSystem, module)); - } catch (Exception e) { - result.completeExceptionally(e); - } - }; - - executor.execute(runnable); - - return result; - } - - @SneakyThrows - public JavaVersion detectJavaVersion(Project project) { - - File ciProperties = workspace.getFile(InfrastructureOperations.CI_PROPERTIES, project); - - if (!ciProperties.exists()) { - throw new IllegalStateException(String.format("Cannot find %s for project %s", ciProperties, project)); - } - - Properties properties = new Properties(); - - try (FileInputStream fis = new FileInputStream(ciProperties)) { - properties.load(fis); - } - - return JavaVersion.fromDockerTag(properties.getProperty("java.main.tag")); - } - - /** - * Returns a new collector to toSummaryCollector {@link ExecutionResult} as {@link Summary} using the {@link Stream} - * API. - * - * @return - */ - public static Collector, ?, Summary> toSummaryCollector() { - return ListWrapperCollector.collectInto(Summary::new); - } - - public static class ExecutionResult { - - private final Project project; - private final T result; - private final Throwable failure; - - public ExecutionResult(Project project, Throwable failure) { - this.project = project; - this.result = null; - this.failure = failure; - } - - public ExecutionResult(Project project, T result) { - this.project = project; - this.result = result; - this.failure = null; - } - - public T getResult() { - return result; - } - - @Override - public String toString() { - return String.format("%-14s - %s", project.getName(), - isSuccessful() ? "๐Ÿ†— Successful" : "๐Ÿงจ Error: " + failure.getMessage()); - } - - public boolean isSuccessful() { - return this.failure == null; - } - } - - public static class Summary { - - private final List> executions; - - public Summary(List> executions) { - this.executions = executions; - - if (!isSuccessful()) { - throw new BuildFailed(this); - } - } - - public List> getExecutions() { - return executions; - } - - public boolean isSuccessful() { - return this.executions.stream().allMatch(ExecutionResult::isSuccessful); - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - - builder.append("Execution summary"); - builder.append(IOUtils.LINE_SEPARATOR); - builder.append(executions.stream().map(it -> "\t" + it).collect(Collectors.joining(IOUtils.LINE_SEPARATOR))); - - return builder.toString(); - } - } - - static class BuildFailed extends RuntimeException { - - public BuildFailed(Summary summary) { - super(summary.toString()); - } - } - -} diff --git a/release-tools/src/main/java/org/springframework/data/release/build/BuildOperations.java b/release-tools/src/main/java/org/springframework/data/release/build/BuildOperations.java deleted file mode 100644 index d36dc37..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/build/BuildOperations.java +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright 2016-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.build; - -import lombok.NonNull; -import lombok.RequiredArgsConstructor; - -import java.nio.file.Path; -import java.util.List; -import java.util.function.BiFunction; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import org.assertj.core.util.VisibleForTesting; - -import org.springframework.data.release.deployment.DeploymentInformation; -import org.springframework.data.release.model.Module; -import org.springframework.data.release.model.ModuleIteration; -import org.springframework.data.release.model.Phase; -import org.springframework.data.release.model.Project; -import org.springframework.data.release.model.Projects; -import org.springframework.data.release.model.Train; -import org.springframework.data.release.model.TrainIteration; -import org.springframework.data.release.utils.Logger; -import org.springframework.plugin.core.PluginRegistry; -import org.springframework.stereotype.Component; -import org.springframework.util.Assert; - -/** - * @author Oliver Gierke - * @author Mark Paluch - */ -@Component -@RequiredArgsConstructor -public class BuildOperations { - - private final @NonNull PluginRegistry buildSystems; - private final @NonNull Logger logger; - private final @NonNull MavenProperties properties; - private final @NonNull BuildExecutor executor; - - /** - * Updates all inter-project dependencies based on the given {@link TrainIteration} and release {@link Phase}. - * - * @param iteration must not be {@literal null}. - * @param phase must not be {@literal null}. - * @throws Exception - */ - public void updateProjectDescriptors(TrainIteration iteration, Phase phase) throws Exception { - - Assert.notNull(iteration, "Train iteration must not be null!"); - Assert.notNull(phase, "Phase must not be null!"); - - UpdateInformation updateInformation = UpdateInformation.of(iteration, phase); - - BuildExecutor.Summary summary = executor.doWithBuildSystemOrdered(iteration, - (system, it) -> system.updateProjectDescriptors(it, updateInformation)); - - logger.log(iteration, "Update Project Descriptors done: %s", summary); - } - - /** - * Triggers the distribution builds for all modules participating in the given {@link TrainIteration}. - * - * @param iteration must not be {@literal null}. - */ - public void distributeResources(TrainIteration iteration) { - - Assert.notNull(iteration, "Train iteration must not be null!"); - - distributeResources(iteration.getTrain()); - } - - /** - * Triggers the distribution builds for all modules participating in the given {@link Train}. - * - * @param train must not be {@literal null}. - */ - public void distributeResources(Train train) { - - Assert.notNull(train, "Train must not be null!"); - - BuildExecutor.Summary summary = executor.doWithBuildSystemAnyOrder(train, - BuildSystem::triggerDistributionBuild); - - logger.log(train, "Distribution build: %s", summary); - } - - /** - * Triggers the distribution builds for the given module. - * - * @param iteration must not be {@literal null}. - */ - public void distributeResources(ModuleIteration iteration) { - - Assert.notNull(iteration, "ModuleIteration must not be null!"); - - doWithBuildSystem(iteration, BuildSystem::triggerDistributionBuild); - } - - /** - * Performs the release build for all modules in the given {@link TrainIteration}. - * - * @param iteration must not be {@literal null}. - * @return - */ - public List performRelease(TrainIteration iteration) { - - BuildExecutor.Summary summary = executor.doWithBuildSystemOrdered(iteration, - (buildSystem, moduleIteration) -> performRelease(moduleIteration)); - - logger.log(iteration, "Release: %s", summary); - - return summary.getExecutions().stream().map(BuildExecutor.ExecutionResult::getResult).collect(Collectors.toList()); - } - - /** - * Performs the release build for the given {@link ModuleIteration}. - * - * @param module must not be {@literal null}. - * @return - */ - public DeploymentInformation performRelease(ModuleIteration module) { - return buildAndDeployRelease(module); - } - - /** - * Prepares the versions of the given {@link TrainIteration} depending on the given {@link Phase}. - * - * @param iteration must not be {@literal null}. - * @param phase must not be {@literal null}. - */ - public void prepareVersions(TrainIteration iteration, Phase phase) { - - Assert.notNull(iteration, "Train iteration must not be null!"); - Assert.notNull(phase, "Phase must not be null!"); - - BuildExecutor.Summary summary = executor.doWithBuildSystemOrdered(iteration, - (system, module) -> system.prepareVersion(module, phase)); - - logger.log(iteration, "Prepare versions: %s", summary); - } - - /** - * Prepares the version of the given {@link ModuleIteration} depending on the given {@link Phase}. - * - * @param iteration must not be {@literal null}. - * @param phase must not be {@literal null}. - * @return - */ - @VisibleForTesting - public ModuleIteration prepareVersion(ModuleIteration iteration, Phase phase) { - - Assert.notNull(iteration, "Module iteration must not be null!"); - Assert.notNull(phase, "Phase must not be null!"); - - return doWithBuildSystem(iteration, (system, module) -> system.prepareVersion(module, phase)); - } - - /** - * Returns the {@link Path} of the local artifact repository. - * - * @return - */ - public Path getLocalRepository() { - return properties.getLocalRepository().toPath(); - } - - /** - * Builds the release for the given {@link ModuleIteration} and deploys it to the staging repository. - * - * @param module must not be {@literal null}. - * @return - */ - public DeploymentInformation buildAndDeployRelease(ModuleIteration module) { - return doWithBuildSystem(module, BuildSystem::deploy); - } - - /** - * Triggers a normal build for the given {@link ModuleIteration}. - * - * @param module must not be {@literal null}. - * @return - */ - public ModuleIteration triggerBuild(ModuleIteration module) { - return doWithBuildSystem(module, BuildSystem::triggerBuild); - } - - /** - * Triggers the pre-release checks for all modules of the given {@link TrainIteration}. - * - * @param iteration must not be {@literal null}. - */ - public void runPreReleaseChecks(TrainIteration iteration) { - - Assert.notNull(iteration, "Train iteration must not be null!"); - - executor.doWithBuildSystemAnyOrder(iteration, BuildSystem::triggerPreReleaseCheck); - } - - /** - * Verifies Java version presence and that the project can be build using Maven. - */ - public void verify() { - - Project project = Projects.BUILD; - BuildSystem buildSystem = buildSystems.getRequiredPluginFor(project); - - buildSystem.withJavaVersion(executor.detectJavaVersion(project)).verify(); - } - - /** - * Selects the build system for the module contained in the given {@link ModuleIteration} and executes the given - * function with it. - * - * @param module must not be {@literal null}. - * @param function must not be {@literal null}. - * @return - */ - private T doWithBuildSystem(ModuleIteration module, BiFunction function) { - - Assert.notNull(module, "ModuleIteration must not be null!"); - - Supplier exception = () -> new IllegalStateException( - String.format("No build system plugin found for project %s!", module.getProject())); - - BuildSystem buildSystem = buildSystems.getPluginFor(module.getProject(), exception); - - return function.apply(buildSystem.withJavaVersion(executor.detectJavaVersion(module.getProject())), module); - } - -} diff --git a/release-tools/src/main/java/org/springframework/data/release/build/BuildSystem.java b/release-tools/src/main/java/org/springframework/data/release/build/BuildSystem.java deleted file mode 100644 index 1f77a7c..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/build/BuildSystem.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2016-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.build; - -import org.springframework.data.release.deployment.DeploymentInformation; -import org.springframework.data.release.model.JavaVersion; -import org.springframework.data.release.model.ModuleIteration; -import org.springframework.data.release.model.Phase; -import org.springframework.data.release.model.Project; -import org.springframework.data.release.model.ProjectAware; -import org.springframework.plugin.core.Plugin; - -/** - * Plugin interface to back different build systems. - * - * @author Oliver Gierke - * @author Mark Paluch - */ -interface BuildSystem extends Plugin { - - /** - * Updates the project descriptors for the given {@link ModuleIteration} using the given {@link UpdateInformation}. - * - * @param iteration must not be {@literal null}. - * @param updateInformation must not be {@literal null}. - */ - M updateProjectDescriptors(M iteration, UpdateInformation updateInformation); - - /** - * Prepares the project descriptor of the {@link ModuleIteration} for the given release {@link Phase}. - * - * @param module must not be {@literal null}. - * @param phase must not be {@literal null}. - * @return - */ - ModuleIteration prepareVersion(ModuleIteration module, Phase phase); - - /** - * Deploy artifacts for the given {@link ModuleIteration} and return the {@link DeploymentInformation}. - * - * @param module must not be {@literal null}. - * @return - */ - DeploymentInformation deploy(ModuleIteration module); - - /** - * Runs the distribution build. - * - * @param module must not be {@literal null}. - * @return - */ - M triggerDistributionBuild(M module); - - M triggerBuild(M module); - - /** - * Triggers the pre-release checks for the given {@link ModuleIteration}. - * - * @param module must not be {@literal null}. - * @return - */ - M triggerPreReleaseCheck(M module); - - /** - * Verify general functionality and correctness of the build setup. - */ - void verify(); - - /** - * Prepare the build system with a Java version. - * - * @param javaVersion - * @return - */ - BuildSystem withJavaVersion(JavaVersion javaVersion); -} diff --git a/release-tools/src/main/java/org/springframework/data/release/build/CommandLine.java b/release-tools/src/main/java/org/springframework/data/release/build/CommandLine.java deleted file mode 100644 index 0c7c58b..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/build/CommandLine.java +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Copyright 2017-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.build; - -import lombok.AccessLevel; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import lombok.Value; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.function.BooleanSupplier; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.springframework.data.release.model.Masked; -import org.springframework.util.Assert; - -/** - * Value object to represent a Maven command line. - * - * @author Oliver Gierke - */ -@Value -class CommandLine { - - @NonNull List goals; - @NonNull List arguments; - - /** - * Creates a new {@link CommandLine} for the given {@link Goal} and {@link Argument}s. - * - * @param goal must not be {@literal null}. - * @param argument must not be {@literal null}. - * @return - */ - public static CommandLine of(Goal goal, Argument... argument) { - return new CommandLine(Collections.singletonList(goal), Arrays.asList(argument)); - } - - /** - * Creates a new {@link CommandLine} for the given {@link Goal}s and {@link Argument}s. - * - * @param goal must not be {@literal null}. - * @param argument must not be {@literal null}. - * @return - */ - public static CommandLine of(Goal first, Goal second, Argument... argument) { - return new CommandLine(Arrays.asList(first, second), Arrays.asList(argument)); - } - - /** - * Returns a new {@link CommandLine} with the given {@link Argument} added in case the given {@link BooleanSupplier} - * evaluates to {@literal true}. - * - * @param argument must not be {@literal null}. - * @param condition must not be {@literal null}. - * @return - */ - public CommandLine conditionalAnd(Argument argument, BooleanSupplier condition) { - return condition.getAsBoolean() ? and(argument) : this; - } - - /** - * Returns a new {@link CommandLine} with the given {@link Argument} added. - * - * @param argument must not be {@literal null}. - * @return - */ - public CommandLine and(Argument argument) { - - Assert.notNull(argument, "Argument must not be null!"); - - List newArguments = new ArrayList(arguments.size() + 1); - newArguments.addAll(arguments); - newArguments.add(argument); - - return new CommandLine(goals, newArguments); - } - - /** - * Renders the current {@link CommandLine} as a plain {@link List} of {@link String}s using the given {@link Function} - * to expand the {@link Goal}s. - * - * @param goalExpansion must not be {@literal null}. - * @return - */ - public List toCommandLine(Function goalExpansion) { - - Stream goalStream = goals.stream().map(goalExpansion); - Stream argumentStream = arguments.stream().map(it -> it.toCommandLineArgument()); - - return Stream.concat(goalStream, argumentStream).collect(Collectors.toList()); - } - - /* - * (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - - Stream goalStream = goals.stream().map(it -> it.getGoal()); - Stream argumentStream = arguments.stream().map(Object::toString); - - return Stream.concat(goalStream, argumentStream).collect(Collectors.joining(" ")); - } - - /** - * Represents a Maven goal to invoke. Can be a custom one but also one of the predefined instances. - * - * @author Oliver Gierke - */ - @Value(staticConstructor = "goal") - public static class Goal { - - public static final Goal CLEAN = Goal.goal("clean"); - public static final Goal INSTALL = Goal.goal("install"); - public static final Goal DEPLOY = Goal.goal("deploy"); - public static final Goal VALIDATE = Goal.goal("validate"); - public static final Goal VERIFY = Goal.goal("verify"); - - String goal; - } - - @Value - @RequiredArgsConstructor(access = AccessLevel.PRIVATE) - public static class Argument { - - public static Argument SKIP_TESTS = Argument.arg("skipTests"); - - @NonNull String name; - @NonNull Optional> value; - - private Argument(String name, ArgumentValue value) { - this(name, Optional.of(value)); - } - - static Argument of(String name) { - return new Argument(name, Optional.empty()); - } - - /** - * Enables the given comma-separated profiles for the {@link CommandLine}. - * - * @param name must not be {@literal null} or empty. - * @return - */ - public static Argument profile(String name, String... others) { - - Assert.hasText(name, "Profiles must not be null or empty!"); - Assert.notNull(others, "Other profiles must not be null!"); - - String profiles = Stream.concat(Stream.of(name), Arrays.stream(others)).collect(Collectors.joining(",")); - - return Argument.of("-P".concat(profiles)); - } - - public static Argument arg(String name) { - return Argument.of("-D".concat(name)); - } - - public static Argument debug() { - return Argument.of("-X"); - } - - public Argument withValue(Object value) { - return new Argument(name, ArgumentValue.of(value)); - } - - public Argument withQuotedValue(Object value) { - return new Argument(name, ArgumentValue.of(value, it -> String.format("\"%s\"", it.toString()))); - } - - public Argument withValue(Masked masked) { - return new Argument(name, ArgumentValue.of(masked)); - } - - public String toCommandLineArgument() { - return toNameValuePair(value.map(ArgumentValue::toCommandLine)); - } - - /* - * (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return toNameValuePair(value.map(Object::toString)); - } - - private String toNameValuePair(Optional source) { - - return source// - .map(it -> String.format("%s=%s", name, it))// - .orElse(name); - } - - @RequiredArgsConstructor(access = AccessLevel.PRIVATE) - private static class ArgumentValue { - - private final @NonNull T value; - private final @NonNull Optional> preparer; - private final @NonNull Optional> toString; - - public static ArgumentValue of(T value) { - return new ArgumentValue<>(value, Optional.empty(), Optional.empty()); - } - - public static ArgumentValue of(T value, Function preparer) { - return new ArgumentValue<>(value, Optional.of(preparer), Optional.empty()); - } - - /** - * Returns an {@link ArgumentValue} for the given {@link Masked} value. - * - * @param masked must not be {@literal null}. - * @return - */ - public static ArgumentValue of(T masked) { - return new ArgumentValue<>(masked, Optional.empty(), Optional.of(it -> it.masked())); - } - - /** - * Returns the {@link String} variant of the argument value. - * - * @return - */ - public String toCommandLine() { - return preparer.map(it -> it.apply(value)).orElseGet(() -> value.toString()); - } - - /* - * (non-Javadoc) - * @see java.lang.Object#toString() - */ - public String toString() { - return toString.map(it -> it.apply(value)).orElseGet(() -> toCommandLine()); - } - } - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/build/GroupId.java b/release-tools/src/main/java/org/springframework/data/release/build/GroupId.java deleted file mode 100644 index 0eba8be..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/build/GroupId.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.build; - -import lombok.Value; - -/** - * Value object to represent an artifacts group identifier. - * - * @author Oliver Gierke - */ -@Value(staticConstructor = "of") -class GroupId { - - private final String value; - - public String asPath() { - return value.replace('.', '/'); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/build/MavenArtifact.java b/release-tools/src/main/java/org/springframework/data/release/build/MavenArtifact.java deleted file mode 100644 index 730a0d4..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/build/MavenArtifact.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.build; - -import static org.springframework.data.release.model.Projects.*; - -import lombok.EqualsAndHashCode; -import lombok.Getter; - -import org.springframework.data.release.model.ArtifactVersion; -import org.springframework.data.release.model.ModuleIteration; -import org.springframework.data.release.model.Project; -import org.springframework.util.Assert; - -/** - * Value object to represent a Maven artifact. - * - * @author Oliver Gierke - */ -@EqualsAndHashCode -public class MavenArtifact { - - private static final GroupId GROUP_ID = GroupId.of("org.springframework.data"); - - private final Project project; - private final @Getter Repository repository; - private final @Getter ArtifactVersion version; - - /** - * Creates a new {@link MavenArtifact} for the given {@link ModuleIteration}. - * - * @param module must not be {@literal null}. - */ - public MavenArtifact(ModuleIteration module) { - - Assert.notNull(module, "Module iteration must not be null!"); - - this.project = module.getModule().getProject(); - this.repository = new Repository(module.getIteration()); - this.version = ArtifactVersion.of(module); - } - - public MavenArtifact(Project project, ArtifactVersion version) { - - this.project = project; - this.repository = new Repository(version); - this.version = version; - } - - public String getGroupId() { - return GROUP_ID.getValue(); - } - - /** - * Returns the Maven artifact identifier. - * - * @return - */ - public String getArtifactId() { - - String artifactId = String.format("spring-data-%s", project.getName().toLowerCase()); - - return REST.equals(project) ? artifactId.concat("-webmvc") : artifactId; - } - - public ArtifactVersion getNextDevelopmentVersion() { - return version.getNextDevelopmentVersion(); - } - - /** - * Returns the URL pointing to the artifacts. - * - * @return - */ - public String getRootUrl() { - return String.format("%s/%s/%s/%s", repository.getUrl(), GROUP_ID.asPath(), getArtifactId(), version); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/build/MavenBuildSystem.java b/release-tools/src/main/java/org/springframework/data/release/build/MavenBuildSystem.java deleted file mode 100644 index c108886..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/build/MavenBuildSystem.java +++ /dev/null @@ -1,464 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.build; - -import static org.springframework.data.release.build.CommandLine.Argument.*; -import static org.springframework.data.release.build.CommandLine.Goal.*; -import static org.springframework.data.release.model.Projects.*; - -import lombok.AccessLevel; -import lombok.RequiredArgsConstructor; -import lombok.experimental.FieldDefaults; - -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.StringWriter; -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.function.Consumer; -import java.util.regex.Pattern; - -import javax.xml.transform.TransformerException; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; - -import org.apache.commons.io.IOUtils; - -import org.springframework.core.annotation.Order; -import org.springframework.data.release.build.CommandLine.Argument; -import org.springframework.data.release.build.CommandLine.Goal; -import org.springframework.data.release.build.Pom.Artifact; -import org.springframework.data.release.deployment.DefaultDeploymentInformation; -import org.springframework.data.release.deployment.DeploymentInformation; -import org.springframework.data.release.deployment.DeploymentProperties; -import org.springframework.data.release.io.Workspace; -import org.springframework.data.release.model.ArtifactVersion; -import org.springframework.data.release.model.Gpg; -import org.springframework.data.release.model.JavaVersion; -import org.springframework.data.release.model.ModuleIteration; -import org.springframework.data.release.model.Phase; -import org.springframework.data.release.model.Project; -import org.springframework.data.release.model.ProjectAware; -import org.springframework.data.release.model.TrainIteration; -import org.springframework.data.release.utils.Logger; -import org.springframework.stereotype.Component; -import org.springframework.util.Assert; - -import org.xmlbeam.ProjectionFactory; -import org.xmlbeam.XBProjector; -import org.xmlbeam.dom.DOMAccess; -import org.xmlbeam.io.XBStreamInput; - -/** - * @author Oliver Gierke - * @author Mark Paluch - */ -@Component -@Order(100) -@RequiredArgsConstructor -@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) -class MavenBuildSystem implements BuildSystem { - - static String POM_XML = "pom.xml"; - - Workspace workspace; - ProjectionFactory projectionFactory; - Logger logger; - MavenRuntime mvn; - DeploymentProperties properties; - Gpg gpg; - - @Override - public BuildSystem withJavaVersion(JavaVersion javaVersion) { - return new MavenBuildSystem(workspace, projectionFactory, logger, mvn.withJavaVersion(javaVersion), properties, - gpg); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.release.build.BuildSystem#updateProjectDescriptors(org.springframework.data.release.model.ModuleIteration, org.springframework.data.release.model.TrainIteration, org.springframework.data.release.model.Phase) - */ - @Override - public M updateProjectDescriptors(M module, UpdateInformation information) { - - PomUpdater updater = new PomUpdater(logger, information, module.getProject()); - - if (updater.isBuildProject()) { - - if (information.isBomInBuildProject()) { - updateBom(information, "bom/pom.xml", BUILD); - } - - updateParentPom(information); - - } else if (updater.isBomProject()) { - updateBom(information, "bom/pom.xml", BOM); - } else { - - doWithProjection(workspace.getFile(POM_XML, updater.getProject()), pom -> { - - updater.updateDependencyProperties(pom); - updater.updateParentVersion(pom); - updater.updateRepository(pom); - }); - } - - return module; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.release.build.BuildSystem#triggerDistributionBuild(org.springframework.data.release.model.Module) - */ - @Override - public M triggerDistributionBuild(M module) { - - Project project = module.getProject(); - - if (BUILD.equals(project)) { - return module; - } - - if (BOM.equals(project)) { - return module; - } - - if (!isMavenProject(project)) { - logger.log(project, "Skipping project as no pom.xml could be found in the working directory!"); - return module; - } - - logger.log(project, "Triggering distribution buildโ€ฆ"); - - mvn.execute(project, CommandLine.of(Goal.CLEAN, Goal.DEPLOY, // - SKIP_TESTS, profile("distribute"), Argument.of("-B"), - arg("artifactory.server").withValue(properties.getServer().getUri()), - arg("artifactory.distribution-repository").withValue(properties.getDistributionRepository()), - arg("artifactory.username").withValue(properties.getUsername()), - arg("artifactory.password").withValue(properties.getPassword()))); - - mvn.execute(project, CommandLine.of(Goal.CLEAN, Goal.DEPLOY, // - SKIP_TESTS, profile("distribute-schema"), Argument.of("-B"), - arg("artifactory.server").withValue(properties.getServer().getUri()), - arg("artifactory.distribution-repository").withValue(properties.getDistributionRepository()), - arg("artifactory.username").withValue(properties.getUsername()), - arg("artifactory.password").withValue(properties.getPassword()))); - - logger.log(project, "Successfully finished distribution build!"); - - return module; - } - - private void updateBom(UpdateInformation updateInformation, String file, Project project) { - - TrainIteration iteration = updateInformation.getTrain(); - - logger.log(BUILD, "Updating BOM pom.xmlโ€ฆ"); - - doWithProjection(workspace.getFile(file, project), pom -> { - - for (ModuleIteration module : iteration.getModulesExcept(BUILD, BOM)) { - - ArtifactVersion version = updateInformation.getProjectVersionToSet(module.getProject()); - - logger.log(project, "%s", module); - - String moduleArtifactId = new MavenArtifact(module).getArtifactId(); - pom.setDependencyManagementVersion(moduleArtifactId, version); - logger.log(project, "Updated managed dependency version for %s to %s!", moduleArtifactId, version); - - module.getProject().doWithAdditionalArtifacts(additionalArtifact -> { - - String artifactId = additionalArtifact.getArtifactId(); - Artifact artifact = pom.getManagedDependency(artifactId); - - if (artifact != null) { - pom.setDependencyManagementVersion(artifactId, version); - logger.log(project, "Updated managed dependency version for %s to %s!", artifactId, version); - } else { - logger.log(project, "Artifact %s not found, skipping update!", artifactId); - } - }); - } - - if (updateInformation.getPhase().equals(Phase.PREPARE)) { - - // Make sure we have no snapshot leftovers - List snapshotDependencies = pom.getSnapshotDependencies(); - - if (!snapshotDependencies.isEmpty()) { - throw new IllegalStateException(String.format("Found snapshot dependencies %s!", snapshotDependencies)); - } - } - }); - } - - private void updateParentPom(UpdateInformation information) { - - // Fix version of shared resources to to-be-released version. - doWithProjection(workspace.getFile("parent/pom.xml", BUILD), ParentPom.class, pom -> { - - logger.log(BUILD, "Setting shared resources version to %s.", information.getParentVersionToSet()); - pom.setSharedResourcesVersion(information.getParentVersionToSet()); - - logger.log(BUILD, "Setting releasetrain property to %s.", information.getReleaseTrainVersion()); - pom.setReleaseTrain(information.getReleaseTrainVersion()); - }); - } - - public boolean isMavenProject(ModuleIteration module) { - - Project project = module.getProject(); - - if (!isMavenProject(project)) { - logger.log(module, "No pom.xml file found, skipping project."); - return false; - } - - return true; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.release.build.BuildSystem#prepareVersion(org.springframework.data.release.model.ModuleIteration, org.springframework.data.release.model.Phase) - */ - @Override - public ModuleIteration prepareVersion(ModuleIteration module, Phase phase) { - - Project project = module.getProject(); - UpdateInformation information = UpdateInformation.of(module.getTrainIteration(), phase); - - CommandLine goals = CommandLine.of(goal("versions:set"), goal("versions:commit")); - - if (BOM.equals(project)) { - - mvn.execute(project, goals.and(arg("newVersion").withValue(information.getReleaseTrainVersion())) // - .and(arg("generateBackupPoms").withValue("false"))); - - mvn.execute(project, goals.and(arg("newVersion").withValue(information.getReleaseTrainVersion())) // - .and(arg("generateBackupPoms").withValue("false")) // - .and(arg("processAllModules").withValue("true")) // - .and(Argument.of("-pl").withValue("bom"))); - - } else { - mvn.execute(project, goals.and(arg("newVersion").withValue(information.getProjectVersionToSet(project))) - .and(arg("generateBackupPoms").withValue("false"))); - } - - if (BUILD.equals(project)) { - - if (!module.getTrain().usesCalver()) { - mvn.execute(project, goals.and(arg("newVersion").withValue(information.getReleaseTrainVersion())) // - .and(arg("generateBackupPoms").withValue("false")) // - .and(arg("groupId").withValue("org.springframework.data")) // - .and(arg("artifactId").withValue("spring-data-releasetrain"))); - } - - mvn.execute(project, CommandLine.of(Goal.INSTALL)); - } - - return module; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.release.build.BuildSystem#deploy(org.springframework.data.release.model.ModuleIteration) - */ - @Override - public DeploymentInformation deploy(ModuleIteration module) { - - Assert.notNull(module, "Module must not be null!"); - - DeploymentInformation information = new DefaultDeploymentInformation(module, properties); - - deployToArtifactory(module, information); - deployToMavenCentral(module); - - return information; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.release.build.BuildSystem#triggerBuild(org.springframework.data.release.model.ModuleIteration) - */ - @Override - public M triggerBuild(M module) { - - CommandLine arguments = CommandLine.of(Goal.CLEAN, Goal.INSTALL)// - .conditionalAnd(SKIP_TESTS, () -> module.getProject().skipTests()); - - mvn.execute(module.getProject(), arguments); - - return module; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.release.build.BuildSystem#triggerPreReleaseCheck(org.springframework.data.release.model.ModuleIteration) - */ - public M triggerPreReleaseCheck(M module) { - - mvn.execute(module.getProject(), CommandLine.of(Goal.CLEAN, Goal.VALIDATE, profile("pre-release"))); - - return module; - } - - /* - * (non-Javadoc) - * @see org.springframework.plugin.core.Plugin#supports(java.lang.Object) - */ - @Override - public boolean supports(Project project) { - return isMavenProject(project); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.release.build.BuildSystem#verify() - */ - @Override - public void verify() { - - logger.log(BUILD, "Verifying Maven Build Systemโ€ฆ"); - - CommandLine arguments = CommandLine.of(Goal.CLEAN, Goal.VERIFY, // - profile("central"), // - SKIP_TESTS, // - arg("gpg.executable").withValue(gpg.getExecutable()), // - arg("gpg.keyname").withValue(gpg.getKeyname()), // - arg("gpg.password").withValue(gpg.getPassword())); - - mvn.execute(BUILD, arguments); - - mvn.execute(BUILD, CommandLine.of(Goal.goal("nexus-staging:rc-list-profiles"), // - profile("central"))); - } - - /** - * Triggers Maven commands to deploy module artifacts to Spring Artifactory. - * - * @param module must not be {@literal null}. - * @param information must not be {@literal null}. - */ - private void deployToArtifactory(ModuleIteration module, DeploymentInformation information) { - - Assert.notNull(module, "Module iteration must not be null!"); - Assert.notNull(information, "Deployment information must not be null!"); - - if (!module.getIteration().isPreview()) { - logger.log(module, "Not a preview version (milestone or release candidate). Skipping Artifactory deployment."); - return; - } - - logger.log(module, "Deploying artifacts to Spring Artifactoryโ€ฆ"); - - CommandLine arguments = CommandLine.of(Goal.CLEAN, Goal.DEPLOY, // - profile("ci,release,artifactory"), // - SKIP_TESTS, // - arg("artifactory.server").withValue(properties.getServer().getUri()), - arg("artifactory.staging-repository").withValue(properties.getStagingRepository()), - arg("artifactory.username").withValue(properties.getUsername()), - arg("artifactory.password").withValue(properties.getPassword()), - arg("artifactory.build-name").withQuotedValue(information.getBuildName()), - arg("artifactory.build-number").withValue(information.getBuildNumber())); - - mvn.execute(module.getProject(), arguments); - } - - /** - * Triggers Maven commands to deploy to Sonatype's OSS Nexus if the given {@link ModuleIteration} refers to a version - * that has to be publicly released. - * - * @param module must not be {@literal null}. - */ - private void deployToMavenCentral(ModuleIteration module) { - - Assert.notNull(module, "Module iteration must not be null!"); - - if (!module.getIteration().isPublic()) { - - logger.log(module, "Skipping deployment to Maven Central as it's not a public version!"); - return; - } - - logger.log(module, "Deploying artifacts to Sonatype OSS Nexusโ€ฆ"); - - CommandLine arguments = CommandLine.of(Goal.CLEAN, Goal.DEPLOY, // - profile("ci,release,central"), // - SKIP_TESTS, // - arg("gpg.executable").withValue(gpg.getExecutable()), // - arg("gpg.keyname").withValue(gpg.getKeyname()), // - arg("gpg.password").withValue(gpg.getPassword())); - - mvn.execute(module.getProject(), arguments); - } - - private boolean isMavenProject(Project project) { - return workspace.getFile(POM_XML, project).exists(); - } - - private void doWithProjection(File file, Consumer callback) { - doWithProjection(file, Pom.class, callback); - } - - /** - * TODO: Move XML file callbacks using the {@link ProjectionFactory} to {@link Workspace}. - */ - private void doWithProjection(File file, Class type, Consumer callback) { - - try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) { - byte[] content = doWithProjection((XBProjector) projectionFactory, bis, type, callback); - - try (FileOutputStream fos = new FileOutputStream(file)) { - fos.write(content); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - static byte[] doWithProjection(XBProjector projector, InputStream stream, Class type, - Consumer callback) throws IOException { - - XBStreamInput io = projector.io().stream(stream); - T pom = io.read(type); - callback.accept(pom); - - StringWriter writer = new StringWriter(); - try { - projector.config().createTransformer().transform(new DOMSource(((DOMAccess) pom).getDOMNode()), - new StreamResult(writer)); - } catch (TransformerException e) { - throw new RuntimeException(e); - } - - String s = writer.toString(); - - if (s.contains("standalone=\"no\"?><")) { - s = s.replaceAll(Pattern.quote("standalone=\"no\"?><"), "standalone=\"no\"?>" + IOUtils.LINE_SEPARATOR + "<"); - } - - if (!s.endsWith(IOUtils.LINE_SEPARATOR)) { - s += IOUtils.LINE_SEPARATOR; - } - - return s.getBytes(StandardCharsets.UTF_8); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/build/MavenProperties.java b/release-tools/src/main/java/org/springframework/data/release/build/MavenProperties.java deleted file mode 100644 index 4acc907..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/build/MavenProperties.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2015-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.build; - -import lombok.Data; -import lombok.extern.slf4j.Slf4j; - -import java.io.File; -import java.util.Map; - -import org.apache.commons.io.FileUtils; - -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.stereotype.Component; -import org.springframework.util.Assert; - -/** - * Maven configuration properties. - * - * @author Oliver Gierke - * @author Mark Paluch - */ -@Slf4j -@Data -@Component -@ConfigurationProperties(prefix = "maven") -class MavenProperties { - - private File mavenHome; - private File localRepository; - private Map plugins; - private boolean consoleLogger = true; - private boolean parallelize = false; - - /** - * Configures the local Maven repository location to use. In case the given folder does not already exists it's - * created. - * - * @param localRepository must not be {@literal null} or empty. - */ - public void setLocalRepository(String localRepository) { - - Assert.hasText(localRepository, "Local repository must not be null!"); - - log.info("Using {} as local Maven repository!", localRepository); - - this.localRepository = new File(localRepository.replace("~", FileUtils.getUserDirectoryPath())); - - if (!this.localRepository.exists()) { - this.localRepository.mkdirs(); - } - } - - /** - * Returns the fully-qualified plugin goal for the given local one. - * - * @param goal must not be {@literal null} or empty. - * @return - */ - public String getFullyQualifiedPlugin(String goal) { - - Assert.hasText(goal, "Goal must not be null or empty!"); - - if (goal.startsWith("-")) { - return goal; - } - - String[] parts = goal.split(":"); - - if (parts.length != 2 || !plugins.containsKey(parts[0])) { - return goal; - } - - return plugins.get(parts[0]).concat(":").concat(parts[1]); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/build/MavenRuntime.java b/release-tools/src/main/java/org/springframework/data/release/build/MavenRuntime.java deleted file mode 100644 index a49d68e..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/build/MavenRuntime.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright 2015-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.build; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -import java.io.Closeable; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.List; -import java.util.stream.Collectors; - -import org.apache.maven.shared.invoker.DefaultInvocationRequest; -import org.apache.maven.shared.invoker.DefaultInvoker; -import org.apache.maven.shared.invoker.InvocationRequest; -import org.apache.maven.shared.invoker.InvocationResult; -import org.apache.maven.shared.invoker.Invoker; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.release.io.JavaRuntimes; -import org.springframework.data.release.io.Workspace; -import org.springframework.data.release.model.JavaVersion; -import org.springframework.data.release.model.Project; -import org.springframework.data.release.utils.Logger; -import org.springframework.shell.support.util.StringUtils; -import org.springframework.stereotype.Component; - -/** - * @author Oliver Gierke - * @author Mark Paluch - */ -@Slf4j -@Component -class MavenRuntime { - - private final Workspace workspace; - private final Logger logger; - private final MavenProperties properties; - private final JavaRuntimes.JdkInstallation jdk; - - /** - * Creates a new {@link MavenRuntime} for the given {@link Workspace} and Maven home. - * - * @param workspace must not be {@literal null}. - * @param logger must not be {@literal null}. - * @param properties must not be {@literal null}. - */ - @Autowired - public MavenRuntime(Workspace workspace, Logger logger, MavenProperties properties) { - this(workspace, logger, properties, JavaVersion.JAVA_8); - } - - private MavenRuntime(Workspace workspace, Logger logger, MavenProperties properties, - JavaVersion requiredJavaVersion) { - - this.workspace = workspace; - this.logger = logger; - this.properties = properties; - this.jdk = JavaRuntimes.Selector.from(requiredJavaVersion).notGraalVM().getRequiredJdkInstallation(); - } - - public MavenRuntime withJavaVersion(JavaVersion javaVersion) { - return new MavenRuntime(workspace, logger, properties, javaVersion); - } - - public void execute(Project project, CommandLine arguments) { - - logger.log(project, "๐Ÿ“ฆ Executing mvn %s", arguments.toString()); - - try (MavenLogger mavenLogger = getLogger(project, arguments.getGoals())) { - - Invoker invoker = new DefaultInvoker(); - invoker.setMavenHome(properties.getMavenHome()); - invoker.setOutputHandler(mavenLogger::info); - invoker.setErrorHandler(mavenLogger::warn); - - File localRepository = properties.getLocalRepository(); - - if (localRepository != null) { - invoker.setLocalRepositoryDirectory(localRepository); - } - - File javaHome = getJavaHome(); - mavenLogger.info(String.format("Java Home: %s", jdk)); - mavenLogger.info(String.format("Executing: mvn %s", arguments)); - - InvocationRequest request = new DefaultInvocationRequest(); - request.setJavaHome(javaHome); - request.setShellEnvironmentInherited(true); - request.setBaseDirectory(workspace.getProjectDirectory(project)); - request.setBatchMode(true); - - request.setGoals(arguments.toCommandLine(it -> properties.getFullyQualifiedPlugin(it.getGoal()))); - - InvocationResult result = invoker.execute(request); - - if (result.getExitCode() != 0) { - logger.warn(project, "๐Ÿ™ˆ Failed execution mvn %s", arguments.toString()); - - throw new IllegalStateException("๐Ÿ™ˆ Failed execution mvn " + arguments.toString(), - result.getExecutionException()); - } - logger.log(project, "๐Ÿ†— Successful execution mvn %s", arguments.toString()); - } catch (Exception e) { - if (e instanceof RuntimeException) { - throw (RuntimeException) e; - } - throw new RuntimeException(e); - } - } - - private File getJavaHome() { - return jdk.getHome().getAbsoluteFile(); - } - - private MavenLogger getLogger(Project project, List goals) { - - if (this.properties.isConsoleLogger()) { - return new SlfLogger(log, project); - } - - return new FileLogger(log, project, this.workspace.getLogsDirectory(), goals); - } - - /** - * Maven Logging Forwarder. - */ - interface MavenLogger extends Closeable { - - void info(String message); - - void warn(String message); - } - - @RequiredArgsConstructor - static class SlfLogger implements MavenLogger { - - private final org.slf4j.Logger logger; - private final String logPrefix; - - SlfLogger(org.slf4j.Logger logger, Project project) { - this.logger = logger; - this.logPrefix = StringUtils.padRight(project.getName(), 10); - } - - @Override - public void info(String message) { - logger.info(logPrefix + ": " + message); - } - - @Override - public void warn(String message) { - logger.warn(logPrefix + ": " + message); - } - - @Override - public void close() throws IOException { - // no-op - } - } - - static class FileLogger implements MavenLogger { - - private final PrintWriter printWriter; - private final FileOutputStream outputStream; - - FileLogger(org.slf4j.Logger logger, Project project, File logsDirectory, List goals) { - - if (!logsDirectory.exists()) { - logsDirectory.mkdirs(); - } - - String goalNames = goals.stream().map(CommandLine.Goal::getGoal).collect(Collectors.joining("-")); - - String filename = String.format("mvn-%s-%s.log", project.getName(), goalNames).replace(':', '.'); - - try { - File file = new File(logsDirectory, filename); - logger.info("Routing Maven output to " + file.getCanonicalPath()); - outputStream = new FileOutputStream(file, true); - } catch (IOException e) { - throw new RuntimeException(e); - } - - printWriter = new PrintWriter(outputStream, true); - } - - @Override - public void info(String message) { - printWriter.println(message); - } - - @Override - public void warn(String message) { - printWriter.println(message); - } - - @Override - public void close() throws IOException { - printWriter.close(); - outputStream.close(); - } - } - -} diff --git a/release-tools/src/main/java/org/springframework/data/release/build/ParentPom.java b/release-tools/src/main/java/org/springframework/data/release/build/ParentPom.java deleted file mode 100644 index 17907ea..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/build/ParentPom.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.build; - -import org.springframework.data.release.model.ArtifactVersion; -import org.xmlbeam.annotation.XBValue; -import org.xmlbeam.annotation.XBWrite; - -/** - * @author Oliver Gierke - * @author Mark Paluch - */ -public interface ParentPom extends Pom { - - @XBWrite("/project/properties/releasetrain") - void setReleaseTrain(@XBValue String releaseTrain); - - @XBWrite("/project/profiles/profile[id=\"distribute\"]/dependencies/dependency[artifactId=\"spring-data-build-resources\"]/version") - void setSharedResourcesVersion(@XBValue ArtifactVersion value); -} diff --git a/release-tools/src/main/java/org/springframework/data/release/build/Pom.java b/release-tools/src/main/java/org/springframework/data/release/build/Pom.java deleted file mode 100644 index 28d7458..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/build/Pom.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.build; - -import java.util.List; - -import org.springframework.data.release.model.ArtifactVersion; -import org.xmlbeam.annotation.XBRead; -import org.xmlbeam.annotation.XBValue; -import org.xmlbeam.annotation.XBWrite; - -/** - * @author Oliver Gierke - */ -public interface Pom { - - @XBRead("/project") - Artifact getArtifact(); - - @XBRead("/project/version") - String getRawVersion(); - - @XBRead("/project/version") - ArtifactVersion getVersion(); - - @XBWrite("/project/version") - void setVersion(ArtifactVersion version); - - @XBWrite("/project/parent/version") - void setParentVersion(ArtifactVersion version); - - @XBRead("/project/properties/{0}") - String getProperty(String property); - - @XBWrite("/project/properties/{0}") - void setProperty(String property, @XBValue ArtifactVersion value); - - @XBWrite("/project/properties/{0}") - void setProperty(String property, @XBValue String value); - - @XBWrite("/project/repositories/repository[id=\"{0}\"]/id") - void setRepositoryId(String oldId, @XBValue String newId); - - @XBWrite("/project/repositories/repository[id=\"{0}\"]/url") - void setRepositoryUrl(String id, @XBValue String url); - - /** - * Sets the version of the dependency with the given artifact identifier to the given {@link ArtifactVersion}. - * - * @param artifactId - * @param version - */ - @XBWrite("/project/dependencies/dependency[artifactId=\"{0}\"]/version") - Pom setDependencyVersion(String artifactId, @XBValue ArtifactVersion version); - - @XBRead("/project/dependencies/dependency[artifactId=\"{0}\"]/version") - String getDependencyVersion(String artifactId); - - @XBWrite("/project/dependencyManagement/dependencies/dependency[artifactId=\"{0}\"]/version") - Pom setDependencyManagementVersion(String artifactId, @XBValue ArtifactVersion version); - - @XBRead("/project/dependencyManagement/dependencies/dependency[artifactId=\"{0}\"]") - Artifact getManagedDependency(String artifactId); - - @XBRead("//dependency[substring(version, string-length(version) - string-length('-SNAPSHOT') + 1) = '-SNAPSHOT']") - List getSnapshotDependencies(); - - public interface Repository { - - @XBRead("child::id") - String getId(); - - @XBRead("child::url") - String getUrl(); - } - - public interface Artifact { - - @XBRead("child::groupId") - GroupId getGroupId(); - - @XBRead("child::artifactId") - String getArtifactId(); - - @XBRead("child::version") - String getVersion(); - - default String getArtifactPath() { - return "/".concat(getGroupId().asPath()).concat("/").concat(getArtifactId()); - } - - default String getPath() { - return getArtifactPath().concat(getVersion()); - } - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/build/PomUpdater.java b/release-tools/src/main/java/org/springframework/data/release/build/PomUpdater.java deleted file mode 100644 index d60ca06..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/build/PomUpdater.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2015-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.build; - -import static org.springframework.data.release.model.Phase.*; -import static org.springframework.data.release.model.Projects.*; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -import org.springframework.data.release.model.ArtifactVersion; -import org.springframework.data.release.model.Project; -import org.springframework.data.release.utils.Logger; -import org.springframework.util.Assert; - -/** - * @author Oliver Gierke - */ -@RequiredArgsConstructor -class PomUpdater { - - private final Logger logger; - private final UpdateInformation information; - private final @Getter Project project; - - public boolean isBuildProject() { - return BUILD.equals(project); - } - - public boolean isBomProject() { - return BOM.equals(project); - } - - public void updateArtifactVersion(Pom pom) { - - ArtifactVersion version = information.getProjectVersionToSet(project); - logger.log(project, "Updated project version to %s.", version); - pom.setVersion(version); - } - - public void updateDependencyProperties(Pom pom) { - - project.getDependencies().forEach(dependency -> { - - String dependencyProperty = dependency.getDependencyProperty(); - - if (pom.getProperty(dependencyProperty) == null) { - return; - } - - ArtifactVersion version = information.getProjectVersionToSet(dependency); - - logger.log(project, "Updating %s dependency version property %s to %s.", dependency.getFullName(), - dependencyProperty, version); - pom.setProperty(dependencyProperty, version); - }); - } - - /** - * Updates the version of the parent project in the given {@link Pom}. - * - * @param pom must not be {@literal null}. - */ - public void updateParentVersion(Pom pom) { - - Assert.notNull(pom, "Pom must not be null!"); - - ArtifactVersion version = information.getParentVersionToSet(); - - logger.log(project, "Updating Spring Data Build Parent version to %s.", version); - pom.setParentVersion(version); - } - - /** - * Updates the repository section in the given {@link Pom}. - * - * @param pom must not be {@literal null}. - */ - public void updateRepository(Pom pom) { - - Assert.notNull(pom, "Pom must not be null!"); - - String message = "Switching to Spring repository %s (%s)."; - Repository repository = information.getRepository(); - - if (PREPARE.equals(information.getPhase())) { - - logger.log(project, message, repository.getId(), repository.getUrl()); - - pom.setRepositoryId(repository.getSnapshotId(), repository.getId()); - pom.setRepositoryUrl(repository.getId(), repository.getUrl()); - - } else { - - logger.log(project, message, repository.getSnapshotId(), repository.getSnapshotUrl()); - - pom.setRepositoryId(repository.getId(), repository.getSnapshotId()); - pom.setRepositoryUrl(repository.getSnapshotId(), repository.getSnapshotUrl()); - } - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/build/Repository.java b/release-tools/src/main/java/org/springframework/data/release/build/Repository.java deleted file mode 100644 index 4100aaa..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/build/Repository.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.build; - -import lombok.Value; - -import org.springframework.data.release.model.ArtifactVersion; -import org.springframework.data.release.model.Iteration; -import org.springframework.util.Assert; - -/** - * @author Oliver Gierke - */ - -@Value -public class Repository { - - private static final String ID_BASE = "spring-libs-"; - private static final String BASE = "https://repo.spring.io/libs-"; - - String id, url; - - public Repository(Iteration iteration) { - - Assert.notNull(iteration, "Iteration must not be null!"); - - this.id = ID_BASE.concat(iteration.isPublic() ? "release" : "milestone"); - this.url = BASE.concat(iteration.isPublic() ? "release" : "milestone"); - } - - public Repository(ArtifactVersion version) { - - String suffix = getSuffixFor(version); - - this.id = ID_BASE.concat(suffix); - this.url = BASE.concat(suffix); - } - - public String getSnapshotId() { - return ID_BASE.concat("snapshot"); - } - - public String getSnapshotUrl() { - return BASE.concat("snapshot"); - } - - private static String getSuffixFor(ArtifactVersion version) { - - if (version.isSnapshotVersion()) { - return "snapshot"; - } - - if (version.isMilestoneVersion() || version.isReleaseCandidateVersion()) { - return "milestone"; - } - - if (version.isReleaseVersion()) { - return "release"; - } - - throw new IllegalArgumentException(String.format("Unsupported ArtifactVersion %s!", version)); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/build/UpdateInformation.java b/release-tools/src/main/java/org/springframework/data/release/build/UpdateInformation.java deleted file mode 100644 index 00b1ded..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/build/UpdateInformation.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2015-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.build; - -import static org.springframework.data.release.model.Projects.*; - -import lombok.Getter; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; - -import org.springframework.data.release.model.ArtifactVersion; -import org.springframework.data.release.model.Phase; -import org.springframework.data.release.model.Project; -import org.springframework.data.release.model.TrainIteration; -import org.springframework.util.Assert; - -/** - * Value object to expose update information for a given {@link TrainIteration} and phase. - * - * @author Oliver Gierke - */ -@RequiredArgsConstructor(staticName = "of") -public class UpdateInformation { - - private final @NonNull @Getter TrainIteration train; - private final @NonNull @Getter Phase phase; - - /** - * Returns the {@link ArtifactVersion} to be set for the given {@link Project}. - * - * @param dependency must not be {@literal null}. - * @return will never be {@literal null}. - */ - public ArtifactVersion getProjectVersionToSet(Project dependency) { - - Assert.notNull(dependency, "Project must not be null!"); - - ArtifactVersion dependencyVersion = train.getModuleVersion(dependency); - - switch (phase) { - case PREPARE: - return dependencyVersion; - case CLEANUP: - return dependencyVersion.getNextDevelopmentVersion(); - case MAINTENANCE: - return dependencyVersion.getNextBugfixVersion(); - } - - throw new IllegalStateException("Unexpected phase detected " + phase + " detected!"); - } - - /** - * Returns the {@link ArtifactVersion} to be set for the parent reference. - * - * @return will never be {@literal null}. - */ - public ArtifactVersion getParentVersionToSet() { - - ArtifactVersion version = train.getModuleVersion(BUILD); - - switch (phase) { - case PREPARE: - return version; - case CLEANUP: - return version.getNextDevelopmentVersion(); - case MAINTENANCE: - return version.getNextBugfixVersion(); - } - - throw new IllegalStateException("Unexpected phase detected " + phase + " detected!"); - } - - /** - * Returns the {@link Repository} to use (milestone or release). - * - * @return will never be {@literal null}. - */ - public Repository getRepository() { - return new Repository(train.getIteration()); - } - - /** - * Returns the version {@link String} to be used to describe the release train. - * - * @return will never be {@literal null}. - */ - public String getReleaseTrainVersion() { - - boolean usesCalver = train.getTrain().usesCalver(); - - switch (phase) { - case PREPARE: - return train.getReleaseTrainNameAndVersion(); - case MAINTENANCE: - if (usesCalver) { - return String.format("%s-SNAPSHOT", train.getNextBugfixName()); - } - - case CLEANUP: - - if (usesCalver) { - - if (train.getIteration().isGAIteration()) { - return String.format("%s-SNAPSHOT", train.getNextIterationName()); - } - - return String.format("%s-SNAPSHOT", train.getNextBugfixName()); - } - - return String.format("%s-BUILD-SNAPSHOT", train.getName()); - } - - throw new IllegalStateException("Unexpected phase detected " + phase + " detected!"); - } - - public boolean isBomInBuildProject() { - return !train.getTrain().usesCalver(); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/cli/ModelCommands.java b/release-tools/src/main/java/org/springframework/data/release/cli/ModelCommands.java deleted file mode 100644 index 330e07a..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/cli/ModelCommands.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.cli; - -import java.util.stream.Collectors; - -import org.springframework.data.release.CliComponent; -import org.springframework.data.release.TimedCommand; -import org.springframework.data.release.model.ReleaseTrains; -import org.springframework.data.release.model.Train; -import org.springframework.shell.core.annotation.CliCommand; -import org.springframework.shell.core.annotation.CliOption; - -/** - * @author Oliver Gierke - */ -@CliComponent -class ModelCommands extends TimedCommand { - - @CliCommand(value = "trains", help = "Displays all release trains or contents of them if a name is provided") - public String train(@CliOption(key = { "", "train" }) Train train) { - - return train != null ? train.toString() - : ReleaseTrains.TRAINS.stream().map(Train::getName).collect(Collectors.joining(", ")); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/cli/ProjectConverter.java b/release-tools/src/main/java/org/springframework/data/release/cli/ProjectConverter.java deleted file mode 100644 index 435f776..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/cli/ProjectConverter.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.cli; - -import java.util.List; - -import org.springframework.data.release.model.Project; -import org.springframework.data.release.model.Projects; -import org.springframework.shell.core.Completion; -import org.springframework.shell.core.Converter; -import org.springframework.shell.core.MethodTarget; -import org.springframework.stereotype.Component; - -/** - * @author Mark Paluch - */ -@Component -public class ProjectConverter implements Converter { - - /* - * (non-Javadoc) - * @see org.springframework.shell.core.Converter#supports(java.lang.Class, java.lang.String) - */ - @Override - public boolean supports(Class type, String optionContext) { - return Project.class.isAssignableFrom(type); - } - - /* - * (non-Javadoc) - * @see org.springframework.shell.core.Converter#convertFromText(java.lang.String, java.lang.Class, java.lang.String) - */ - @Override - public Project convertFromText(String value, Class targetType, String optionContext) { - return Projects.requiredByName(value); - } - - /* - * (non-Javadoc) - * @see org.springframework.shell.core.Converter#getAllPossibleValues(java.util.List, java.lang.Class, java.lang.String, java.lang.String, org.springframework.shell.core.MethodTarget) - */ - @Override - public boolean getAllPossibleValues(List completions, Class targetType, String existingData, - String optionContext, MethodTarget target) { - - for (Project project : Projects.all()) { - completions.add(new Completion(project.getName())); - } - - return true; - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/cli/ReleaseCommands.java b/release-tools/src/main/java/org/springframework/data/release/cli/ReleaseCommands.java deleted file mode 100644 index 50b168e..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/cli/ReleaseCommands.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.cli; - -import static org.springframework.data.release.model.Projects.*; - -import lombok.AccessLevel; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import lombok.experimental.FieldDefaults; - -import org.springframework.data.release.CliComponent; -import org.springframework.data.release.TimedCommand; -import org.springframework.data.release.build.BuildOperations; -import org.springframework.data.release.deployment.DeploymentInformation; -import org.springframework.data.release.deployment.DeploymentOperations; -import org.springframework.data.release.git.GitOperations; -import org.springframework.data.release.issues.IssueTrackerCommands; -import org.springframework.data.release.issues.github.GitHubCommands; -import org.springframework.data.release.misc.ReleaseOperations; -import org.springframework.data.release.model.ArtifactVersion; -import org.springframework.data.release.model.ModuleIteration; -import org.springframework.data.release.model.Phase; -import org.springframework.data.release.model.Project; -import org.springframework.data.release.model.Projects; -import org.springframework.data.release.model.ReleaseTrains; -import org.springframework.data.release.model.Train; -import org.springframework.data.release.model.TrainIteration; -import org.springframework.shell.core.annotation.CliCommand; -import org.springframework.shell.core.annotation.CliOption; -import org.springframework.util.Assert; - -/** - * @author Oliver Gierke - */ -@CliComponent -@RequiredArgsConstructor -@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) -class ReleaseCommands extends TimedCommand { - - @NonNull GitOperations git; - @NonNull ReleaseOperations misc; - @NonNull DeploymentOperations deployment; - @NonNull BuildOperations build; - @NonNull IssueTrackerCommands tracker; - @NonNull GitHubCommands gitHub; - - @CliCommand("release predict") - public String predictTrainAndIteration() { - - return git.getTags(COMMONS).getLatest().toArtifactVersion().// - map(ReleaseCommands::getTrainNameForCommonsVersion).// - orElse(null); - } - - /** - * Composite command to ship a full release. - * - * @param iteration - * @throws Exception - */ - @CliCommand(value = "ship-it") - public void shipIt(@CliOption(key = "", mandatory = true) TrainIteration iteration) throws Exception { - - tracker.trackerPrepare(iteration); - - prepare(iteration); - - buildRelease(iteration, null); - - conclude(iteration); - - gitHub.push(iteration); - - distribute(iteration, null); - - tracker.closeIteration(iteration); - } - - /** - * Prepares the release of the given iteration of the given train. - * - * @param iteration - * @throws Exception - */ - @CliCommand(value = "release prepare", help = "Prepares the release of the iteration of the given train.") - public void prepare(@CliOption(key = "", mandatory = true) TrainIteration iteration) throws Exception { - - git.prepare(iteration); - - build.runPreReleaseChecks(iteration); - - misc.updateResources(iteration); - build.updateProjectDescriptors(iteration, Phase.PREPARE); - git.commit(iteration, "Prepare %s."); - - build.prepareVersions(iteration, Phase.PREPARE); - git.commit(iteration, "Release version %s."); - } - - @CliCommand(value = "release build") - public void buildRelease(@CliOption(key = "", mandatory = true) TrainIteration iteration, // - @CliOption(key = "project", mandatory = false) String projectName) { - - if (!iteration.getIteration().isPublic()) { - deployment.verifyAuthentication(); - } - - if (projectName != null) { - - Project project = Projects.requiredByName(projectName); - ModuleIteration module = iteration.getModule(project); - - DeploymentInformation information = build.performRelease(module); - deployment.promote(information); - - } else { - build.performRelease(iteration).forEach(deployment::promote); - } - } - - /** - * Concludes the release of the given {@link TrainIteration}. - * - * @param iteration - * @throws Exception - */ - @CliCommand(value = "release conclude") - public void conclude(@CliOption(key = "", mandatory = true) TrainIteration iteration) throws Exception { - - Assert.notNull(iteration, "Train iteration must not be null!"); - - // Tag release - git.tagRelease(iteration); - - if (iteration.getTrain().isAlwaysUseBranch()) { - setupMaintenanceVersions(iteration); - } else { - - build.prepareVersions(iteration, Phase.CLEANUP); - git.commit(iteration, "Prepare next development iteration."); - - // Prepare main branch - build.updateProjectDescriptors(iteration, Phase.CLEANUP); - git.commit(iteration, "After release cleanups."); - - // Prepare maintenance branches - if (iteration.getIteration().isGAIteration()) { - - // Create bugfix branches - git.createMaintenanceBranches(iteration); - - // Set project version to maintenance once - setupMaintenanceVersions(iteration); - } - } - } - - private void setupMaintenanceVersions(TrainIteration iteration) throws Exception { - - // Set project version to maintenance once - build.prepareVersions(iteration, Phase.MAINTENANCE); - git.commit(iteration, "Prepare next development iteration."); - - // Update inter-project dependencies and repositories - build.updateProjectDescriptors(iteration, Phase.MAINTENANCE); - git.commit(iteration, "After release cleanups."); - - // Back to main branch - git.checkout(iteration); - } - - /** - * Triggers the distribution of release artifacts for all projects. - * - * @param iteration - * @throws Exception - */ - @CliCommand("release distribute") - public void distribute(@CliOption(key = "", mandatory = true) TrainIteration iteration, - @CliOption(key = "project", mandatory = false) String projectName) { - - git.checkout(iteration); - - if (projectName != null) { - Project project = Projects.requiredByName(projectName); - ModuleIteration module = iteration.getModule(project); - - build.distributeResources(module); - } else { - build.distributeResources(iteration); - } - } - - private static String getTrainNameForCommonsVersion(ArtifactVersion version) { - - return ReleaseTrains.TRAINS.stream().// - filter(train -> version.toString().startsWith(train.getModule(COMMONS).getVersion().toString())).// - findFirst().map(Train::getName).orElse(null); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/cli/SpringDataReleaseCliBannerProvider.java b/release-tools/src/main/java/org/springframework/data/release/cli/SpringDataReleaseCliBannerProvider.java deleted file mode 100644 index 12601de..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/cli/SpringDataReleaseCliBannerProvider.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.cli; - -import org.apache.commons.io.IOUtils; - -import org.springframework.boot.SpringBootVersion; -import org.springframework.core.Ordered; -import org.springframework.core.annotation.Order; -import org.springframework.shell.plugin.BannerProvider; -import org.springframework.shell.support.util.FileUtils; -import org.springframework.stereotype.Component; - -@Order(Ordered.HIGHEST_PRECEDENCE) -@Component -class SpringDataReleaseCliBannerProvider implements BannerProvider { - - /* - * (non-Javadoc) - * @see org.springframework.shell.plugin.NamedProvider#getProviderName() - */ - @Override - public String getProviderName() { - return "Spring Data Release Shell"; - } - - /* - * (non-Javadoc) - * @see org.springframework.shell.plugin.BannerProvider#getBanner() - */ - @Override - public String getBanner() { - - StringBuilder builder = new StringBuilder(); - - builder.append(FileUtils.readBanner(SpringDataReleaseCliBannerProvider.class, "banner.txt")); - builder.append(getVersion()).append(IOUtils.LINE_SEPARATOR); - builder.append(IOUtils.LINE_SEPARATOR); - - return builder.toString(); - } - - /* - * (non-Javadoc) - * @see org.springframework.shell.plugin.BannerProvider#getVersion() - */ - @Override - public String getVersion() { - return "1.0 on Spring Boot " + SpringBootVersion.getVersion(); - } - - /* - * (non-Javadoc) - * @see org.springframework.shell.plugin.BannerProvider#getWelcomeMessage() - */ - @Override - public String getWelcomeMessage() { - return "Welcome to the Spring Data Release Shell!"; - } - -} diff --git a/release-tools/src/main/java/org/springframework/data/release/cli/SpringDataReleaseCliPromptProvider.java b/release-tools/src/main/java/org/springframework/data/release/cli/SpringDataReleaseCliPromptProvider.java deleted file mode 100644 index 790f6bb..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/cli/SpringDataReleaseCliPromptProvider.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.cli; - -import org.springframework.core.Ordered; -import org.springframework.core.annotation.Order; -import org.springframework.shell.plugin.PromptProvider; -import org.springframework.stereotype.Component; - -/** - * @author Oliver Gierke - */ -@Component -@Order(Ordered.HIGHEST_PRECEDENCE) -class SpringDataReleaseCliPromptProvider implements PromptProvider { - - /* - * (non-Javadoc) - * @see org.springframework.shell.plugin.PromptProvider#getPrompt() - */ - @Override - public String getPrompt() { - return "$ "; - } - - /* - * (non-Javadoc) - * @see org.springframework.shell.plugin.NamedProvider#getProviderName() - */ - @Override - public String getProviderName() { - return "spring-data-release-cli"; - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/cli/StaticResources.java b/release-tools/src/main/java/org/springframework/data/release/cli/StaticResources.java deleted file mode 100644 index 1c35af9..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/cli/StaticResources.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.cli; - -import lombok.RequiredArgsConstructor; - -import org.springframework.data.release.git.GitProject; -import org.springframework.data.release.git.Tag; -import org.springframework.data.release.git.VersionTags; -import org.springframework.data.release.model.ArtifactVersion; -import org.springframework.data.release.model.DocumentationMetadata; -import org.springframework.data.release.model.ModuleIteration; -import org.springframework.data.release.model.Project; - -/** - * @author Oliver Gierke - * @author Mark Paluch - */ -@RequiredArgsConstructor -public class StaticResources { - - private final DocumentationMetadata metadata; - - private final String releaseUrl; - - public StaticResources(ModuleIteration module) { - - this.metadata = DocumentationMetadata.of(module.getProject(), ArtifactVersion.of(module), false); - - Project project = module.getProject(); - GitProject gitProject = GitProject.of(project); - Tag tag = VersionTags.empty(module.getProject()).createTag(module); - - this.releaseUrl = String.format("%s/releases/tag/%s", gitProject.getProjectUri(), tag.getName()); - } - - public String getDocumentationUrl() { - return metadata.getReferenceDocUrl(); - } - - public String getJavaDocUrl() { - return metadata.getApiDocUrl(); - } - - public String getChangelogUrl() { - return releaseUrl; - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/cli/TrainConverter.java b/release-tools/src/main/java/org/springframework/data/release/cli/TrainConverter.java deleted file mode 100644 index fe17326..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/cli/TrainConverter.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.cli; - -import java.util.List; -import java.util.regex.Pattern; - -import org.springframework.data.release.model.ArtifactVersion; -import org.springframework.data.release.model.ReleaseTrains; -import org.springframework.data.release.model.Train; -import org.springframework.shell.core.Completion; -import org.springframework.shell.core.Converter; -import org.springframework.shell.core.MethodTarget; -import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; - -/** - * @author Oliver Gierke - * @author Mark Paluch - */ -@Component -public class TrainConverter implements Converter { - - private static final Pattern CALVER = Pattern.compile("(\\d{4})(\\.(\\d))+"); - - /* - * (non-Javadoc) - * @see org.springframework.shell.core.Converter#supports(java.lang.Class, java.lang.String) - */ - @Override - public boolean supports(Class type, String optionContext) { - return Train.class.isAssignableFrom(type); - } - - /* - * (non-Javadoc) - * @see org.springframework.shell.core.Converter#convertFromText(java.lang.String, java.lang.Class, java.lang.String) - */ - @Override - public Train convertFromText(String value, Class targetType, String optionContext) { - - if (StringUtils.isEmpty(value)) { - return null; - } - - if (CALVER.matcher(value).matches()) { - - ArtifactVersion version = ArtifactVersion.of(value); - return ReleaseTrains.getTrainByCalver(version.getVersion()); - } - - return ReleaseTrains.getTrainByName(value); - } - - /* - * (non-Javadoc) - * @see org.springframework.shell.core.Converter#getAllPossibleValues(java.util.List, java.lang.Class, java.lang.String, java.lang.String, org.springframework.shell.core.MethodTarget) - */ - @Override - public boolean getAllPossibleValues(List completions, Class targetType, String existingData, - String optionContext, MethodTarget target) { - return false; - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/cli/TrainIterationConverter.java b/release-tools/src/main/java/org/springframework/data/release/cli/TrainIterationConverter.java deleted file mode 100644 index 9b698aa..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/cli/TrainIterationConverter.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.cli; - -import java.util.List; -import java.util.regex.Pattern; - -import org.springframework.data.release.model.ArtifactVersion; -import org.springframework.data.release.model.Iteration; -import org.springframework.data.release.model.ReleaseTrains; -import org.springframework.data.release.model.Train; -import org.springframework.data.release.model.TrainIteration; -import org.springframework.shell.core.Completion; -import org.springframework.shell.core.Converter; -import org.springframework.shell.core.MethodTarget; -import org.springframework.stereotype.Component; - -/** - * @author Oliver Gierke - * @author Mark Paluch - */ -@Component -public class TrainIterationConverter implements Converter { - - private static final Pattern CALVER = Pattern.compile("(\\d{4})(\\.(\\d+))+(-M(\\d)|-RC(\\d))?"); - - /* - * (non-Javadoc) - * @see org.springframework.shell.core.Converter#supports(java.lang.Class, java.lang.String) - */ - @Override - public boolean supports(Class type, String optionContext) { - return TrainIteration.class.equals(type); - } - - /* - * (non-Javadoc) - * @see org.springframework.shell.core.Converter#convertFromText(java.lang.String, java.lang.Class, java.lang.String) - */ - @Override - public TrainIteration convertFromText(String value, Class targetType, String optionContext) { - - if (CALVER.matcher(value).matches()) { - - ArtifactVersion version = ArtifactVersion.of(value); - Train train = ReleaseTrains.getTrainByCalver(version.getVersion()); - - if (version.isReleaseVersion()) { - if (version.isBugFixVersion()) { - return train.getIteration("SR" + version.getVersion().getBugfix()); - } - return train.getIteration(Iteration.GA); - } - - return train.getIteration(version.getSuffix()); - } - - String[] parts = value.split(" "); - - if (parts.length != 2) { - throw new IllegalArgumentException(String.format("Cannot resolve TrainIteration from '%s'", value)); - } - - Train train = ReleaseTrains.getTrainByName(parts[0].trim()); - - return train.getIteration(parts[1].trim()); - } - - /* - * (non-Javadoc) - * @see org.springframework.shell.core.Converter#getAllPossibleValues(java.util.List, java.lang.Class, java.lang.String, java.lang.String, org.springframework.shell.core.MethodTarget) - */ - @Override - public boolean getAllPossibleValues(List completions, Class targetType, String existingData, - String optionContext, MethodTarget target) { - - for (Train train : ReleaseTrains.TRAINS) { - - for (Iteration iteration : train.getIterations()) { - - TrainIteration trainIteration = train.getIteration(iteration.getName()); - - completions.add(new Completion(trainIteration.toString())); - - if (trainIteration.getTrain().usesCalver()) { - completions.add(new Completion(trainIteration.getCalver().toMajorMinorBugfix())); - } - } - } - - return true; - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/cli/VerifyCommands.java b/release-tools/src/main/java/org/springframework/data/release/cli/VerifyCommands.java deleted file mode 100644 index 7d0b0c4..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/cli/VerifyCommands.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2021-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.cli; - -import lombok.AccessLevel; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import lombok.experimental.FieldDefaults; - -import org.springframework.data.release.CliComponent; -import org.springframework.data.release.TimedCommand; -import org.springframework.data.release.build.BuildOperations; -import org.springframework.data.release.deployment.DeploymentOperations; -import org.springframework.data.release.git.GitOperations; -import org.springframework.data.release.issues.github.GitHub; -import org.springframework.data.release.sagan.SaganClient; -import org.springframework.data.release.utils.Logger; -import org.springframework.shell.core.annotation.CliCommand; - -/** - * Commands to verify a correct Release Tools Setup. - * - * @author Mark Paluch - */ -@CliComponent -@RequiredArgsConstructor -@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) -class VerifyCommands extends TimedCommand { - - @NonNull GitOperations git; - @NonNull GitHub github; - @NonNull DeploymentOperations deployment; - @NonNull BuildOperations build; - @NonNull SaganClient saganClient; - @NonNull Logger logger; - - @CliCommand("verify") - public void verifyReleaseTools() { - - // Git checkout build - git.verify(); - - // Maven interaction - build.verify(); - - // Artifactory verification - deployment.verifyAuthentication(); - - // GitHub verification - github.verifyAuthentication(); - - // Sagan Verification - saganClient.verifyAuthentication(); - - logger.log("Verify", "All settings are verified. You can ship a release now."); - } - -} diff --git a/release-tools/src/main/java/org/springframework/data/release/deployment/ArtifactoryClient.java b/release-tools/src/main/java/org/springframework/data/release/deployment/ArtifactoryClient.java deleted file mode 100644 index 0f56c61..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/deployment/ArtifactoryClient.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2015-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.deployment; - -import lombok.RequiredArgsConstructor; -import lombok.Value; - -import java.io.IOException; -import java.net.URI; -import java.util.function.Consumer; - -import org.springframework.data.release.model.ModuleIteration; -import org.springframework.data.release.utils.Logger; -import org.springframework.util.Assert; -import org.springframework.web.client.HttpClientErrorException; -import org.springframework.web.client.RestOperations; - -import com.fasterxml.jackson.databind.ObjectMapper; - -/** - * A client to interact with Artifactory. - * - * @author Oliver Gierke - * @author Mark Paluch - */ -@RequiredArgsConstructor -class ArtifactoryClient { - - private final RestOperations template; - private final Logger logger; - private final DeploymentProperties properties; - - /** - * Triggers the promotion of the artifacts identified by the given {@link DeploymentInformation}. - * - * @param information must not be {@literal null}. - */ - public void promote(DeploymentInformation information) { - - Assert.notNull(information, "DeploymentInformation must not be null!"); - - ModuleIteration module = information.getModule(); - URI uri = properties.getServer().getPromotionResource(information); - - logger.log(module, "Promoting %s %s from %s to %s.", information.getBuildName(), information.getBuildNumber(), - properties.getStagingRepository(), information.getTargetRepository()); - - try { - template.postForEntity(uri, - new PromotionRequest(information.getTargetRepository(), properties.getStagingRepository()), String.class); - } catch (HttpClientErrorException o_O) { - handle(message -> logger.warn(information.getModule(), message), "Promotion failed!", o_O); - } - } - - public void verify() { - - URI verificationResource = properties.getServer().getVerificationResource(); - - try { - - logger.log("Artifactory", "Verifying authentication using a GET call to %s.", verificationResource); - - template.getForEntity(verificationResource, String.class); - - logger.log("Artifactory", "Authentication verified!"); - - } catch (HttpClientErrorException o_O) { - handle(message -> logger.log("Artifactory Client", message), "Authentication verification failed!", o_O); - throw new IllegalStateException("Authentication verification failed!"); - } - } - - private void handle(Consumer logger, String message, HttpClientErrorException o_O) { - - try { - - logger.accept(message); - - Errors errors = new ObjectMapper().readValue(o_O.getResponseBodyAsByteArray(), Errors.class); - errors.getErrors().forEach(logger); - errors.getMessages().forEach(logger); - - } catch (IOException e) { - o_O.addSuppressed(e); - throw new RuntimeException(o_O.getResponseBodyAsString(), o_O); - } - } - - public void deleteArtifacts(DeploymentInformation information) { - template.delete(properties.getServer().getDeleteBuildResource(information)); - } - - @Value - static class PromotionRequest { - String targetRepo, sourceRepo; - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/deployment/ArtifactoryCommands.java b/release-tools/src/main/java/org/springframework/data/release/deployment/ArtifactoryCommands.java deleted file mode 100644 index 7e537cc..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/deployment/ArtifactoryCommands.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2016-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.deployment; - -import lombok.NonNull; -import lombok.RequiredArgsConstructor; - -import org.springframework.data.release.CliComponent; -import org.springframework.data.release.TimedCommand; -import org.springframework.shell.core.annotation.CliCommand; - -/** - * Commands to interact with Artifactory. - * - * @author Oliver Gierke - */ -@CliComponent -@RequiredArgsConstructor -class ArtifactoryCommands extends TimedCommand { - - private final @NonNull DeploymentOperations deployment; - - @CliCommand(value = "artifactory verify", help = "Verifies authentication at Artifactory.") - public void verify() { - deployment.verifyAuthentication(); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/deployment/DefaultDeploymentInformation.java b/release-tools/src/main/java/org/springframework/data/release/deployment/DefaultDeploymentInformation.java deleted file mode 100644 index 7551cca..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/deployment/DefaultDeploymentInformation.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2015-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.deployment; - -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; - -import java.time.LocalDateTime; -import java.time.ZoneOffset; -import java.util.HashMap; -import java.util.Map; - -import org.springframework.data.release.model.ModuleIteration; -import org.springframework.web.util.UriTemplate; - -/** - * Information about a deployment. - * - * @author Oliver Gierke - */ -@RequiredArgsConstructor(access = AccessLevel.PACKAGE) -public class DefaultDeploymentInformation implements DeploymentInformation { - - private static UriTemplate REPOSITORY_TEMPLATE = new UriTemplate( - "artifactory::default::{server};build.number={buildNumber};build.name={buildName}"); - - private final @Getter @NonNull ModuleIteration module; - private final @NonNull DeploymentProperties properties; - private final @Getter String buildNumber; - - public DefaultDeploymentInformation(ModuleIteration module, DeploymentProperties properties) { - this(module, properties, String.valueOf(LocalDateTime.now().toEpochSecond(ZoneOffset.UTC))); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.release.deployment.DeploymentInformation#getBuildName() - */ - @Override - public String getBuildName() { - return module.getProject().getFullName().concat(" - Release"); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.release.deployment.DeploymentInformation#getTargetRepository() - */ - @Override - public String getTargetRepository() { - return properties.getRepositoryPrefix() - .concat(module.getIteration().isPublic() ? "libs-release-local" : "libs-milestone-local"); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.release.deployment.DeploymentInformation#getDeploymentTargetUrl() - */ - @Override - public String getDeploymentTargetUrl() { - - Map parameters = new HashMap<>(); - parameters.put("server", properties.getStagingRepositoryUrl()); - parameters.putAll(getBuildInfoParameters()); - - return REPOSITORY_TEMPLATE.expand(parameters).toString(); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.release.deployment.DeploymentInformation#getBuildInfoParameters() - */ - @Override - public Map getBuildInfoParameters() { - - Map parameters = new HashMap<>(); - parameters.put("buildNumber", getBuildNumber()); - parameters.put("buildName", getBuildName()); - - return parameters; - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/deployment/DeploymentConfiguration.java b/release-tools/src/main/java/org/springframework/data/release/deployment/DeploymentConfiguration.java deleted file mode 100644 index da88b6d..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/deployment/DeploymentConfiguration.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2015-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.deployment; - -import lombok.NonNull; -import lombok.RequiredArgsConstructor; - -import java.io.IOException; -import java.util.Arrays; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.release.utils.Logger; -import org.springframework.http.HttpRequest; -import org.springframework.http.client.ClientHttpRequestExecution; -import org.springframework.http.client.ClientHttpRequestInterceptor; -import org.springframework.http.client.ClientHttpResponse; -import org.springframework.web.client.RestTemplate; - -/** - * Configuration to set up deployment components. - * - * @author Oliver Gierke - */ -@Configuration(proxyBeanMethods = false) -class DeploymentConfiguration { - - @Autowired DeploymentProperties properties; - - @Bean - public ArtifactoryClient client(Logger logger, RestTemplate artifactoryRestTemplate) { - return new ArtifactoryClient(artifactoryRestTemplate, logger, properties); - } - - @Bean - public RestTemplate artifactoryRestTemplate() { - - RestTemplate template = new RestTemplate(); - template.setInterceptors(Arrays.asList(new AuthenticatingClientHttpRequestInterceptor(properties))); - - return template; - } - - @RequiredArgsConstructor - private static class AuthenticatingClientHttpRequestInterceptor implements ClientHttpRequestInterceptor { - - private final @NonNull DeploymentProperties properties; - - /* - * (non-Javadoc) - * @see org.springframework.http.client.ClientHttpRequestInterceptor#intercept(org.springframework.http.HttpRequest, byte[], org.springframework.http.client.ClientHttpRequestExecution) - */ - @Override - public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) - throws IOException { - - request.getHeaders().add("X-Api-Key", properties.getApiKey()); - request.getHeaders().add("Authentication", properties.getCredentials().toString()); - - return execution.execute(request, body); - } - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/deployment/DeploymentInformation.java b/release-tools/src/main/java/org/springframework/data/release/deployment/DeploymentInformation.java deleted file mode 100644 index 148cccf..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/deployment/DeploymentInformation.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2016-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.deployment; - -import java.util.Map; - -import org.springframework.data.release.model.ModuleIteration; - -/** - * @author Oliver Gierke - */ -public interface DeploymentInformation { - - /** - * Returns the name of the build. - * - * @return will never be {@literal null} or empty. - */ - String getBuildName(); - - /** - * Returns a unique build number for this particular deployment. - * - * @return will never be {@literal null} or empty. - */ - String getBuildNumber(); - - /** - * Returns the full URL to be used as deployment target. - * - * @return will never be {@literal null} or empty. - */ - String getDeploymentTargetUrl(); - - /** - * Returns the name of the repository to deploy to. - * - * @return will never be {@literal null} or empty. - */ - String getTargetRepository(); - - /** - * Returns the {@link ModuleIteration} the deployment information was created for. - * - * @return - */ - ModuleIteration getModule(); - - /** - * Returns a {@link Map} to expand a URI template to access the build information. - * - * @return - */ - Map getBuildInfoParameters(); -} diff --git a/release-tools/src/main/java/org/springframework/data/release/deployment/DeploymentOperations.java b/release-tools/src/main/java/org/springframework/data/release/deployment/DeploymentOperations.java deleted file mode 100644 index e2b2f9a..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/deployment/DeploymentOperations.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2015-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.deployment; - -import lombok.RequiredArgsConstructor; - -import org.springframework.data.release.utils.Logger; -import org.springframework.stereotype.Component; -import org.springframework.util.Assert; - -/** - * Deployment functionality. - * - * @author Oliver Gierke - * @author Mark Paluch - */ -@Component -@RequiredArgsConstructor -public class DeploymentOperations { - - private final ArtifactoryClient client; - private final Logger logger; - - public void verifyAuthentication() { - client.verify(); - } - - /** - * Promotes the artifacts identified by the given {@link DeploymentInformation}. - * - * @param information must not be {@literal null}. - */ - public void promote(DeploymentInformation information) { - - Assert.notNull(information, "DeploymentInformation must not be null!"); - - if (information.getModule().getIteration().isPublic()) { - logger.log(information.getModule(), - "Skipping build promotion as it's a public version and was staged to OSS Sonatype."); - return; - } - - client.promote(information); - } - - /** - * Rolls back the given {@link DeploymentInformation}. - * - * @param information must not be {@literal null}. - */ - public void rollback(DeploymentInformation information) { - - Assert.notNull(information, "DeploymentInformation must not be null!"); - - if (information.getModule().getIteration().isPublic()) { - logger.log(information.getModule(), - "Skipping build rollback as it's a public version and was deployed to Maven Central directly,"); - return; - } - - client.deleteArtifacts(information); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/deployment/DeploymentProperties.java b/release-tools/src/main/java/org/springframework/data/release/deployment/DeploymentProperties.java deleted file mode 100644 index f952e83..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/deployment/DeploymentProperties.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright 2015-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.deployment; - -import lombok.Data; - -import java.net.URI; - -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.data.release.model.Password; -import org.springframework.data.release.utils.HttpBasicCredentials; -import org.springframework.stereotype.Component; -import org.springframework.util.Assert; -import org.springframework.web.util.UriTemplate; - -/** - * @author Oliver Gierke - * @author Mark Paluch - */ -@Data -@Component -@ConfigurationProperties(prefix = "deployment") -public class DeploymentProperties { - - /** - * The Artifactory host. - */ - private Server server; - - /** - * The deployer's username. - */ - private String username; - - private String apiKey; - - /** - * The deployer's password. - */ - private Password password; - - /** - * The repository to deploy the artifacts to. - */ - private String stagingRepository; - - /** - * The repository to deploy docs/schemas to. - */ - private String distributionRepository; - - private String repositoryPrefix = ""; - - public String getStagingRepository() { - return repositoryPrefix.concat(stagingRepository); - } - - public String getDistributionRepository() { - return repositoryPrefix.concat(distributionRepository); - } - - /** - * Returns the URI of the staging repository. - * - * @return - */ - public String getStagingRepositoryUrl() { - return server.getUri().toString().concat("/").concat(stagingRepository); - } - - public HttpBasicCredentials getCredentials() { - return new HttpBasicCredentials(username, password); - } - - @Data - public static class Server { - - private static final String PROMOTION_RESOURCE = "/api/build/promote/{buildName}/{buildNumber}"; - private static final String DELETE_BUILD_RESOURCE = "/api/build/{buildName}?buildNumbers={buildNumber}&artifacts=1"; - private static final String VERIFICATION_RESOURCE = "/api/storage/temp-private-local"; - - private String uri; - - /** - * Returns the URI to the resource that a promotion can be triggered at. - * - * @param information must not be {@literal null}. - * @return - */ - public URI getPromotionResource(DeploymentInformation information) { - - Assert.notNull(information, "DeploymentInformation must not be null!"); - - return new UriTemplate(uri.concat(PROMOTION_RESOURCE)).expand(information.getBuildInfoParameters()); - } - - public URI getDeleteBuildResource(DeploymentInformation information) { - - return new UriTemplate(uri.concat(DELETE_BUILD_RESOURCE)).expand(information.getBuildInfoParameters()); - } - - public URI getVerificationResource() { - return URI.create(uri.concat(VERIFICATION_RESOURCE)); - } - } - -} diff --git a/release-tools/src/main/java/org/springframework/data/release/deployment/Errors.java b/release-tools/src/main/java/org/springframework/data/release/deployment/Errors.java deleted file mode 100644 index fa781a7..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/deployment/Errors.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2015-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.deployment; - -import lombok.Data; - -import java.util.ArrayList; -import java.util.List; - -/** - * @author Oliver Gierke - */ -@Data -public class Errors { - - private List errors = new ArrayList<>(); - private List messages = new ArrayList<>(); - - public List getErrors(Errors this) { - return errors; - } - - @Data - static class Error { - - private String message; - private int status; - - public String toString() { - return String.format("%s - %s", status, message); - } - } - - @Data - static class Message { - - private String level, message; - - public String toString() { - return String.format("%s - %s", level, message); - } - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/git/BackportTargets.java b/release-tools/src/main/java/org/springframework/data/release/git/BackportTargets.java deleted file mode 100644 index 54af987..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/git/BackportTargets.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2016-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.git; - -import lombok.Getter; - -import java.util.Collection; -import java.util.Iterator; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.bouncycastle.util.Iterable; - -import org.springframework.data.release.model.ModuleIteration; -import org.springframework.data.release.model.Train; -import org.springframework.util.Assert; - -class BackportTargets implements Iterable { - - private final @Getter Branch source; - private final Set targets; - - /** - * Creates a new {@link BackportTargets} instance for the given {@link ModuleIteration} and - * - * @param module must not be {@literal null}. - * @param targets must not be {@literal null}. - */ - public BackportTargets(ModuleIteration module, Collection targets) { - - Assert.notNull(module, "Module iteration must not be null!"); - Assert.notNull(targets, "Target trains must not be null!"); - - this.source = Branch.from(module); - - Stream branches = targets.stream().map(target -> target.getModuleIfAvailable(module.getProject()))// - .flatMap(o -> o.map(Stream::of).orElse(Stream.empty()))// - .map(Branch::from); - - this.targets = Stream.concat(branches, source.isMainBranch() ? Stream.empty() : Stream.of(Branch.MAIN)) - .collect(Collectors.toSet()); - } - - /* - * (non-Javadoc) - * @see java.lang.Iterable#iterator() - */ - @Override - public Iterator iterator() { - return targets.iterator(); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/git/Branch.java b/release-tools/src/main/java/org/springframework/data/release/git/Branch.java deleted file mode 100644 index 6b0fc0e..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/git/Branch.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.git; - -import lombok.AccessLevel; -import lombok.EqualsAndHashCode; -import lombok.RequiredArgsConstructor; - -import org.springframework.data.release.model.IterationVersion; -import org.springframework.data.release.model.Tracker; -import org.springframework.data.release.model.Version; -import org.springframework.data.release.model.VersionAware; -import org.springframework.util.Assert; - -/** - * Value type to represent an SCM branch. - * - * @author Oliver Gierke - * @author Mark Paluch - */ -@EqualsAndHashCode -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) -public class Branch implements Comparable { - - public static final Branch MAIN = new Branch("main"); - - private final String name; - - /** - * Creates a new {@link Branch} from the given {@link IterationVersion}. - * - * @param iterationVersion must not be {@literal null}. - * @return - */ - public static Branch from(IterationVersion iterationVersion) { - - Assert.notNull(iterationVersion, "Iteration version must not be null!"); - - if (iterationVersion.isBranchVersion()) { - return from((VersionAware) iterationVersion); - } - - return MAIN; - } - - public static Branch from(VersionAware versioned) { - return from(versioned.getVersion()); - } - - public static Branch from(Version version) { - return from(version.toString().concat(".x")); - } - - /** - * Creates a new {@link Branch} from the given name. Uses the local part of it only. - * - * @param name must not be {@literal null} or empty. - * @return - */ - public static Branch from(String name) { - - int slashIndex = name.lastIndexOf('/'); - - return new Branch(slashIndex != -1 ? name.substring(slashIndex + 1) : name); - } - - public boolean isMainBranch() { - return MAIN.equals(this); - } - - /** - * Returns whether the current branch is an issue branch for the given {@link Tracker}. - * - * @param tracker must not be {@literal null}. - * @return - */ - public boolean isIssueBranch(Tracker tracker) { - - Assert.notNull(tracker, "Tracker must not be null!"); - return name.matches(tracker.getTicketPattern()); - } - - /* - * (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return name; - } - - /* - * (non-Javadoc) - * @see java.lang.Comparable#compareTo(java.lang.Object) - */ - @Override - public int compareTo(Branch o) { - return name.compareToIgnoreCase(o.name); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/git/Branches.java b/release-tools/src/main/java/org/springframework/data/release/git/Branches.java deleted file mode 100644 index 48222c3..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/git/Branches.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2016-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.git; - -import lombok.EqualsAndHashCode; - -import java.util.Iterator; -import java.util.List; -import java.util.stream.Collectors; - -import org.springframework.util.Assert; - -/** - * Value object to represent a collection of {@link Branch}es. - * - * @author Mark Paluch - */ -@EqualsAndHashCode -public class Branches implements Iterable { - - private final List branches; - - /** - * Creates a new {@link Branches} instance for the given {@link List} of {@link Branch}es. - * - * @param source must not be {@literal null}. - */ - Branches(List source) { - - Assert.notNull(source, "Tags must not be null!"); - - this.branches = source.stream().// - sorted().collect(Collectors.toList()); - } - - /** - * Returns all {@link Branch}es as {@link List}. - * - * @return - */ - public List asList() { - return branches; - } - - /* - * (non-Javadoc) - * @see java.lang.Iterable#iterator() - */ - @Override - public Iterator iterator() { - return branches.iterator(); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/git/Commit.java b/release-tools/src/main/java/org/springframework/data/release/git/Commit.java deleted file mode 100644 index 6b819e0..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/git/Commit.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.git; - -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -import java.util.Optional; - -import org.springframework.data.release.issues.Ticket; - -/** - * @author Oliver Gierke - * @author Mark Paluch - */ -@EqualsAndHashCode -@RequiredArgsConstructor -public class Commit { - - private final Ticket ticket; - - @Getter - private final String summary; - private final Optional details; - - /* - * (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - - StringBuilder builder = new StringBuilder(); - - - builder.append(summary); - - if (!summary.endsWith(".")) { - builder.append("."); - } - - details.ifPresent(it -> { - builder.append("\n"); - builder.append("\n"); - builder.append(it); - }); - - builder.append("\n\nSee ").append(ticket.getId()); - - return builder.toString(); - - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/git/GitCommands.java b/release-tools/src/main/java/org/springframework/data/release/git/GitCommands.java deleted file mode 100644 index 7485701..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/git/GitCommands.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.git; - -import lombok.NonNull; -import lombok.RequiredArgsConstructor; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.Executor; -import java.util.stream.Collectors; - -import org.springframework.data.release.CliComponent; -import org.springframework.data.release.TimedCommand; -import org.springframework.data.release.issues.Changelog; -import org.springframework.data.release.issues.IssueTracker; -import org.springframework.data.release.issues.Ticket; -import org.springframework.data.release.issues.TicketReference; -import org.springframework.data.release.issues.Tickets; -import org.springframework.data.release.model.ArtifactVersion; -import org.springframework.data.release.model.ModuleIteration; -import org.springframework.data.release.model.Project; -import org.springframework.data.release.model.Projects; -import org.springframework.data.release.model.ReleaseTrains; -import org.springframework.data.release.model.Train; -import org.springframework.data.release.model.TrainIteration; -import org.springframework.data.release.utils.ExecutionUtils; -import org.springframework.plugin.core.PluginRegistry; -import org.springframework.shell.core.annotation.CliCommand; -import org.springframework.shell.core.annotation.CliOption; -import org.springframework.shell.support.table.Table; -import org.springframework.shell.support.table.TableHeader; -import org.springframework.util.StringUtils; - -/** - * @author Oliver Gierke - */ -@CliComponent -@SuppressWarnings("deprecation") -@RequiredArgsConstructor -class GitCommands extends TimedCommand { - - private final PluginRegistry trackers; - private final @NonNull GitOperations git; - private final @NonNull Executor executor; - - @CliCommand("git co-train") - public void checkout(@CliOption(key = "", mandatory = true) Train train) throws Exception { - git.checkout(train); - } - - @CliCommand("git co") - public void checkout(@CliOption(key = "", mandatory = true) TrainIteration iteration) throws Exception { - git.checkout(iteration); - } - - @CliCommand("git update") - public void update(@CliOption(key = { "", "train" }, mandatory = true) String trainName) - throws Exception, InterruptedException { - git.update(ReleaseTrains.getTrainByName(trainName)); - } - - @CliCommand("git tags") - public String tags(@CliOption(key = { "project" }, mandatory = true) String projectName) throws Exception { - - Project project = ReleaseTrains.getProjectByName(projectName); - - return StringUtils.collectionToDelimitedString(git.getTags(project).asList(), "\n"); - } - - @CliCommand("git previous") - public String previous(@CliOption(key = "", mandatory = true) TrainIteration iteration) throws Exception { - return git.getPreviousIteration(iteration).toString(); - } - - @CliCommand("git changelog") - public String changelog(@CliOption(key = "", mandatory = true) TrainIteration iteration, - @CliOption(key = "module") String moduleName) { - - TrainIteration previousIteration = git.getPreviousIteration(iteration); - - if (StringUtils.hasText(moduleName)) { - - ModuleIteration module = iteration.getModule(Projects.requiredByName(moduleName)); - List ticketRefs = git.getTicketReferencesBetween(module.getProject(), previousIteration, - iteration); - - Changelog changelog = Changelog.of(module, toTickets(module, ticketRefs)); - return String.format("%s %s%n%s", module.getModule().getProject().getFullName(), ArtifactVersion.of(module), - changelog.toString(false, " ")); - } - - return ExecutionUtils - .runAndReturn(executor, iteration, module -> changelog(iteration, module.getModule().getProject().getName())) // - .stream() // - .collect(Collectors.joining("\n")); - } - - private Tickets toTickets(ModuleIteration module, List ticketReferences) { - - IssueTracker issueTracker = trackers.getRequiredPluginFor(module.getProject(), - () -> String.format("No issue tracker found for project %s!", module.getProject())); - - List ticketIds = ticketReferences.stream().map(TicketReference::getId).collect(Collectors.toList()); - - List tickets = new ArrayList<>(issueTracker.findTickets(module, ticketIds).getTickets()); - - return new Tickets(tickets); - } - - /** - * Resets all projects contained in the given {@link Train}. - * - * @param iteration - * @throws Exception - */ - @CliCommand("git reset") - public void reset(@CliOption(key = "", mandatory = true) TrainIteration iteration) throws Exception { - git.reset(iteration); - } - - @CliCommand("git prepare") - public void prepare(@CliOption(key = "", mandatory = true) TrainIteration iteration) throws Exception { - git.prepare(iteration); - } - - /** - * Pushes all changes of all modules of the given {@link TrainIteration} to the remote server. If {@code tags} is - * given, only the tags are pushed. - * - * @param iteration - * @param tags - * @throws Exception - */ - @CliCommand("git push") - public void push(// - @CliOption(key = "", mandatory = true) TrainIteration iteration, // - @CliOption(key = "tags", specifiedDefaultValue = "true", unspecifiedDefaultValue = "false") String tags) - throws Exception { - - boolean pushTags = Boolean.parseBoolean(tags); - - if (pushTags) { - git.pushTags(iteration.getTrain()); - } else { - git.push(iteration); - } - } - - @CliCommand("git remove tags") - public void removeTags(@CliOption(key = "", mandatory = true) TrainIteration iteration) { - git.removeTags(iteration); - } - - /** - * List the branches with their tickets of the git repository. - * - * @param projectName - * @return - * @throws Exception - */ - @CliCommand("git issuebranches") - public Table issuebranches(@CliOption(key = { "" }, mandatory = true) String projectName, - @CliOption(key = "resolved", unspecifiedDefaultValue = "false", specifiedDefaultValue = "true") Boolean resolved) - throws Exception { - - Project project = ReleaseTrains.getProjectByName(projectName); - TicketBranches ticketBranches = git.listTicketBranches(project); - - Table table = new Table(); - table.addHeader(1, new TableHeader("Branch")); - table.addHeader(2, new TableHeader("Status")); - table.addHeader(3, new TableHeader("Description")); - - ticketBranches.stream().sorted().// - filter(branch -> ticketBranches.hasTicketFor(branch, resolved)).// - forEachOrdered(branch -> { - - Optional ticket = ticketBranches.findTicket(branch); - - table.addRow(branch.toString(), // - ticket.map(t -> t.getTicketStatus().getLabel()).orElse(""), // - ticket.map(t -> t.getSummary()).orElse("")); - }); - - return table; - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/git/GitOperations.java b/release-tools/src/main/java/org/springframework/data/release/git/GitOperations.java deleted file mode 100644 index b545f4b..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/git/GitOperations.java +++ /dev/null @@ -1,1131 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.git; - -import lombok.AccessLevel; -import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; -import lombok.experimental.FieldDefaults; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.time.LocalDateTime; -import java.util.*; -import java.util.concurrent.Executor; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; - -import org.apache.commons.io.FileUtils; -import org.eclipse.jgit.api.AddCommand; -import org.eclipse.jgit.api.CheckoutCommand; -import org.eclipse.jgit.api.CommitCommand; -import org.eclipse.jgit.api.CreateBranchCommand.SetupUpstreamMode; -import org.eclipse.jgit.api.Git; -import org.eclipse.jgit.api.LogCommand; -import org.eclipse.jgit.api.ResetCommand.ResetType; -import org.eclipse.jgit.api.errors.EmptyCommitException; -import org.eclipse.jgit.api.errors.RefNotFoundException; -import org.eclipse.jgit.errors.UnsupportedCredentialItem; -import org.eclipse.jgit.lib.ObjectId; -import org.eclipse.jgit.lib.PersonIdent; -import org.eclipse.jgit.lib.Ref; -import org.eclipse.jgit.lib.Repository; -import org.eclipse.jgit.revwalk.RevCommit; -import org.eclipse.jgit.revwalk.RevWalk; -import org.eclipse.jgit.storage.file.FileRepositoryBuilder; -import org.eclipse.jgit.transport.CredentialItem; -import org.eclipse.jgit.transport.CredentialItem.CharArrayType; -import org.eclipse.jgit.transport.CredentialItem.InformationalMessage; -import org.eclipse.jgit.transport.CredentialsProvider; -import org.eclipse.jgit.transport.RefSpec; -import org.eclipse.jgit.transport.TagOpt; -import org.eclipse.jgit.transport.URIish; - -import org.springframework.data.release.io.Workspace; -import org.springframework.data.release.issues.IssueTracker; -import org.springframework.data.release.issues.Ticket; -import org.springframework.data.release.issues.TicketReference; -import org.springframework.data.release.issues.TicketStatus; -import org.springframework.data.release.model.ArtifactVersion; -import org.springframework.data.release.model.Gpg; -import org.springframework.data.release.model.Iteration; -import org.springframework.data.release.model.ModuleIteration; -import org.springframework.data.release.model.Project; -import org.springframework.data.release.model.ProjectAware; -import org.springframework.data.release.model.Projects; -import org.springframework.data.release.model.ReleaseTrains; -import org.springframework.data.release.model.Train; -import org.springframework.data.release.model.TrainIteration; -import org.springframework.data.release.utils.ExecutionUtils; -import org.springframework.data.release.utils.Logger; -import org.springframework.data.util.Pair; -import org.springframework.data.util.Streamable; -import org.springframework.plugin.core.PluginRegistry; -import org.springframework.stereotype.Component; -import org.springframework.util.Assert; - -/** - * Component to execute Git related operations. - * - * @author Oliver Gierke - * @author Mark Paluch - */ -@Component -@RequiredArgsConstructor -@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) -public class GitOperations { - - private enum BranchCheckoutMode { - CREATE_ONLY, CREATE_AND_UPDATE; - } - - GitServer server = new GitServer(); - Executor executor; - Workspace workspace; - Logger logger; - PluginRegistry issueTracker; - GitProperties gitProperties; - Gpg gpg; - - /** - * Returns the {@link GitProject} for the given {@link Project}. - * - * @param project - * @return - */ - public GitProject getGitProject(Project project) { - return new GitProject(project, server); - } - - /** - * Resets the repositories for all modules of the given {@link Train}. - * - * @param train must not be {@literal null}. - */ - public void reset(TrainIteration train) { - - Assert.notNull(train, "Train must not be null!"); - - ExecutionUtils.run(executor, train, module -> { - reset(module.getProject(), Branch.from(module)); - }); - } - - public void checkout(Train train) { - checkout(train, true); - } - - /** - * Checks out all projects of the given {@link Train}. - * - * @param train - * @param update whether to fetch an update from origin. - */ - public void checkout(Train train, boolean update) { - - Assert.notNull(train, "Train must not be null!"); - - if (update) { - update(train); - } - - AtomicBoolean mainSwitch = new AtomicBoolean(); - ExecutionUtils.run(executor, train, module -> { - - Project project = module.getProject(); - - doWithGit(project, git -> { - - ModuleIteration gaIteration = train.getModuleIteration(project, Iteration.GA); - Optional gaTag = findTagFor(project, ArtifactVersion.of(gaIteration)); - - if (!gaTag.isPresent()) { - logger.log(project, "Checking out main branch as no GA release tag could be found!"); - } - - Branch branch = gaTag.isPresent() ? Branch.from(module) : Branch.MAIN; - - CheckoutCommand command = git.checkout().setName(branch.toString()); - - if (!branchExists(project, branch)) { - - logger.log(project, "git checkout -b %s --track origin/%s", branch, branch); - command.setCreateBranch(true)// - .setStartPoint("origin/".concat(branch.toString()))// - .call(); - } else { - - logger.log(project, "git checkout %s", branch); - command.call(); - } - - reset(project, branch); - }); - }); - - if (mainSwitch.get()) { - logger.warn(train, - "Successfully checked out projects. There were switches to main for certain projects. This happens if the train has no branches yet."); - } else { - logger.log(train, "Successfully checked out projects."); - } - - } - - /** - * Checks out all projects of the given {@link TrainIteration}. - * - * @param iteration must not be {@literal null}. - */ - public void checkout(TrainIteration iteration) { - - Assert.notNull(iteration, "Train iteration must not be null!"); - - ExecutionUtils.run(executor, iteration, module -> { - - Project project = module.getProject(); - ArtifactVersion artifactVersion = ArtifactVersion.of(module); - Tag tag = findTagFor(project, artifactVersion).orElseThrow(() -> new IllegalStateException( - String.format("No tag found for version %s of project %s, aborting.", artifactVersion, project))); - - doWithGit(project, git -> { - - logger.log(module, "git checkout %s", tag); - git.checkout().setName(tag.toString()).call(); - }); - }); - - logger.log(iteration, "Successfully checked out projects."); - } - - public void prepare(TrainIteration iteration) { - - ExecutionUtils.run(executor, iteration, module -> { - - Project project = module.getProject(); - Branch branch = Branch.from(module); - - update(project); - checkout(project, branch); - - logger.log(project, "Pulling latest updates for branch %sโ€ฆ", branch); - - doWithGit(project, git -> { - - logger.log(project, "git pull origin %s", branch); - git.pull()// - .setRebase(true)// - .call(); - }); - - logger.log(project, "Pulling updates done!", branch); - }); - - reset(iteration); - } - - public void update(Train train) { - ExecutionUtils.run(executor, train, module -> update(module.getProject())); - } - - public void push(TrainIteration iteration) { - ExecutionUtils.run(executor, iteration, this::push); - } - - public void push(ModuleIteration module) { - - Branch branch = Branch.from(module); - logger.log(module, "git push origin %s", branch); - - if (!branchExists(module.getProject(), branch)) { - - logger.log(module, "No branch %s in %s, skip push", branch, module.getProject().getName()); - return; - } - - doWithGit(module.getProject(), git -> { - - Ref ref = git.getRepository().findRef(branch.toString()); - - git.push()// - .setRemote("origin")// - .setRefSpecs(new RefSpec(ref.getName()))// - .setCredentialsProvider(gitProperties.getCredentials())// - .call(); - }); - } - - public void pushTags(Train train) { - - ExecutionUtils.run(executor, train.getModules(), module -> { - - logger.log(module.getProject(), "git push --tags origin"); - - doWithGit(module.getProject(), git -> { - - git.push()// - .setRemote("origin")// - .setPushTags()// - .setCredentialsProvider(gitProperties.getCredentials())// - .call(); - }); - }); - } - - /** - * Updates the given {@link Project}. Will either pull the latest changes or clone the project's repository if not - * already available. - * - * @param project must not be {@literal null}. - */ - public void update(Project project) { - - Assert.notNull(project, "Project must not be null!"); - - logger.log(project, "Updating projectโ€ฆ"); - - GitProject gitProject = new GitProject(project, server); - String repositoryName = gitProject.getRepositoryName(); - - doWithGit(project, git -> { - - if (workspace.hasProjectDirectory(project)) { - - logger.log(project, "Found existing repository %s. Obtaining latest changesโ€ฆ", repositoryName); - - checkout(project, Branch.MAIN); - - logger.log(project, "git fetch --tags"); - git.fetch().setTagOpt(TagOpt.FETCH_TAGS).call(); - - } else { - clone(project); - } - }); - - logger.log(project, "Project update done!"); - } - - /** - * Updates the given {@link Project} by fetching all tags. - * - * @param project must not be {@literal null}. - */ - public void fetchTags(Project project) { - - Assert.notNull(project, "Project must not be null!"); - - logger.log(project, "Updating project tagsโ€ฆ"); - - GitProject gitProject = new GitProject(project, server); - String repositoryName = gitProject.getRepositoryName(); - - doWithGit(project, git -> { - - if (workspace.hasProjectDirectory(project)) { - - logger.log(project, "Found existing repository %s. Obtaining tagsโ€ฆ", repositoryName); - logger.log(project, "git fetch --tags"); - git.fetch().setTagOpt(TagOpt.FETCH_TAGS).call(); - - } else { - clone(project); - } - }); - - logger.log(project, "Project tags update done!"); - } - - public VersionTags getTags(Project project) { - - return doWithGit(project, git -> { - return new VersionTags(project, git.tagList().call().stream()// - .map(ref -> { - - RevCommit commit = getCommit(git.getRepository(), ref); - - PersonIdent authorIdent = commit.getAuthorIdent(); - Date authorDate = authorIdent.getWhen(); - TimeZone authorTimeZone = authorIdent.getTimeZone(); - LocalDateTime localDate = authorDate.toInstant().atZone(authorTimeZone.toZoneId()).toLocalDateTime(); - - return Tag.of(ref.getName(), localDate); - })// - .collect(Collectors.toList())); - }); - } - - private RevCommit getCommit(Repository repository, Ref ref) { - - return doWithGit(repository, git -> { - - Ref peeledRef = git.getRepository().getRefDatabase().peel(ref); - LogCommand log = git.log(); - if (peeledRef.getPeeledObjectId() != null) { - log.add(peeledRef.getPeeledObjectId()); - } else { - log.add(ref.getObjectId()); - } - - return Streamable.of(log.call()).stream().findFirst() - .orElseThrow(() -> new IllegalStateException("Cannot resolve commit for " + ref)); - }); - } - - /** - * Retrieve a list of remote branches where their related ticket is resolved. - * - * @param project must not be {@literal null}. - * @return - */ - public TicketBranches listTicketBranches(Project project) { - - Assert.notNull(project, "Project must not be null!"); - - IssueTracker tracker = issueTracker.getRequiredPluginFor(project, - () -> String.format("No issue tracker found for project %s!", project)); - - return doWithGit(project, git -> { - - update(project); - - Map ticketIds = getRemoteBranches(project)// - .filter(branch -> branch.isIssueBranch(project.getTracker()))// - .collect(Collectors.toMap(Branch::toString, branch -> branch)); - - Collection tickets = tracker.findTickets(project, ticketIds.keySet()); - - return TicketBranches - .from(tickets.stream().collect(Collectors.toMap(ticket -> ticketIds.get(ticket.getId()), ticket -> ticket))); - }); - } - - /** - * Lookup the previous {@link TrainIteration} from existing tags. - * - * @param trainIteration must not be {@literal null}. - * @return - * @throws IllegalStateException if no previous iteration could be found. - */ - public TrainIteration getPreviousIteration(TrainIteration trainIteration) { - - Assert.notNull(trainIteration, "TrainIteration must not be null!"); - - if (trainIteration.getIteration().isMilestone() && trainIteration.getIteration().getIterationValue() == 1) { - - Train trainToUse = getPreviousTrain(trainIteration); - return trainToUse.getIteration(Iteration.GA); - } - - Optional mostRecentBefore = getTags(Projects.BUILD) // - .filter((tag, ti) -> ti.getTrain().equals(trainIteration.getTrain())) // - .find((tag, iteration) -> iteration.getIteration().compareTo(trainIteration.getIteration()) < 0, - Pair::getSecond); - - return mostRecentBefore.orElseThrow(() -> new IllegalStateException( - "Cannot determine previous iteration for " + trainIteration.getReleaseTrainNameAndVersion())); - } - - public List getTicketReferencesBetween(Project project, TrainIteration from, TrainIteration to) { - - VersionTags tags = getTags(project); - - List ticketReferences = doWithGit(project, git -> { - - Repository repo = git.getRepository(); - - ModuleIteration toModuleIteration = to.getModule(project); - ObjectId fromTag = resolveLowerBoundary(project, from, tags, repo); - ObjectId toTag = resolveUpperBoundary(toModuleIteration, tags, repo); - - Iterable commits = git.log().addRange(fromTag, toTag).call(); - - return StreamSupport.stream(commits.spliterator(), false).flatMap(it -> { - - ParsedCommitMessage message = ParsedCommitMessage.parse(it.getFullMessage()); - - if (message.getTicketReference() == null) { - logger.warn(toModuleIteration, "Commit %s does not refer to a ticket (%s)", it.getName(), - it.getShortMessage()); - return Stream.empty(); - } - - return Stream.of(message.getTicketReference()); - - }).collect(Collectors.toList()); - }); - - // make TicketReference unique - Set uniqueIds = new HashSet<>(); - List uniqueTicketReferences = new ArrayList<>(); - - for (TicketReference reference : ticketReferences) { - if (uniqueIds.add(reference.getId())) { - uniqueTicketReferences.add(reference); - } - } - - uniqueTicketReferences.sort(Comparator. naturalOrder().reversed()); - - return uniqueTicketReferences; - } - - protected ObjectId resolveLowerBoundary(Project project, TrainIteration iteration, VersionTags tags, Repository repo) - throws IOException { - - if (iteration.contains(project)) { - - Optional fromTag = tags.filter(iteration.getTrain()).findTag(iteration.getIteration()); - - if (!fromTag.isPresent()) { - - // fall back to main - return repo.parseCommit(repo.resolve(Branch.MAIN.toString())); - } - - Tag tag = fromTag.orElseThrow(() -> new IllegalStateException( - String.format("Cannot determine from tag for %s %s", project.getName(), iteration))); - - return repo.parseCommit(repo.resolve(tag.getName())); - } - - return repo.resolve(getFirstCommit(repo)); - } - - protected ObjectId resolveUpperBoundary(ModuleIteration iteration, VersionTags tags, Repository repo) - throws IOException { - - Optional tag = tags.filter(iteration.getTrain()).findTag(iteration.getIteration()); - String rangeEnd = tag.map(Tag::getName).orElse(Branch.from(iteration).toString()); - return repo.parseCommit(repo.resolve(rangeEnd)); - } - - private static String getFirstCommit(Repository repo) throws IOException { - - try (RevWalk revWalk = new RevWalk(repo)) { - return revWalk.parseCommit(repo.resolve("main")).getName(); - } - } - - private static Train getPreviousTrain(TrainIteration trainIteration) { - - Train trainToUse = ReleaseTrains.CODD; - - for (Train train : ReleaseTrains.trains()) { - if (train.isBefore(trainIteration.getTrain())) { - trainToUse = train; - } else { - break; - } - } - return trainToUse; - } - - private Stream getRemoteBranches(Project project) { - - return doWithGit(project, git -> { - - Collection refs = git.lsRemote()// - .setHeads(true)// - .setTags(false)// - .call(); - - return refs.stream()// - .map(Ref::getName)// - .map(Branch::from);// - }); - } - - /** - * Tags the release commits for the given {@link TrainIteration}. - * - * @param iteration - */ - public void tagRelease(TrainIteration iteration) { - - Assert.notNull(iteration, "Train iteration must not be null!"); - - ExecutionUtils.run(executor, iteration, module -> { - - Project project = module.getProject(); - ObjectId hash = getReleaseHash(module); - Tag tag = getTags(project).createTag(module); - - doWithGit(project, git -> { - - try (RevWalk walk = new RevWalk(git.getRepository())) { - - RevCommit commit = walk.parseCommit(hash); - - logger.log(module, "git tag %s %s", tag, hash.getName()); - git.tag().setName(tag.toString()).setObjectId(commit).call(); - } - }); - }); - } - - /** - * Commits all changes currently made to all modules of the given {@link TrainIteration}. The summary can contain a - * single {@code %s} placeholder which the version of the current module will get replace into. - * - * @param iteration must not be {@literal null}. - * @param summary must not be {@literal null} or empty. - * @throws Exception - */ - public void commit(TrainIteration iteration, String summary) { - commit(iteration, summary, Optional.empty()); - } - - /** - * Commits all changes currently made to all modules of the given {@link TrainIteration}. The summary can contain a - * single {@code %s} placeholder which the version of the current module will get replace into. - * - * @param iteration must not be {@literal null}. - * @param summary must not be {@literal null} or empty. - * @param details can be {@literal null} or empty. - */ - public void commit(TrainIteration iteration, String summary, Optional details) { - - Assert.notNull(iteration, "Train iteration must not be null!"); - Assert.hasText(summary, "Summary must not be null or empty!"); - - ExecutionUtils.run(executor, iteration, - module -> commit(module, expandSummary(summary, module, iteration), details)); - } - - /** - * Commits the given files for the given {@link ModuleIteration} using the given summary for the commit message. If no - * files are given, all pending changes are committed. - * - * @param module must not be {@literal null}. - * @param summary must not be {@literal null} or empty. - */ - public void commit(ModuleIteration module, String summary) { - commit(module, summary, Optional.empty()); - } - - /** - * Commits the given files for the given {@link ModuleIteration} using the given summary and details for the commit - * message. If no files are given, all pending changes are committed. - * - * @param module must not be {@literal null}. - * @param summary must not be {@literal null} or empty. - * @param details can be {@literal null} or empty. - */ - public void commit(ModuleIteration module, String summary, Optional details) { - - Assert.notNull(module, "Module iteration must not be null!"); - Assert.hasText(summary, "Summary must not be null or empty!"); - - Project project = module.getProject(); - IssueTracker tracker = issueTracker.getRequiredPluginFor(project, - () -> String.format("No issue tracker found for project %s!", project)); - Ticket ticket = tracker.getReleaseTicketFor(module); - - commit(module, ticket, summary, details, true); - } - - /** - * Commits the given files for the given {@link ModuleIteration} using the given summary and details for the commit - * message. If no files are given, all pending changes are committed. - * - * @param module must not be {@literal null}. - * @param summary must not be {@literal null} or empty. - * @param details can be {@literal null} or empty. - */ - public void commit(ModuleIteration module, String summary, Optional details, boolean all) { - - Assert.notNull(module, "Module iteration must not be null!"); - Assert.hasText(summary, "Summary must not be null or empty!"); - - Project project = module.getProject(); - IssueTracker tracker = issueTracker.getRequiredPluginFor(project, - () -> String.format("No issue tracker found for project %s!", project)); - Ticket ticket = tracker.getReleaseTicketFor(module); - - commit(module, ticket, summary, details, all); - } - - /** - * Commits the given files for the given {@link ModuleIteration} using the given summary and details for the commit - * message. If no files are given, all pending changes are committed. - * - * @param module must not be {@literal null}. - * @param summary must not be {@literal null} or empty. - * @param details can be {@literal null} or empty. - */ - public void commit(ProjectAware module, Ticket ticket, String summary, Optional details, boolean all) { - - Assert.notNull(module, "ProjectAware must not be null!"); - - commit(module.getProject(), ticket, summary, details, all); - } - - /** - * Commits the given files for the given {@link ModuleIteration} using the given summary and details for the commit - * message. If no files are given, all pending changes are committed. - * - * @param project must not be {@literal null}. - * @param summary must not be {@literal null} or empty. - * @param details can be {@literal null} or empty. - */ - public void commit(Project project, Ticket ticket, String summary, Optional details, boolean all) { - - Assert.notNull(project, "Project must not be null!"); - Assert.hasText(summary, "Summary must not be null or empty!"); - - Commit commit = new Commit(ticket, summary, details); - String author = gitProperties.getAuthor(); - String email = gitProperties.getEmail(); - boolean allowEmpty = all; - - logger.log(project, "git commit -m \"%s\" %s --author=\"%s <%s>\"", commit.getSummary(), - gpg.isGpgAvailable() ? "-S" + gpg.getKeyname() : "", author, email); - - doWithGit(project, git -> { - - CommitCommand commitCommand = git.commit()// - .setMessage(commit.toString())// - .setAuthor(author, email)// - .setCommitter(author, email)// - .setAllowEmpty(allowEmpty) // - .setAll(all); - - if (gpg.isGpgAvailable()) { - commitCommand.setSign(true).setSigningKey(gpg.getKeyname()) - .setCredentialsProvider(new GpgPassphraseProvider(gpg)); - } else { - commitCommand.setSign(false); - } - - try { - commitCommand.call(); - } catch (EmptyCommitException e) { - // allowed if not all - } - }); - } - - /** - * Adds the {@code filepattern} to the staging area. - * - * @param project must not be {@literal null}. - * @param filepattern must not be {@literal null} or empty. - */ - public void add(Project project, String filepattern) { - - Assert.notNull(project, "Project must not be null!"); - - logger.log(project, "git add \"filepattern\""); - - doWithGit(project, git -> { - - AddCommand commitCommand = git.add()// - .addFilepattern(filepattern); - - commitCommand.call(); - }); - } - - /** - * Checks out the given {@link Branch} of the given {@link Project}. If the given branch doesn't exist yet, a tracking - * branch is created assuming the branch exists in the {@code origin} remote. Pulls the latest changes from the - * checked out branch will be pulled to make sure we see them. - * - * @param project must not be {@literal null}. - * @param branch must not be {@literal null}. - */ - public void checkout(Project project, Branch branch) { - checkout(project, branch, BranchCheckoutMode.CREATE_AND_UPDATE); - } - - /** - * Checks out the given {@link Branch} of the given {@link Project}. If the given branch doesn't exist yet, a tracking - * branch is created assuming the branch exists in the {@code origin} remote. If the {@link BranchCheckoutMode} is set - * to {@code CREATE_AND_UPDATE} the latest changes from the checked out branch will be pulled to make sure we see - * them. - * - * @param project must not be {@literal null}. - * @param branch must not be {@literal null}. - * @param mode must not be {@literal null}. - */ - private void checkout(Project project, Branch branch, BranchCheckoutMode mode) { - - Assert.notNull(project, "Project must not be null!"); - Assert.notNull(branch, "Branch must not be null!"); - - logger.log(project, "Checking out projectโ€ฆ"); - - doWithGit(project, git -> { - - Optional ref = Optional.ofNullable(git.getRepository().findRef(branch.toString())); - CheckoutCommand checkout = git.checkout().setName(branch.toString()); - - if (ref.isPresent()) { - - logger.log(project, "git checkout %s", branch); - - } else { - - logger.log(project, "git checkout --track -b %s origin/%s", branch, branch); - - checkout.setCreateBranch(true)// - .setUpstreamMode(SetupUpstreamMode.TRACK)// - .setStartPoint("origin/".concat(branch.toString())); - } - - try { - checkout.call(); - } catch (RefNotFoundException o_O) { - // TODO: - } - - switch (mode) { - - case CREATE_ONLY: - break; - case CREATE_AND_UPDATE: - default: - // Pull latest changes to make sure the branch is up to date - logger.log(project, "git pull origin %s", branch); - - git.pull()// - .setRemote("origin")// - .setRebase(true) // - .setRemoteBranchName(branch.toString())// - .call(); - break; - } - }); - - logger.log(project, "Checkout done!"); - } - - public void createMaintenanceBranches(TrainIteration iteration) { - - if (!iteration.getIteration().isGAIteration()) { - return; - } - - checkout(iteration); - - ExecutionUtils.run(executor, iteration, module -> { - - Branch branch = createMaintenanceBranch(module); - checkout(module.getProject(), branch, BranchCheckoutMode.CREATE_ONLY); - }); - } - - public void removeTags(TrainIteration iteration) { - - ExecutionUtils.run(executor, iteration, module -> { - - Project project = module.getProject(); - ArtifactVersion artifactVersion = ArtifactVersion.of(module); - - Optional tag = findTagFor(project, artifactVersion); - - if (!tag.isPresent()) { - logger.log(module, "No tag %s found project %s, skipping.", artifactVersion, project); - return; - } - - doWithGit(project, git -> { - - logger.log(module, "git tag -D %s", tag.get()); - git.tagDelete().setTags(tag.get().toString()).call(); - }); - }); - } - - /** - * Verify general Git operations. - */ - @SneakyThrows - public void verify() { - - Project project = Projects.BUILD; - File projectDirectory = workspace.getProjectDirectory(project); - if (projectDirectory.exists()) { - FileUtils.deleteDirectory(projectDirectory); - } - - update(project); - checkout(project, Branch.MAIN); - - commitRandomFile(project, projectDirectory); - - reset(project, Branch.MAIN); - } - - private void commitRandomFile(Project project, File projectDirectory) throws IOException { - - String randomFileName = UUID.randomUUID() + ".txt"; - File randomFile = new File(projectDirectory, randomFileName); - - try (FileOutputStream fos = new FileOutputStream(randomFile)) { - fos.write(randomFileName.getBytes(StandardCharsets.UTF_8)); - } - - commit(project, new Ticket("1234", "Verify", new TicketStatus() { - @Override - public String getLabel() { - return null; - } - - @Override - public boolean isResolved() { - return false; - } - }), "Verify Commit Signing", Optional.empty(), true); - } - - /** - * Creates a version branch for the given {@link ModuleIteration}. - * - * @param module must not be {@literal null}. - * @return - */ - private Branch createMaintenanceBranch(ModuleIteration module) { - - Assert.notNull(module, "Module iteration must not be null!"); - - Branch branch = Branch.from(module.getVersion()); - - doWithGit(module.getProject(), git -> { - logger.log(module, "git checkout -b %s", branch); - git.branchCreate().setName(branch.toString()).call(); - }); - - return branch; - } - - /** - * Returns the {@link ObjectId} of the commit that is considered the release commit. It is identified by the summary - * starting with the release ticket identifier, followed by a dash separated by spaces and the key word - * {@code Release}. To prevent skimming through the entire Git history, we expect such a commit to be found within the - * 50 most recent commits. - * - * @param module - * @return - * @throws Exception - */ - private ObjectId getReleaseHash(ModuleIteration module) { - return findRequiredCommit(module, "Release"); - } - - private ObjectId findRequiredCommit(ModuleIteration module, String summary) { - - Predicate trigger = calculateFilter(module, summary); - - return findCommit(module, summary).orElseThrow(() -> new IllegalStateException(String - .format("Did not find a commit with summary starting with '%s' for project %s", module.getProject(), trigger))); - } - - private Optional findCommit(ModuleIteration module, String summary) { - return findCommit(module.getProject(), calculateFilter(module, summary)); - } - - private Optional findCommit(Project project, Predicate filter) { - - return doWithGit(project, git -> { - - for (RevCommit commit : git.log().setMaxCount(50).call()) { - - if (filter.test(commit)) { - return Optional.of(commit.getId()); - } - } - - return Optional.empty(); - }); - } - - private Predicate calculateFilter(ModuleIteration module, String summary) { - - Project project = module.getProject(); - Ticket releaseTicket = issueTracker - .getRequiredPluginFor(project, () -> String.format("No issue tracker found for project %s!", project))// - .getReleaseTicketFor(module); - - return revCommit -> { - - if (revCommit.getShortMessage().contains(summary) && revCommit.getFullMessage().contains(releaseTicket.getId())) { - return true; - } - - return false; - }; - } - - /** - * Returns the {@link Tag} that represents the {@link ArtifactVersion} of the given {@link Project}. - * - * @param project - * @param version - * @return - * @throws IOException - */ - private Optional findTagFor(Project project, ArtifactVersion version) { - - return getTags(project).stream()// - .filter(tag -> tag.toArtifactVersion().map(it -> it.equals(version)).orElse(false))// - .findFirst(); - } - - private Repository getRepository(Project project) throws IOException { - return FileRepositoryBuilder.create(workspace.getFile(".git", project)); - } - - private void clone(Project project) throws Exception { - - GitProject gitProject = getGitProject(project); - - logger.log(project, "No repository found! Cloning from %sโ€ฆ", gitProject.getProjectUri()); - - Git git = Git.cloneRepository()// - .setURI(gitProject.getProjectUri())// - .setDirectory(workspace.getProjectDirectory(project))// - .call(); - - git.checkout()// - .setName(Branch.MAIN.toString())// - .call(); - - logger.log(project, "Cloning done!", project); - } - - private boolean branchExists(Project project, Branch branch) { - - try (Git git = new Git(getRepository(project))) { - - return git.getRepository().findRef(branch.toString()) != null; - - } catch (Exception o_O) { - throw new RuntimeException(o_O); - } - } - - private void reset(Project project, Branch branch) { - - logger.log(project, "git reset --hard origin/%s", branch); - - doWithGit(project, git -> { - - git.reset()// - .setMode(ResetType.HARD)// - .setRef("origin/".concat(branch.toString()))// - .call(); - }); - } - - private static String expandSummary(String summary, ModuleIteration module, TrainIteration iteration) { - return summary.contains("%s") ? String.format(summary, module.getMediumVersionString()) : summary; - } - - private T doWithGit(Project project, GitCallback callback) { - - try (Git git = new Git(getRepository(project))) { - return callback.doWithGit(git); - } catch (Exception o_O) { - throw new RuntimeException(o_O); - } - } - - private T doWithGit(Repository repository, GitCallback callback) { - - try (Git git = new Git(repository)) { - return callback.doWithGit(git); - } catch (Exception o_O) { - throw new RuntimeException(o_O); - } - } - - private void doWithGit(Project project, VoidGitCallback callback) { - - doWithGit(project, (GitCallback) git -> { - callback.doWithGit(git); - return null; - }); - } - - private interface GitCallback { - T doWithGit(Git git) throws Exception; - } - - private interface VoidGitCallback { - void doWithGit(Git git) throws Exception; - } - - /** - * {@link CredentialsProvider} for GPG Keys used with JGit Commit Signing. - */ - private static class GpgPassphraseProvider extends CredentialsProvider { - - private final Gpg gpg; - - private GpgPassphraseProvider(Gpg gpg) { - this.gpg = gpg; - } - - @Override - public boolean isInteractive() { - return false; - } - - @Override - public boolean supports(CredentialItem... items) { - - boolean matchesKey = matchesKey(items); - boolean hasSettableCharArray = Arrays.stream(items).anyMatch(CharArrayType.class::isInstance); - - return matchesKey && hasSettableCharArray; - } - - private boolean matchesKey(CredentialItem[] items) { - return Arrays.stream(items).filter(InformationalMessage.class::isInstance) // - .map(CredentialItem::getPromptText) // - .map(it -> it.toLowerCase(Locale.US)) // - .anyMatch(it -> it.contains(gpg.getKeyname().toLowerCase(Locale.US))); - } - - @Override - public boolean get(URIish uri, CredentialItem... items) throws UnsupportedCredentialItem { - - if (!matchesKey(items)) { - return false; - } - - for (CredentialItem item : items) { - if (item instanceof CharArrayType) { - ((CharArrayType) item).setValueNoCopy(gpg.getPassword().toString().toCharArray()); - - return true; - } - } - return false; - } - } - - private static class VersionedIterations { - - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/git/GitProject.java b/release-tools/src/main/java/org/springframework/data/release/git/GitProject.java deleted file mode 100644 index 31ae8eb..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/git/GitProject.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.git; - -import lombok.AccessLevel; -import lombok.EqualsAndHashCode; -import lombok.RequiredArgsConstructor; - -import org.springframework.data.release.model.Project; -import org.springframework.data.release.model.Projects; - -/** - * @author Oliver Gierke - */ -@EqualsAndHashCode -@RequiredArgsConstructor(access = AccessLevel.PACKAGE) -public class GitProject { - - private static final String PROJECT_PREFIX = "spring-data"; - - private final Project project; - private final GitServer server; - - public static GitProject of(Project project) { - return new GitProject(project, GitServer.INSTANCE); - } - - /** - * Returns the name of the repository the project is using. - * - * @return - */ - public String getRepositoryName() { - return String.format("%s-%s", PROJECT_PREFIX, - project == Projects.JDBC ? "relational" : project.getName().toLowerCase()); - } - - /** - * Returns the URI of the {@link Project}'s repository. - * - * @return - */ - public String getProjectUri() { - return server.getUri() + getRepositoryName(); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/git/GitProperties.java b/release-tools/src/main/java/org/springframework/data/release/git/GitProperties.java deleted file mode 100644 index 74079db..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/git/GitProperties.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2015-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.git; - -import lombok.AccessLevel; -import lombok.Data; -import lombok.Getter; - -import javax.annotation.PostConstruct; - -import org.eclipse.jgit.transport.CredentialsProvider; -import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.data.release.model.Password; -import org.springframework.data.release.utils.HttpBasicCredentials; -import org.springframework.stereotype.Component; -import org.springframework.util.Assert; - -/** - * Configurable properties for Git. - * - * @author Oliver Gierke - * @author Mark Paluch - */ -@Data -@Component -@ConfigurationProperties(prefix = "git") -public class GitProperties { - - private @Getter(AccessLevel.PRIVATE) Password password; - private String username, author, email; - - @PostConstruct - public void init() { - - Assert.hasText(username, "No GitHub username (git.username) configured!"); - Assert.notNull(password, "No GitHub password (git.password) configured!"); - Assert.hasText(author, "No Git author (git.author) configured!"); - Assert.hasText(email, "No Git email (git.email) configured!"); - } - - /** - * Returns the jGit {@link CredentialsProvider} to be used. - * - * @return - */ - public CredentialsProvider getCredentials() { - return new UsernamePasswordCredentialsProvider(username, password.toString()); - } - - public HttpBasicCredentials getHttpCredentials() { - return new HttpBasicCredentials(username, password); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/git/GitServer.java b/release-tools/src/main/java/org/springframework/data/release/git/GitServer.java deleted file mode 100644 index eb09a0f..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/git/GitServer.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.git; - -/** - * @author Oliver Gierke - */ -public class GitServer { - - public static final GitServer INSTANCE = new GitServer(); - - private static final String SERVER_URI = "https://github.com/spring-projects/"; - - public String getUri() { - return SERVER_URI; - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/git/ParsedCommitMessage.java b/release-tools/src/main/java/org/springframework/data/release/git/ParsedCommitMessage.java deleted file mode 100644 index d5d6124..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/git/ParsedCommitMessage.java +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright 2020-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.git; - -import lombok.Getter; -import lombok.ToString; - -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.Optional; -import java.util.regex.MatchResult; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.springframework.data.release.issues.TicketReference; -import org.springframework.lang.Nullable; - -/** - * Value object representing a parsed commit message. The {@link #parse(String)} method inspects a commit message to - * extract a {@link TicketReference}, related tickets, a pull request reference and summary/body from the commit. Commit - * messages may used {@code <ticket> - summary} syntax for Jira and GitHub tickets (gh- and # notation). This - * parser also supports {@code Original pull request}, {@code Related ticket} and GitHub close keywords. - * - * @author Mark Paluch - */ -@Getter -@ToString -class ParsedCommitMessage { - - private static final Pattern JIRA_TICKET = Pattern.compile("(?>\\[)?([A-Z]+[ ]?-[ ]?\\d+)(?>\\])?"); - private static final Pattern GITHUB_TICKET = Pattern.compile("((?>#|gh-)\\d+)"); - - private static final Pattern GITHUB_CLOSE_SYNTAX = Pattern.compile( - "(?>closes|closed|close|fixes|fixed|fix|resolves|resolved|resolve|see|related to)[\\s:]*((?>#|gh-)\\d+)", - Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); - - private static final Pattern GITHUB_PREFIX_SYNTAX = Pattern.compile("^(#\\d+)"); - - private static final Pattern A_TICKET = Pattern - .compile(String.format("(%s|%s)", JIRA_TICKET.pattern(), GITHUB_TICKET.pattern())); - - private static final Pattern ORIGINAL_PULL_REQUEST = Pattern - .compile("Original (?>pull request|PR|pullrequest)[:]*(?>\\s+)?" + A_TICKET.pattern(), Pattern.CASE_INSENSITIVE); - - private static final Pattern RELATED_TICKET = Pattern.compile( - "Related (?>tickets|ticket)[:]*(?>\\s+)?((" + A_TICKET.pattern() + "(?>[\\s,]*))+)", Pattern.CASE_INSENSITIVE); - - private final String summary; - private final @Nullable String body; - - private final TicketReference ticketReference; - private final TicketReference pullRequestReference; - private final List relatedTickets; - - private ParsedCommitMessage(String summary, @Nullable String body) { - - this.summary = summary; - this.body = body; - - TicketReference ticketReference = null; - TicketReference pullRequestReference = null; - - // DATACASS-nnn - syntax - Optional jiraTicket = tryParseJiraTicketReference(summary); - - if (jiraTicket.isPresent()) { - ticketReference = jiraTicket.get(); - } - - // Closes (gh-nnn|#nnn) syntax - Matcher gitHubMatcher = GITHUB_CLOSE_SYNTAX.matcher(summary + "\n" + body); - - // #nnn syntax - Optional gitHubTicket = tryParseGitHubTicketReference(summary); - - if (gitHubTicket.isPresent()) { - ticketReference = gitHubTicket.get(); - } else { - if (gitHubMatcher.find()) { - ticketReference = new TicketReference(gitHubMatcher.group(1), summary, TicketReference.Style.GitHub); - } - } - - List relatedTickets = parseRelatedTickets(body, gitHubMatcher); - Optional optionalOriginalPr = parsePullRequestReference(body); - - if (optionalOriginalPr.isPresent()) { - - pullRequestReference = optionalOriginalPr.get(); - - if (ticketReference == null) { - ticketReference = pullRequestReference; - pullRequestReference = null; - } - } - - this.ticketReference = ticketReference; - this.pullRequestReference = pullRequestReference; - this.relatedTickets = relatedTickets; - } - - /** - * Parse a commit message into {@link ParsedCommitMessage}. - * - * @param message - * @return - */ - public static ParsedCommitMessage parse(String message) { - - int lineBreak = message.indexOf('\n'); - - String summary; - String body; - - if (lineBreak > -1) { - summary = message.substring(0, lineBreak).trim(); - body = message.substring(lineBreak + 1).trim(); - } else { - summary = message.trim(); - body = null; - } - - return new ParsedCommitMessage(summary, body); - } - - protected static Optional tryParseGitHubTicketReference(String summary) { - - Matcher gitHubPrefixMatcher = GITHUB_PREFIX_SYNTAX.matcher(summary); - - if (gitHubPrefixMatcher.find()) { - - MatchResult mr = gitHubPrefixMatcher.toMatchResult(); - if (mr.start(1) == 0) { - int summaryStart = findSummaryIndex(summary, mr.end(1)); - - return Optional.of(new TicketReference(gitHubPrefixMatcher.group(1).toUpperCase(Locale.ROOT), - summaryStart > -1 ? summary.substring(summaryStart) : summary, TicketReference.Style.GitHub)); - } - } - - return Optional.empty(); - } - - protected static Optional tryParseJiraTicketReference(String summary) { - - Matcher jiraMatcher = JIRA_TICKET.matcher(summary); - - if (jiraMatcher.find()) { - - MatchResult mr = jiraMatcher.toMatchResult(); - - // allow [โ€ฆ] syntax and start of message syntax - if (mr.start(1) < 2) { - int summaryStart = findSummaryIndex(summary, mr.end(1)); - - return Optional.of(new TicketReference(jiraMatcher.group(1).toUpperCase(Locale.ROOT), - summaryStart > -1 ? summary.substring(summaryStart) : summary, TicketReference.Style.Jira)); - } - } - - return Optional.empty(); - } - - protected static Optional parsePullRequestReference(String body) { - - if (body != null) { - - Matcher prMatcher = ORIGINAL_PULL_REQUEST.matcher(body); - - if (prMatcher.find()) { - return extractTicket(prMatcher.group(1)); - } - } - - return Optional.empty(); - } - - protected static List parseRelatedTickets(String body, Matcher gitHubMatcher) { - - List relatedTickets = new ArrayList<>(); - if (body != null) { - Matcher relatedTicketsMatcher = RELATED_TICKET.matcher(body); - - if (relatedTicketsMatcher.find()) { - - String ticketIds[] = relatedTicketsMatcher.group(1).split(","); - - for (String ticketId : ticketIds) { - extractTicket(ticketId).ifPresent(relatedTickets::add); - } - } - - while (gitHubMatcher.find()) { - extractTicket(gitHubMatcher.group(1)).ifPresent(relatedTickets::add); - } - } - - return relatedTickets; - } - - protected static Optional extractTicket(String ticketId) { - - if (JIRA_TICKET.matcher(ticketId.trim()).matches()) { - return Optional.of(new TicketReference(ticketId.trim(), null, TicketReference.Style.Jira)); - } - - if (GITHUB_TICKET.matcher(ticketId.trim()).matches()) { - return Optional.of(new TicketReference(ticketId.trim(), null, TicketReference.Style.GitHub)); - } - - return Optional.empty(); - } - - private static int findSummaryIndex(String summary, int startAt) { - - int dash = summary.indexOf("- ", startAt); - - if (dash > -1) { - return dash + 2; - } - - int space = summary.indexOf(" ", startAt); - - if (space > -1) { - return space + 1; - } - - return -1; - } - -} diff --git a/release-tools/src/main/java/org/springframework/data/release/git/Tag.java b/release-tools/src/main/java/org/springframework/data/release/git/Tag.java deleted file mode 100644 index 4e453b0..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/git/Tag.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.git; - -import lombok.AccessLevel; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -import java.time.LocalDateTime; -import java.util.Optional; - -import org.springframework.data.release.model.ArtifactVersion; - -/** - * Value object to represent an SCM tag. - * - * @author Oliver Gierke - * @author Mark Paluch - */ -@EqualsAndHashCode -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) -@Getter -public class Tag implements Comparable { - - private final String name; - private final LocalDateTime creationDate; - - public static Tag of(String source) { - return of(source, LocalDateTime.now()); - } - - public static Tag of(String source, LocalDateTime creationDate) { - - int slashIndex = source.lastIndexOf('/'); - - return new Tag(source.substring(slashIndex == -1 ? 0 : slashIndex + 1), creationDate); - } - - /** - * Returns the part of the name of the tag that is suitable to derive a version from the tag. Will transparently strip - * a {@code v} prefix from the name. - * - * @return - */ - private String getVersionSource() { - return name.startsWith("v") ? name.substring(1) : name; - } - - public boolean isVersionTag() { - return toArtifactVersion().isPresent(); - } - - public Optional toArtifactVersion() { - - try { - return Optional.of(getRequiredArtifactVersion()); - } catch (IllegalArgumentException o_O) { - return Optional.empty(); - } - } - - public ArtifactVersion getRequiredArtifactVersion() { - return ArtifactVersion.of(getVersionSource()); - } - - /** - * Creates a new {@link Tag} for the given {@link ArtifactVersion} based on the format of the current one. - * - * @param version - * @return - */ - public Tag createNew(ArtifactVersion version) { - return new Tag(name.startsWith("v") ? "v".concat(version.toString()) : version.toString(), LocalDateTime.now()); - } - - /* - * (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return name; - } - - /* - * (non-Javadoc) - * @see java.lang.Comparable#compareTo(java.lang.Object) - */ - @Override - public int compareTo(Tag that) { - - // Prefer artifact versions but fall back to name comparison - - return toArtifactVersion().map(left -> that.toArtifactVersion().map(right -> left.compareTo(right)).// - orElse(name.compareTo(that.name))).orElse(name.compareTo(that.name)); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/git/TicketBranches.java b/release-tools/src/main/java/org/springframework/data/release/git/TicketBranches.java deleted file mode 100644 index 1c08bc9..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/git/TicketBranches.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2016-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.git; - -import lombok.EqualsAndHashCode; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; - -import java.util.Iterator; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Optional; -import java.util.stream.Collectors; - -import org.springframework.data.release.issues.Ticket; -import org.springframework.data.util.Streamable; -import org.springframework.util.Assert; - -/** - * Value object to represent a collection of {@link Branch}es with assigned tickets. - * - * @author Mark Paluch - * @author Oliver Gierke - */ -@EqualsAndHashCode -@RequiredArgsConstructor(staticName = "from") -public class TicketBranches implements Streamable { - - private final @NonNull Map ticketBranches; - - /** - * Returns whether there's a ticket available for the given {@link Branch}. If {@code requireResolved} is set to - * {@literal true} the answer will only be true if the available ticket is marked resolved. - * - * @param branch must not be {@literal null}. - * @param requireResolved whether the {@link Ticket} we look for is required to be resolved. - * @return - */ - public boolean hasTicketFor(Branch branch, boolean requireResolved) { - - Assert.notNull(branch, "Branch must not be null!"); - - return findTicket(branch).// - map(ticket -> requireResolved ? ticket.getTicketStatus().isResolved() : true).// - orElse(false); - } - - /** - * Returns a {@link TicketBranches} containing only the branches for which resolved {@link Ticket}s are found. - * - * @return - */ - public TicketBranches getResolvedTickets() { - - return new TicketBranches(ticketBranches.entrySet().stream().// - filter(entry -> entry.getValue().getTicketStatus().isResolved()).// - collect(Collectors.toMap(Entry::getKey, Entry::getValue))); - } - - /** - * Returns the ticket for the given {@link Branch}. - * - * @param branch must not be {@literal null}. - * @return - */ - public Optional findTicket(Branch branch) { - - Assert.notNull(branch, "Branch must not be null!"); - return Optional.ofNullable(ticketBranches.get(branch)); - } - - /* - * (non-Javadoc) - * @see java.lang.Iterable#iterator() - */ - @Override - public Iterator iterator() { - return ticketBranches.keySet().iterator(); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/git/VersionTags.java b/release-tools/src/main/java/org/springframework/data/release/git/VersionTags.java deleted file mode 100644 index 25307be..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/git/VersionTags.java +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.git; - -import lombok.EqualsAndHashCode; - -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.TreeMap; -import java.util.function.BiPredicate; -import java.util.function.Function; -import java.util.stream.Collectors; - -import org.springframework.data.release.model.ArtifactVersion; -import org.springframework.data.release.model.Iteration; -import org.springframework.data.release.model.ModuleIteration; -import org.springframework.data.release.model.Project; -import org.springframework.data.release.model.Projects; -import org.springframework.data.release.model.ReleaseTrains; -import org.springframework.data.release.model.Train; -import org.springframework.data.release.model.TrainIteration; -import org.springframework.data.util.Pair; -import org.springframework.data.util.Streamable; -import org.springframework.util.Assert; - -/** - * Value object to represent a collection of {@link Tag}s. - * - * @author Oliver Gierke - * @author Mark Paluch - */ -@EqualsAndHashCode -public class VersionTags implements Streamable { - - private final Project project; - private final List tags; - - /** - * Creates a new {@link VersionTags} instance for the given {@link Project} and {@link List} of {@link Tag}s. - * - * @param project must not be {@literal null}. - * @param source must not be {@literal null}. - */ - VersionTags(Project project, Collection source) { - - Assert.notNull(project, "Project must not be null!"); - Assert.notNull(source, "Tags must not be null!"); - - this.project = project; - this.tags = source.stream().// - filter(Tag::isVersionTag).// - sorted().collect(Collectors.toList()); - } - - /** - * Creates an empty {@link VersionTags} object for {@link Project}. - * - * @param project must not be {@literal null}. - * @return - */ - public static VersionTags empty(Project project) { - return new VersionTags(project, Collections.emptyList()); - } - - /** - * Returns the latest {@link Tag}. - * - * @return - */ - public Tag getLatest() { - return tags.isEmpty() ? null : tags.get(tags.size() - 1); - } - - public Tag createTag(ModuleIteration iteration) { - - if (iteration.getProject().equals(Projects.BOM)) { - return Tag.of(iteration.getTrainIteration().getReleaseTrainNameAndVersion()); - } - - Tag latest = getLatest(); - ArtifactVersion version = ArtifactVersion.of(iteration); - - if (latest != null) { - return latest.createNew(version); - } - - return Tag.of(version.toString()); - } - - /** - * Returns all {@link Tag}s as {@link List}. - * - * @return - */ - public List asList() { - return tags; - } - - /* - * (non-Javadoc) - * @see java.lang.Iterable#iterator() - */ - @Override - public Iterator iterator() { - return tags.iterator(); - } - - /** - * Create {@link VersionTags} that contains only tags from the given {@link Train}. - * - * @param train - * @return - */ - public VersionTags filter(Train train) { - return filter((tag, ti) -> ti.getTrain().equals(train)); - } - - /** - * Create {@link VersionTags} from a filtered subset of this {@link VersionTags}. - * - * @param predicate - * @return - */ - public VersionTags filter(BiPredicate predicate) { - - Map iterations = newMap(); - - withIterations().forEach((tag, trainIteration) -> { - - if (predicate.test(tag, trainIteration)) { - iterations.put(tag, trainIteration); - } - }); - - return new VersionTags(project, iterations.keySet()); - } - - Map withIterations() { - - Map iterations = newMap(); - - for (Tag tag : tags) { - - Optional artifactVersion = tag.toArtifactVersion(); - if (!artifactVersion.isPresent()) { - continue; - } - - ArtifactVersion version = artifactVersion.get(); - Optional iteration = toIteration(version); - - if (!iteration.isPresent()) { - continue; - } - - Optional ti = getTrainIteration(version, project, iteration.get()); - - ti.ifPresent(it -> iterations.put(tag, it)); - } - - return iterations; - } - - public Optional findMostRecentTrainIterationBefore(Iteration iterationToFind) { - return find((tag, iteration) -> iteration.getIteration().compareTo(iterationToFind) < 0, Pair::getSecond); - } - - public Optional findTag(Iteration iterationToFind) { - return find((tag, iteration) -> iteration.getIteration().compareTo(iterationToFind) == 0, Pair::getFirst); - } - - public Optional find(BiPredicate filter, - Function, T> resultExtractor) { - - for (Map.Entry entry : withIterations().entrySet()) { - - if (filter.test(entry.getKey(), entry.getValue())) { - return Optional.of(resultExtractor.apply(Pair.of(entry.getKey(), entry.getValue()))); - } - } - - return Optional.empty(); - } - - private static TreeMap newMap() { - return new TreeMap(Comparator.reverseOrder()); - } - - private static Optional getTrainIteration(ArtifactVersion version, Project project, - Iteration iteration) { - - // consider major version bump during ReleaseTrain - boolean minorIncrementedButTargetsNextTrain = false; - - for (Train train : ReleaseTrains.trains()) { - - TrainIteration ti = new TrainIteration(train, iteration); - - if (!ti.contains(project)) { - continue; - } - - if (minorIncrementedButTargetsNextTrain) { - return Optional.of(ti); - } - - ModuleIteration mi = ti.getModule(project); - - ArtifactVersion artifactVersion = ArtifactVersion.of(mi); - - if (artifactVersion.equals(version)) { - return Optional.of(ti); - } - - if (artifactVersion.getNextMinorVersion().equals(version)) { - minorIncrementedButTargetsNextTrain = true; - } - } - - return Optional.empty(); - } - - private static Optional toIteration(ArtifactVersion version) { - - if (version.isMilestoneVersion()) { - return Optional.of(Iteration.valueOf("M" + version.getLevel())); - } - - if (version.isReleaseCandidateVersion()) { - return Optional.of(Iteration.valueOf("RC" + version.getLevel())); - } - - if (version.isBugFixVersion()) { - return Optional.of(Iteration.valueOf("SR" + version.getLevel())); - } - - if (version.isReleaseVersion()) { - return Optional.of(Iteration.GA); - } - - return Optional.empty(); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/infra/Dependencies.java b/release-tools/src/main/java/org/springframework/data/release/infra/Dependencies.java deleted file mode 100644 index 5d52e33..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/infra/Dependencies.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.infra; - -import java.util.ArrayList; -import java.util.List; - -import org.springframework.util.ReflectionUtils; - -/** - * @author Mark Paluch - */ -public class Dependencies { - - static final List dependencies = new ArrayList<>(); - - public static final Dependency APT = Dependency.of("APT", "com.mysema.maven:apt-maven-plugin"); - - public static final Dependency ASPECTJ = Dependency.of("AspectJ", "org.aspectj:aspectjrt"); - - public static final Dependency ASSERTJ = Dependency.of("AssertJ", "org.assertj:assertj-core"); - - public static final Dependency JACKSON = Dependency.of("Jackson", "com.fasterxml.jackson:jackson-bom"); - - public static final Dependency JACOCO = Dependency.of("Jacoco", "org.jacoco:jacoco"); - - public static final Dependency JODA_TIME = Dependency.of("Joda Time", "joda-time:joda-time"); - - public static final Dependency JUNIT5 = Dependency.of("JUnit", "org.junit:junit-bom"); - - public static final Dependency JUNIT4 = Dependency.of("JUnit", "junit:junit"); - - public static final Dependency KOTLIN = Dependency.of("Kotlin", "org.jetbrains.kotlin:kotlin-bom"); - - public static final Dependency KOTLIN_COROUTINES = Dependency.of("Kotlin Coroutines", - "org.jetbrains.kotlinx:kotlinx-coroutines-bom"); - - public static final Dependency MICROMETER = Dependency.of("Micrometer", "io.micrometer:micrometer-bom"); - - public static final Dependency MICROMETER_TRACING = Dependency.of("Micrometer Tracing", - "io.micrometer:micrometer-tracing-bom"); - - public static final Dependency MOCKITO = Dependency.of("Mockito", "org.mockito:mockito-core"); - - public static final Dependency MOCKK = Dependency.of("Mockk", "io.mockk:mockk"); - - public static final Dependency QUERYDSL = Dependency.of("Querydsl", "com.querydsl:querydsl-bom"); - - public static final Dependency RXJAVA1 = Dependency.of("RxJava", "io.reactivex:rxjava"); - - public static final Dependency RXJAVA2 = Dependency.of("RxJava", "io.reactivex.rxjava2:rxjava"); - - public static final Dependency RXJAVA3 = Dependency.of("RxJava", "io.reactivex.rxjava3:rxjava"); - - public static final Dependency RXJAVA_RS = Dependency.of("RxJava Reactive Streams", - "io.reactivex:rxjava-reactive-streams"); - - public static final Dependency SPRING_HATEOAS = Dependency.of("Spring Hateoas", - "org.springframework.hateoas:spring-hateoas"); - - public static final Dependency SPRING_PLUGIN = Dependency.of("Spring Plugin", - "org.springframework.plugin:spring-plugin"); - - public static final Dependency TESTCONTAINERS = Dependency.of("Testcontainers", "org.testcontainers:testcontainers"); - - public static final Dependency THREE_TEN_BP = Dependency.of("ThreeTenBp", "org.threeten:threetenbp"); - - public static final Dependency OPEN_WEB_BEANS = Dependency.of("OpenWebBeans", "org.apache.openwebbeans:openwebbeans"); - - public static final Dependency SMALLRYE_MUTINY = Dependency.of("Smallrye Mutiny", "io.smallrye.reactive:mutiny"); - - public static final Dependency VAVR = Dependency.of("Vavr", "io.vavr:vavr").excludeVersionStartingWith("1.0.0-alpha"); - - public static final Dependency XML_BEAM = Dependency.of("XMLBeam", "org.xmlbeam:xmlprojector"); - - public static final Dependency MONGODB_CORE = Dependency.of("MongoDB", "org.mongodb:mongodb-driver-core"); - - public static final Dependency MONGODB_LEGACY = Dependency.of("MongoDB", "org.mongodb:mongo-java-driver"); - - public static final Dependency MONGODB_SYNC = Dependency.of("MongoDB", "org.mongodb:mongodb-driver-sync"); - - public static final Dependency MONGODB_ASYNC = Dependency.of("MongoDB", "org.mongodb:mongodb-driver-async"); - - public static final Dependency MONGODB_RS = Dependency.of("MongoDB Reactive Streams", - "org.mongodb:mongodb-driver-reactivestreams"); - - public static final Dependency LETTUCE = Dependency.of("Lettuce", "io.lettuce:lettuce-core"); - - public static final Dependency JEDIS = Dependency.of("Jedis", "redis.clients:jedis"); - - public static final Dependency JMOLECULES = Dependency.of("JMolecules", "org.jmolecules:jmolecules"); - - public static final Dependency JMOLECULES_INTEGRATION = Dependency.of("JMolecules", - "org.jmolecules.integrations:jmolecules-spring"); - - public static final Dependency CASSANDRA_DRIVER3 = Dependency.of("Cassandra Driver", - "com.datastax.cassandra:cassandra-driver-core"); - - public static final Dependency CASSANDRA_DRIVER4 = Dependency.of("Cassandra Driver", - "com.datastax.oss:java-driver-bom"); - - public static final Dependency NEO4J_OGM = Dependency.of("Neo4j OGM", "org.neo4j:neo4j-ogm-api"); - - public static final Dependency NEO4J_DRIVER = Dependency.of("Neo4j Driver", "org.neo4j.driver:neo4j-java-driver"); - - public static final Dependency COUCHBASE = Dependency.of("Couchbase Client", "com.couchbase.client:java-client"); - - public static final Dependency ELASTICSEARCH_RHLC = Dependency.of("Elasticsearch", - "org.elasticsearch.client:elasticsearch-rest-high-level-client"); - - public static final Dependency ELASTICSEARCH_REST_CLIENT = Dependency.of("Elasticsearch REST Client", - "org.elasticsearch.client:elasticsearch-rest-client"); - - public static final Dependency SPRING_LDAP = Dependency.of("Spring LDAP", - "org.springframework.ldap:spring-ldap-core"); - - public static final Dependency MAVEN = Dependency.of("Maven Wrapper", "org.apache.maven:apache-maven"); - - static { - - ReflectionUtils.doWithFields(Dependencies.class, field -> { - - // ignore dependencies constant - if (field.getName().toLowerCase().equals(field.getName())) { - return; - } - - dependencies.add((Dependency) ReflectionUtils.getField(field, null)); - }); - } - - public static Dependency getRequiredByName(String name) { - - return dependencies.stream().filter(it -> it.getName().equals(name)).findFirst() - .orElseThrow(() -> new IllegalArgumentException(String.format("No such dependency: %s", name))); - } - - public static Dependency getRequiredDepependency(String groupId, String artifactId) { - - return dependencies.stream().filter(it -> it.getGroupId().equals(groupId) && it.getArtifactId().equals(artifactId)) - .findFirst() - .orElseThrow(() -> new IllegalArgumentException(String.format("No such dependency: %s", artifactId))); - } - -} diff --git a/release-tools/src/main/java/org/springframework/data/release/infra/Dependency.java b/release-tools/src/main/java/org/springframework/data/release/infra/Dependency.java deleted file mode 100644 index 89dddf3..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/infra/Dependency.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.infra; - -import lombok.Value; - -import java.util.function.Predicate; - -import org.springframework.util.Assert; - -/** - * @author Mark Paluch - */ -@Value -public class Dependency implements Comparable { - - String name; - String groupId, artifactId; - Predicate exclusions; - - public static Dependency of(String name, String ga) { - - Assert.hasText(name, "Name must not be empty"); - Assert.hasText(ga, "GroupId/ArtifactId must not be empty"); - Assert.isTrue(ga.indexOf(':') != -1, "GroupId/ArtifactId must be in the format of org.group:artifact-id"); - - String[] parts = ga.split(":"); - - return new Dependency(name, parts[0], parts[1], it -> false); - } - - public Dependency excludeVersionStartingWith(String identifier) { - return new Dependency(name, groupId, artifactId, it -> it.startsWith(identifier) || exclusions.test(it)); - } - - public boolean shouldInclude(String identifier) { - return !exclusions.test(identifier); - } - - @Override - public int compareTo(Dependency o) { - return name.compareTo(o.name); - } - - @Override - public String toString() { - return getGroupId() + ":" + getArtifactId(); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/infra/DependencyCommands.java b/release-tools/src/main/java/org/springframework/data/release/infra/DependencyCommands.java deleted file mode 100644 index 0422738..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/infra/DependencyCommands.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright 2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.infra; - -import lombok.AccessLevel; -import lombok.RequiredArgsConstructor; -import lombok.experimental.FieldDefaults; - -import java.io.FileInputStream; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.TreeMap; -import java.util.stream.Collectors; - -import org.springframework.data.release.CliComponent; -import org.springframework.data.release.TimedCommand; -import org.springframework.data.release.git.GitOperations; -import org.springframework.data.release.issues.Tickets; -import org.springframework.data.release.model.ModuleIteration; -import org.springframework.data.release.model.Project; -import org.springframework.data.release.model.Projects; -import org.springframework.data.release.model.TrainIteration; -import org.springframework.data.release.utils.Logger; -import org.springframework.shell.core.annotation.CliCommand; -import org.springframework.shell.core.annotation.CliOption; -import org.springframework.shell.support.table.Table; - -/** - * Shell commands for dependency management. - * - * @author Mark Paluch - */ -@CliComponent -@RequiredArgsConstructor -@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) -public class DependencyCommands extends TimedCommand { - - public static final String BUILD_PROPERTIES = "dependency-upgrade-build.properties"; - - DependencyOperations operations; - GitOperations git; - Logger logger; - - @CliCommand(value = "dependency check") - public void check(@CliOption(key = "", mandatory = true) TrainIteration iteration, - @CliOption(key = "all", mandatory = false) Boolean reportAll) throws IOException { - - git.prepare(iteration); - - checkBuildDependencies(iteration, reportAll != null ? reportAll : false); - checkModuleDependencies(iteration, reportAll != null ? reportAll : false); - } - - /** - * Retrieve a dependency report for all store modules to be used typically in Spring Boot upgrade tickets. - * - * @param iteration - * @return - */ - @CliCommand(value = "dependency report") - public String report(@CliOption(key = "", mandatory = true) TrainIteration iteration) { - - git.prepare(iteration); - - List projects = Projects.all().stream() - .filter(it -> it != Projects.BOM && it != Projects.BUILD && it != Projects.COMMONS) - .collect(Collectors.toList()); - - Map dependencies = new TreeMap<>(); - - for (Project project : projects) { - operations.getCurrentDependencies(project).forEach(dependencies::put); - } - - StringBuilder report = new StringBuilder(); - - report.append(System.lineSeparator()).append("Project Dependencies Spring Data ") - .append(iteration.getReleaseTrainNameAndVersion()).append(System.lineSeparator()) - .append(System.lineSeparator()); - - dependencies.forEach((dependency, dependencyVersion) -> { - - report.append(String.format("* %s (%s:%s): %s", dependency.getName(), dependency.getGroupId(), - dependency.getArtifactId(), dependencyVersion.getIdentifier())).append(System.lineSeparator()); - }); - - return report.toString(); - } - - @CliCommand(value = "dependency upgrade") - public void upgrade(@CliOption(key = "", mandatory = true) TrainIteration iteration) - throws IOException, InterruptedException { - - logger.log(iteration, "Applying dependency upgrades to Spring Data Build"); - - ModuleIteration module = iteration.getModule(Projects.BUILD); - DependencyVersions dependencyVersions = loadDependencyUpgrades(module); - - DependencyVersions upgradesToApply = operations.getDependencyUpgradesToApply(module.getProject(), - dependencyVersions); - - if (upgradesToApply.isEmpty()) { - logger.log(module, "No dependency upgrades to apply"); - return; - } - - Tickets tickets = operations.getOrCreateUpgradeTickets(module, upgradesToApply); - operations.upgradeDependencies(tickets, module, dependencyVersions); - - git.push(module); - - // Allow GitHub to catch up with ticket notifications. - Thread.sleep(1500); - - operations.closeUpgradeTickets(module, tickets); - } - - private DependencyVersions loadDependencyUpgrades(ModuleIteration iteration) - throws IOException { - - if (!Files.exists(Paths.get(BUILD_PROPERTIES))) { - logger.log(iteration, "Cannot upgrade dependencies: " + BUILD_PROPERTIES + " does not exist."); - } - - Properties properties = new Properties(); - try (FileInputStream fis = new FileInputStream(BUILD_PROPERTIES)) { - properties.load(fis); - } - - return DependencyUpgradeProposals.fromProperties(iteration.getTrainIteration(), properties); - } - - private void checkModuleDependencies(TrainIteration iteration, boolean reportAll) throws IOException { - - String propertiesFile = "dependency-upgrade-modules.properties"; - - List projects = Projects.all().stream().filter(it -> it != Projects.BOM && it != Projects.BUILD) - .collect(Collectors.toList()); - - DependencyUpgradeProposals proposals = DependencyUpgradeProposals.empty(); - - for (Project project : projects) { - proposals = proposals.mergeWith(operations.getDependencyUpgradeProposals(project, iteration.getIteration())); - } - - Files.write(Paths.get(propertiesFile), proposals.asProperties(iteration).getBytes()); - - Table summary = proposals.toTable(reportAll); - - logger.log(iteration, "Upgrade summary:" + System.lineSeparator() + System.lineSeparator() + summary); - logger.log(iteration, "Upgrade proposals written to " + propertiesFile); - } - - private void checkBuildDependencies(TrainIteration iteration, boolean reportAll) throws IOException { - - String propertiesFile = BUILD_PROPERTIES; - - DependencyUpgradeProposals proposals = operations.getDependencyUpgradeProposals(Projects.BUILD, - iteration.getIteration()); - - Files.write(Paths.get(propertiesFile), proposals.asProperties(iteration).getBytes()); - - Table summary = proposals.toTable(reportAll); - - logger.log(Projects.BUILD, "Upgrade summary:" + System.lineSeparator() + System.lineSeparator() + summary); - logger.log(iteration, "Upgrade proposals written to " + propertiesFile); - } - -} diff --git a/release-tools/src/main/java/org/springframework/data/release/infra/DependencyOperations.java b/release-tools/src/main/java/org/springframework/data/release/infra/DependencyOperations.java deleted file mode 100644 index 2268149..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/infra/DependencyOperations.java +++ /dev/null @@ -1,597 +0,0 @@ -/* - * Copyright 2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.infra; - -import lombok.AccessLevel; -import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; -import lombok.experimental.FieldDefaults; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Properties; -import java.util.concurrent.ExecutorService; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.springframework.data.release.build.Pom; -import org.springframework.data.release.git.GitOperations; -import org.springframework.data.release.io.Workspace; -import org.springframework.data.release.issues.IssueTracker; -import org.springframework.data.release.issues.Ticket; -import org.springframework.data.release.issues.TicketOperations; -import org.springframework.data.release.issues.Tickets; -import org.springframework.data.release.model.Iteration; -import org.springframework.data.release.model.ModuleIteration; -import org.springframework.data.release.model.Project; -import org.springframework.data.release.model.Projects; -import org.springframework.data.release.model.TrainIteration; -import org.springframework.data.release.utils.ExecutionUtils; -import org.springframework.data.release.utils.Logger; -import org.springframework.data.util.Streamable; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; -import org.springframework.web.client.HttpClientErrorException; -import org.springframework.web.client.RestOperations; - -import org.xmlbeam.ProjectionFactory; -import org.xmlbeam.annotation.XBRead; -import org.xmlbeam.io.XBFileIO; -import org.xmlbeam.io.XBStreamInput; - -/** - * Operations for dependency management. - * - * @author Mark Paluch - */ -@Component -@RequiredArgsConstructor -@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) -public class DependencyOperations { - - public static final Pattern REPO_MAVEN_ORG_DIR_LISTING = Pattern - .compile("[^>]+)>([^\\/]+)\\/<\\/a>(?>\\s*)(\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2})(?>\\s*)(?>-)"); - - public static final DateTimeFormatter DIR_LISTING_TIME_FORMAT = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm"); - - private final ProjectionFactory projectionFactory; - private final GitOperations gitOperations; - private final Workspace workspace; - private final TicketOperations tickets; - private final ExecutorService executor; - private final RestOperations restOperations; - private final Logger logger; - - /** - * Obtain dependency upgrade proposals for {@link Project} and {@link Iteration}. Considers dependency upgrade rules - * according to minor/bugfix release increments. Also, SemVer with modifier are only allowed in milestone versions. - * - * @param project - * @param iteration - * @return - */ - public DependencyUpgradeProposals getDependencyUpgradeProposals(Project project, Iteration iteration) { - - DependencyVersions currentDependencies = getCurrentDependencies(project); - Map proposals = Collections.synchronizedMap(new LinkedHashMap<>()); - - ExecutionUtils.run(executor, Streamable.of(currentDependencies.getDependencies()), dependency -> { - - DependencyVersion currentVersion = currentDependencies.get(dependency); - List versions = getAvailableVersions(dependency); - DependencyUpgradeProposal proposal = getDependencyUpgradeProposal(DependencyUpgradePolicy.from(iteration), - currentVersion, versions); - - proposals.put(dependency, proposal); - }); - - return new DependencyUpgradeProposals(proposals); - } - - /** - * Obtain dependency upgrade proposals for Maven Wrapper. - * - * @param project - * @param iteration - * @return - */ - public DependencyUpgradeProposals getMavenWrapperDependencyUpgradeProposals(TrainIteration iteration) { - - for (ModuleIteration moduleIteration : iteration) { - - // ensure we have Maven Wrapper for each project. - getMavenWrapperVersion(moduleIteration.getProject()); - } - - return getDependencyUpgradeProposals(Projects.BUILD, DependencyUpgradePolicy.LATEST_STABLE, Dependencies.MAVEN, - this::getMavenWrapperVersion); - } - - /** - * Applies the upgrade and creates a commit. - * - * @param tickets - * @param module - * @param dependencyVersions - */ - public Tickets upgradeMavenWrapperVersion(Tickets tickets, ModuleIteration module, - DependencyVersions dependencyVersions) { - - if (dependencyVersions.isEmpty()) { - logger.log(module, "No dependency upgrades to apply"); - } - - return doWithDependencyVersionsAndCommit(tickets, module, dependencyVersions, (dependency, version) -> { - upgradeMavenWrapperVersion(module.getProject(), version); - }); - } - - public List getProjectsToUpgradeMavenWrapper(DependencyVersion targetVersion, TrainIteration iteration) { - - List projectsToUpgrade = new ArrayList<>(); - - for (ModuleIteration moduleIteration : iteration) { - - DependencyVersion currentVersion = getMavenWrapperVersion(moduleIteration.getProject()); - - if (targetVersion.isNewer(currentVersion)) { - projectsToUpgrade.add(moduleIteration.getProject()); - } - } - - return projectsToUpgrade; - } - - private DependencyVersion getMavenWrapperVersion(Project project) { - - try { - - File file = getMavenWrapperProperties(project); - String distributionUrl; - - try (FileInputStream fileInputStream = new FileInputStream(file)) { - - Properties properties = new Properties(); - properties.load(fileInputStream); - - distributionUrl = properties.getProperty("distributionUrl"); - - } - - Pattern versionPattern = Pattern.compile(".*/maven2/org/apache/maven/apache-maven/([\\d\\.]+)/.*"); - - Matcher matcher = versionPattern.matcher(distributionUrl); - if (!matcher.find()) { - throw new IllegalStateException( - String.format("Invalid distribution URL in %s: %s", project.getName(), distributionUrl)); - } - - return DependencyVersion.of(matcher.group(1)); - } catch (IOException e) { - throw new IllegalStateException(e); - } - } - - private void upgradeMavenWrapperVersion(Project project, DependencyVersion dependencyVersion) { - - String distributionUrlTemplate = "https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/%s/apache-maven-%s-bin.zip"; - - try { - - File file = getMavenWrapperProperties(project); - Properties properties = new Properties(); - - try (FileInputStream is = new FileInputStream(file)) { - properties.load(is); - } - - properties.setProperty("distributionUrl", - String.format(distributionUrlTemplate, dependencyVersion.getIdentifier(), dependencyVersion.getIdentifier())); - - try (FileOutputStream os = new FileOutputStream(file)) { - properties.store(os, null); - } - - } catch (IOException e) { - throw new IllegalStateException(e); - } - } - - private File getMavenWrapperProperties(Project project) throws FileNotFoundException { - File file = workspace.getFile(".mvn/wrapper/maven-wrapper.properties", project); - - if (!file.exists()) { - throw new FileNotFoundException(file.toString()); - } - return file; - } - - public DependencyUpgradeProposals getDependencyUpgradeProposals(Project project, DependencyUpgradePolicy policy, - Dependency dependency, Function currentVersionExtractor) { - - DependencyVersions currentDependencies = new DependencyVersions( - Collections.singletonMap(dependency, currentVersionExtractor.apply(project))); - Map proposals = Collections.synchronizedMap(new LinkedHashMap<>()); - - DependencyVersion currentVersion = currentDependencies.get(dependency); - List versions = getAvailableVersions(dependency); - DependencyUpgradeProposal proposal = getDependencyUpgradeProposal(policy, currentVersion, versions); - - proposals.put(dependency, proposal); - - return new DependencyUpgradeProposals(proposals); - } - - /** - * Ensures there's a upgrade ticket for each dependency to upgrade. - * - * @param module - * @param dependencyVersions - * @return - */ - public Tickets getOrCreateUpgradeTickets(ModuleIteration module, DependencyVersions dependencyVersions) { - - List summaries = new ArrayList<>(); - dependencyVersions.forEach((dependency, dependencyVersion) -> { - summaries.add(getUpgradeTicketSummary(dependency, dependencyVersion)); - }); - - return tickets.getOrCreateTicketsWithSummary(module, IssueTracker.TicketType.DependencyUpgrade, summaries); - } - - /** - * Applies the upgrade, creates a commit. - * - * @param tickets - * @param module - * @param dependencyVersions - */ - public Tickets upgradeDependencies(Tickets tickets, ModuleIteration module, DependencyVersions dependencyVersions) { - - Project project = module.getProject(); - ProjectDependencies dependencies = ProjectDependencies.get(project); - - if (dependencyVersions.isEmpty()) { - logger.log(module, "No dependency upgrades to apply"); - } - - return doWithDependencyVersionsAndCommit(tickets, module, dependencyVersions, (dependency, version) -> { - - String versionProperty = dependencies.getVersionPropertyFor(dependency); - File pom = getPomFile(project); - update(pom, Pom.class, it -> { - it.setProperty(versionProperty, version.getIdentifier()); - }); - - }); - } - - /** - * Verifies dependencies to upgrade, applies the upgrade, creates a commit. - * - * @param tickets - * @param module - * @param dependencyVersions - */ - private Tickets doWithDependencyVersionsAndCommit(Tickets tickets, ModuleIteration module, - DependencyVersions dependencyVersions, BiConsumer action) { - - if (dependencyVersions.isEmpty()) { - logger.log(module, "No dependency upgrades to apply"); - } - - List ticketsToClose = new ArrayList<>(); - - dependencyVersions.forEach((dependency, dependencyVersion) -> { - - String upgradeTicketSummary = getUpgradeTicketSummary(dependency, dependencyVersion); - Ticket upgradeTicket = getDependencyUpgradeTicket(tickets, upgradeTicketSummary).get(); - - action.accept(dependency, dependencyVersion); - - gitOperations.commit(module, upgradeTicket, upgradeTicketSummary, Optional.empty(), true); - - ticketsToClose.add(upgradeTicket); - }); - - return new Tickets(ticketsToClose); - } - - public void closeUpgradeTickets(ModuleIteration module, Tickets tickets) { - this.tickets.closeTickets(module, tickets); - } - - public DependencyVersions getDependencyUpgradesToApply(Project project, DependencyVersions dependencyVersions) { - - DependencyVersions currentDependencies = getCurrentDependencies(project); - Map upgrades = new LinkedHashMap<>(); - - currentDependencies.forEach((dependency, dependencyVersion) -> { - - if (!dependencyVersions.hasDependency(dependency)) { - return; - } - - DependencyVersion upgradeVersion = dependencyVersions.get(dependency); - - if (upgradeVersion.equals(dependencyVersion)) { - logger.log(project, "Skipping upgrade of %s (%s)", dependency.getName(), dependencyVersion.getIdentifier()); - return; - } - - upgrades.put(dependency, upgradeVersion); - }); - - return new DependencyVersions(upgrades); - } - - private Optional getDependencyUpgradeTicket(Tickets tickets, String upgradeTicketSummary) { - - List upgradeTickets = tickets.filter(it -> it.getSummary().equals(upgradeTicketSummary)).toList(); - - if (upgradeTickets.size() > 1) { - throw new IllegalStateException("Multiple upgrade tickets found: " + upgradeTickets); - } - - return Optional.ofNullable(upgradeTickets.isEmpty() ? null : upgradeTickets.get(0)); - } - - protected static DependencyUpgradeProposal getDependencyUpgradeProposal(DependencyUpgradePolicy policy, - DependencyVersion currentVersion, List allVersions) { - - Optional latestMinor = findLatestMinor(policy, currentVersion, allVersions); - Optional latest = findLatest(policy, allVersions); - List newerVersions = allVersions.stream() // - .sorted() // - .filter(it -> it.compareTo(currentVersion) > 0) // - .collect(Collectors.toList()); - - DependencyVersion latestToUse = latest.filter(it -> it.isNewer(currentVersion)).orElse(currentVersion); - - DependencyVersion latestMinorFallback = latest - .filter(it -> isUpgradeable(policy, it, currentVersion) && it.isNewer(currentVersion)).orElse(currentVersion); - - return DependencyUpgradeProposal.of(policy, currentVersion, latestMinor.orElse(latestMinorFallback), latestToUse, - newerVersions); - } - - private static boolean isUpgradeable(DependencyUpgradePolicy policy, DependencyVersion proposal, - DependencyVersion currentVersion) { - - if (policy.restrictToMinorVersion()) { - - if (proposal.getTrainName() != null && currentVersion.getTrainName() != null) { - return proposal.getTrainName().equals(currentVersion.getTrainName()); - } - - if (proposal.getVersion().getMajor() == currentVersion.getVersion().getMajor() - && proposal.getVersion().getMinor() == currentVersion.getVersion().getMinor()) { - return true; - } - - return false; - } - - if (StringUtils.hasText(proposal.getModifier())) { - return policy.milestoneAllowed(); - } - - return true; - } - - private static Optional findLatest(DependencyUpgradePolicy policy, - List availableVersions) { - - return availableVersions.stream().filter(it -> { - - if (StringUtils.hasText(it.getModifier())) { - return policy.milestoneAllowed(); - } - - return true; - - }).max(DependencyVersion::compareTo); - } - - private static Optional findLatestMinor(DependencyUpgradePolicy policy, - DependencyVersion currentVersion, List availableVersions) { - - return availableVersions.stream().filter(it -> { - - if (StringUtils.hasText(it.getModifier())) { - return policy.milestoneAllowed(); - } - - if (it.getVersion() == null || currentVersion.getVersion() == null) { - return false; - } - - if (it.getTrainName() != null && currentVersion.getTrainName() != null) { - return it.getTrainName().equals(currentVersion.getTrainName()); - } - - if (it.getVersion().getMajor() == currentVersion.getVersion().getMajor() - && it.getVersion().getMinor() == currentVersion.getVersion().getMinor()) { - return true; - } - - return false; - }) // - .max(DependencyVersion::compareTo); - } - - DependencyVersions getCurrentDependencies(Project project) { - - if (!ProjectDependencies.containsProject(project)) { - return DependencyVersions.empty(); - } - - File pom = getPomFile(project); - ProjectDependencies dependencies = ProjectDependencies.get(project); - - return doWithPom(pom, Pom.class, it -> { - - Map versions = new LinkedHashMap<>(); - - for (ProjectDependencies.ProjectDependency projectDependency : dependencies) { - - Dependency dependency = projectDependency.getDependency(); - - if (!((project == Projects.MONGO_DB && projectDependency.getProperty().equals("mongo.reactivestreams")) - || project == Projects.NEO4J || project == Projects.BUILD)) { - - if (it.getDependencyVersion(dependency.getArtifactId()) == null - && it.getManagedDependency(dependency.getArtifactId()) == null) { - continue; - } - } - - String value = it.getProperty(projectDependency.getProperty()); - - if (value != null && !value.contains("${")) { - versions.put(dependency, DependencyVersion.of(value)); - } - } - - return new DependencyVersions(versions); - }); - } - - private File getPomFile(Project project) { - return workspace.getFile(project == Projects.BUILD ? "parent/pom.xml" : "pom.xml", project); - } - - @SneakyThrows - List getAvailableVersions(Dependency dependency) { - - String baseUrl = String.format("https://repo1.maven.org/maven2/%s/%s/", dependency.getGroupId().replace('.', '/'), - dependency.getArtifactId()); - - try { - ResponseEntity mavenMetadata = restOperations.getForEntity(baseUrl + "/maven-metadata.xml", byte[].class); - ResponseEntity directoryListing = restOperations.getForEntity(baseUrl, String.class); - - Map creationDates = parseCreationDates(directoryListing.getBody()); - - XBStreamInput io = projectionFactory.io().stream(new ByteArrayInputStream(mavenMetadata.getBody())); - - MavenMetadata metadata = io.read(MavenMetadata.class); - - return metadata.getVersions().stream().filter(dependency::shouldInclude).flatMap(s -> { - - try { - return Stream.of(DependencyVersion.of(s)); - } catch (Exception e) { - logger.log(dependency.toString(), "Cannot parse dependency version " + s); - return Stream.empty(); - } - }).map(it -> { - - if (creationDates.containsKey(it.getIdentifier())) { - return it.withCreatedAt(creationDates.get(it.getIdentifier())); - } - - return it; - - }).collect(Collectors.toList()); - - } catch (Exception o_O) { - - if (o_O instanceof HttpClientErrorException) { - if (((HttpClientErrorException) o_O).getStatusCode() == HttpStatus.NOT_FOUND) { - return Collections.emptyList(); - } - } - - throw new RuntimeException(String.format("Cannot determine available versions for %s", dependency), o_O); - } - } - - private Map parseCreationDates(String body) { - - Map creationDates = new LinkedHashMap<>(); - Matcher matcher = REPO_MAVEN_ORG_DIR_LISTING.matcher(body); - - while (matcher.find()) { - - String version = matcher.group(1); - LocalDateTime creationDate = LocalDateTime.from(DIR_LISTING_TIME_FORMAT.parse(matcher.group(2))); - creationDates.put(version, creationDate); - } - - return creationDates; - } - - private R doWithPom(File file, Class type, Function callback) { - - XBFileIO io = projectionFactory.io().file(file); - - try { - - T pom = (T) io.read(type); - return callback.apply(pom); - - } catch (Exception o_O) { - throw new RuntimeException(o_O); - } - } - - private void update(File file, Class type, Consumer callback) { - - XBFileIO io = projectionFactory.io().file(file); - - try { - - T pom = (T) io.read(type); - callback.accept(pom); - io.write(pom); - - } catch (Exception o_O) { - throw new RuntimeException(o_O); - } - } - - private static String getUpgradeTicketSummary(Dependency dependency, DependencyVersion dependencyVersion) { - return String.format("Upgrade to %s %s", dependency.getName(), dependencyVersion.getIdentifier()); - } - - public interface MavenMetadata { - - @XBRead("/metadata/versioning/versions/version/text()") - List getVersions(); - - } - -} diff --git a/release-tools/src/main/java/org/springframework/data/release/infra/DependencyUpgradePolicy.java b/release-tools/src/main/java/org/springframework/data/release/infra/DependencyUpgradePolicy.java deleted file mode 100644 index 1975359..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/infra/DependencyUpgradePolicy.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.infra; - -import org.springframework.data.release.model.Iteration; - -/** - * Upgrade policy expressing rules how dependency upgrades should happen. - * - * @author Mark Paluch - */ -interface DependencyUpgradePolicy { - - DependencyUpgradePolicy LATEST_STABLE = new DependencyUpgradePolicy() { - - @Override - public boolean milestoneAllowed() { - return false; - } - - @Override - public boolean restrictToMinorVersion() { - return false; - } - }; - - /** - * @return {@code true} if the use of pre-release dependency versions is allowed. - */ - boolean milestoneAllowed(); - - /** - * @return {@code true} if upgrades only within the minor version (e.g. bugfixes/patch releases) are allowed. - */ - boolean restrictToMinorVersion(); - - /** - * Create a upgrade policy from a {@link Iteration}. - * - * @param iteration - * @return - */ - static DependencyUpgradePolicy from(Iteration iteration) { - - return new DependencyUpgradePolicy() { - - @Override - public boolean milestoneAllowed() { - return iteration.isMilestone() || iteration.isReleaseCandidate(); - } - - @Override - public boolean restrictToMinorVersion() { - return iteration.isPublic(); - } - - }; - } - -} diff --git a/release-tools/src/main/java/org/springframework/data/release/infra/DependencyUpgradeProposal.java b/release-tools/src/main/java/org/springframework/data/release/infra/DependencyUpgradeProposal.java deleted file mode 100644 index eb1d7d9..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/infra/DependencyUpgradeProposal.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.infra; - -import lombok.RequiredArgsConstructor; -import lombok.Value; - -import java.util.List; -import java.util.stream.Collectors; - -/** - * @author Mark Paluch - */ -@Value -@RequiredArgsConstructor -class DependencyUpgradeProposal { - - DependencyVersion current, latest, latestMinor, proposal; - List newerVersions; - - public static DependencyUpgradeProposal of(DependencyUpgradePolicy policy, DependencyVersion currentVersion, - DependencyVersion latestMinor, DependencyVersion latest, List newerVersions) { - - if (policy.restrictToMinorVersion()) { - return new DependencyUpgradeProposal(currentVersion, latest, latestMinor, latestMinor, newerVersions); - } - - return new DependencyUpgradeProposal(currentVersion, latest, latestMinor, latest, newerVersions); - } - - public String getNewVersions(boolean includeAll, boolean includeDate) { - - if (includeAll) { - return getNewerVersions().stream().map(dependencyVersion -> { - - if (includeDate && dependencyVersion.getCreatedAt() != null) { - return String.format("%s (%s)", dependencyVersion.getIdentifier(), - dependencyVersion.getCreatedAt().toLocalDate()); - } - - return dependencyVersion.getIdentifier(); - }).collect(Collectors.joining(", ")); - } - - if (latestMinor.toString().equals(latest.toString())) { - return latest.toString(); - } - - return String.format("%s, %s", latestMinor, latest); - } - - public boolean isUpgradeAvailable() { - return !getCurrent().getIdentifier().equals(getProposal().getIdentifier()); - } - - @Override - public String toString() { - return getProposal().getIdentifier(); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/infra/DependencyUpgradeProposals.java b/release-tools/src/main/java/org/springframework/data/release/infra/DependencyUpgradeProposals.java deleted file mode 100644 index 2f0d15c..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/infra/DependencyUpgradeProposals.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright 2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.infra; - -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Properties; -import java.util.TreeMap; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.fusesource.jansi.Ansi; - -import org.springframework.data.release.model.TrainIteration; -import org.springframework.shell.support.table.Table; -import org.springframework.shell.support.table.TableHeader; - -/** - * Value object capturing upgrade proposals for each {@link Dependency}. - * - * @author Mark Paluch - */ -public class DependencyUpgradeProposals { - - private final Map proposals; - - public DependencyUpgradeProposals(Map proposals) { - this.proposals = proposals; - } - - /** - * Create an empty {@link DependencyUpgradeProposal} object. - * - * @return - */ - public static DependencyUpgradeProposals empty() { - return new DependencyUpgradeProposals(Collections.emptyMap()); - } - - /** - * Create a new {@link DependencyUpgradeProposal} by merging this and {@code other}. - * - * @param other - * @return - */ - public DependencyUpgradeProposals mergeWith(DependencyUpgradeProposals other) { - - Map proposals = new TreeMap<>(this.proposals); - proposals.putAll(other.proposals); - - return new DependencyUpgradeProposals(proposals); - } - - /** - * Create a tabular summary including {@link Ansi} escapes. - * - * @param includeAll - * @return - */ - public Table toTable(boolean includeAll) { - - Table table = new Table(); - table.addHeader(1, new TableHeader("Dependency")); - table.addHeader(2, new TableHeader("Current")); - table.addHeader(3, new TableHeader("Available")); - table.addHeader(4, new TableHeader("Proposed")); - - proposals.forEach((dependency, proposal) -> { - - boolean updateAvailable = proposal.isUpgradeAvailable(); - - String s = updateAvailable - ? Ansi.ansi().fg(Ansi.Color.MAGENTA).a(proposal.getProposal()).fg(Ansi.Color.GREEN).toString() - : proposal.getProposal().toString(); - - if (includeAll || updateAvailable) { - table.addRow(dependency.getName(), proposal.getCurrent().toString(), proposal.getNewVersions(includeAll, false), - s); - } - }); - - return table; - } - - /** - * Return the upgrade proposal as {@link java.util.Properties} representation. - * - * @param iteration - * @return - */ - public String asProperties(TrainIteration iteration) { - - StringBuilder builder = new StringBuilder(); - - builder.append("dependency.train=").append(iteration.getTrain().getName()).append(System.lineSeparator()); - builder.append("dependency.iteration=").append(iteration.getIteration().getName()).append(System.lineSeparator()); - builder.append(System.lineSeparator()); - - builder.append("# Specify the number of desired dependency upgrades as sanity check") - .append(System.lineSeparator()); - builder.append("dependency.upgrade.count=").append(System.lineSeparator()); - - proposals.forEach((dependency, proposal) -> { - - boolean updateAvailable = proposal.isUpgradeAvailable(); - - if (updateAvailable) { - builder.append(System.lineSeparator()); - builder.append(String.format("# %s - Available versions: ", dependency.getName())) - .append(proposal.getNewVersions(true, true)).append(System.lineSeparator()); - - builder.append( - String.format("dependency[%s\\:%s]=%s", dependency.getGroupId(), dependency.getArtifactId(), proposal)); - builder.append(System.lineSeparator()); - } - }); - - return builder.toString(); - } - - /** - * Create a dependency upgrade map by parsing {@link Properties}. - * - * @param iteration - * @param properties - * @return - */ - public static DependencyVersions fromProperties(TrainIteration iteration, Properties properties) { - - Pattern keyPattern = Pattern.compile("dependency\\[([a-zA-Z0-9\\-\\.]+):([a-zA-Z0-9\\-\\.]+)\\]"); - - String verificationTrain = properties.getProperty("dependency.train", ""); - String verificationIteration = properties.getProperty("dependency.iteration", ""); - int expectedUpgradeCount; - try { - expectedUpgradeCount = Integer.parseInt(properties.getProperty("dependency.upgrade.count", "0")); - } catch (NumberFormatException e) { - throw new IllegalArgumentException("Please specify a valid dependency.upgrade.count"); - } - - if (!verificationTrain.equals(iteration.getTrain().getName()) - || !verificationIteration.equals(iteration.getIteration().getName())) { - throw new IllegalArgumentException( - String.format("Verification failed: Dependency upgrade descriptor reports %s %s", verificationTrain, - verificationIteration)); - } - - Map result = new LinkedHashMap<>(); - - properties.forEach((k, v) -> { - - if ("dependency.train".equals(k) || "dependency.iteration".equals(k) || "dependency.upgrade.count".equals(k)) { - return; - } - - Matcher matcher = keyPattern.matcher(k.toString()); - - if (!matcher.matches()) { - throw new IllegalArgumentException(String.format("Unexpected key: %s", k)); - } - - String groupId = matcher.group(1); - String artifactId = matcher.group(2); - Dependency dependency = Dependencies.getRequiredDepependency(groupId, artifactId); - - result.put(dependency, DependencyVersion.of(v.toString())); - }); - - DependencyVersions dependencyVersions = new DependencyVersions(result); - - if (expectedUpgradeCount != result.size()) { - throw new IllegalStateException(String.format( - "The number of expected upgrades (dependency.upgrade.count=%s) does not match the number of actual upgrades (%s): %n%n%s", - expectedUpgradeCount, result.size(), dependencyVersions.toString(1))); - } - - return dependencyVersions; - } - -} diff --git a/release-tools/src/main/java/org/springframework/data/release/infra/DependencyVersion.java b/release-tools/src/main/java/org/springframework/data/release/infra/DependencyVersion.java deleted file mode 100644 index 84ac760..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/infra/DependencyVersion.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.infra; - -import lombok.Value; -import lombok.With; - -import java.time.LocalDateTime; -import java.util.Comparator; -import java.util.Locale; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.springframework.data.release.model.Version; - -/** - * Value object representing a dependency version. The primary identifier is {@link #identifier} that corresponds with - * the actual version identifier. Version identifiers are attempted to be parsed into either a version following Spring - * Version or SemVer rules ({@code 1.2.3.RELEASE}, {@code 1.2.3-rc1}) or train name with counter rules - * ({@code Foo-RELEASE}, {@code Foo-SR1}). - * - * @author Mark Paluch - */ -@Value -class DependencyVersion implements Comparable { - - private static Pattern VERSION = Pattern.compile("((?>(?>\\d+)[\\.]?)+)((?>-)?[a-zA-Z]+)?(\\d+)?"); - private static Pattern NAME_VERSION = Pattern.compile("([A-Za-z]+)-(RELEASE|SR(\\d+)|SNAPSHOT|BUILD-SNAPSHOT)"); - - private static Comparator VERSION_COMPARATOR = Comparator.comparing(DependencyVersion::getVersion) - .thenComparing((o1, o2) -> { - - // no modifier means release so it's higher order - if (o1.getModifier().isEmpty() && !o2.getModifier().isEmpty()) { - return 1; - } - - if (!o1.getModifier().isEmpty() && o2.getModifier().isEmpty()) { - return -1; - } - - return 0; - }).thenComparing(DependencyVersion::getModifier).thenComparing(DependencyVersion::getCounter) - .thenComparing(DependencyVersion::getIdentifier); - - private static Comparator TRAIN_VERSION_COMPARATOR = Comparator - .comparing(DependencyVersion::getTrainName).thenComparing(DependencyVersion::getVersion); - - String identifier; - String trainName; - Version version; - String modifier; - int counter; - @With LocalDateTime createdAt; - - public static DependencyVersion of(String identifier) { - - Matcher bomMatcher = NAME_VERSION.matcher(identifier); - - if (bomMatcher.find()) { - - Version version = Version.of(0); - String modifier = ""; - if (identifier.contains("-SR")) { - version = Version.of(Integer.parseInt(bomMatcher.group(3))); - } - - if (identifier.endsWith("-SNAPSHOT")) { - modifier = "SNAPSHOT"; - } - - return new DependencyVersion(identifier, bomMatcher.group(1), version, modifier, 0, null); - } - - Matcher versionMatcher = VERSION.matcher(identifier); - - if (versionMatcher.find()) { - Version version = null; - String modifier; - String counter; - String versionString = versionMatcher.group(1); - - versionString = versionString.endsWith(".") ? versionString.substring(0, versionString.length() - 1) - : versionString; - try { - version = Version.parse(versionString); - } catch (RuntimeException e) { - throw new IllegalArgumentException(String.format("Cannot parse version number %s", versionString), e); - } - - modifier = versionMatcher.group(2); - counter = versionMatcher.group(3); - - return new DependencyVersion(identifier, null, version, modifier == null ? "" : modifier.toUpperCase(Locale.ROOT), - counter != null ? Integer.parseInt(counter) : 0, null); - } - - throw new IllegalArgumentException(String.format("Cannot parse version identifier %s", identifier)); - } - - public boolean isNewer(DependencyVersion other) { - return this.compareTo(other) > 0; - } - - @Override - public int compareTo(DependencyVersion o) { - - if (trainName != null && o.trainName != null) { - return TRAIN_VERSION_COMPARATOR.compare(this, o); - } - - if (trainName != null) { - return -1; - } - - if (o.trainName != null) { - return 1; - } - - if (version != null && o.version != null) { - return VERSION_COMPARATOR.compare(this, o); - } - - return identifier.compareTo(o.identifier); - } - - @Override - public String toString() { - return identifier; - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/infra/DependencyVersions.java b/release-tools/src/main/java/org/springframework/data/release/infra/DependencyVersions.java deleted file mode 100644 index 0dd5163..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/infra/DependencyVersions.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.infra; - -import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.Value; - -import java.util.Collection; -import java.util.Collections; -import java.util.Map; -import java.util.function.BiConsumer; - -import org.apache.maven.shared.utils.StringUtils; - -/** - * Value upgrade capturing {@link DependencyVersion} for a {@link Dependency}. - * - * @author Mark Paluch - */ -@Value -@RequiredArgsConstructor(access = AccessLevel.PACKAGE) -class DependencyVersions { - - @Getter Map versions; - - public static DependencyVersions empty() { - return new DependencyVersions(Collections.emptyMap()); - } - - /** - * Returns whether there's a version specified for {@link Dependency}. - * - * @param dependency - * @return - */ - public boolean hasDependency(Dependency dependency) { - return versions.containsKey(dependency); - } - - /** - * Returns the {@link DependencyVersion} for {@link Dependency} or throws {@link IllegalArgumentException} if no - * version was specified. It's recommended to check whether a version has been specified using - * {@link #hasDependency(Dependency)}. - * - * @param dependency - * @return - * @see #hasDependency(Dependency) - */ - public DependencyVersion get(Dependency dependency) { - - if (!hasDependency(dependency)) { - throw new IllegalArgumentException(String.format("No such dependency: %s", dependency)); - } - - return versions.get(dependency); - } - - /** - * Apply the {@code action} to all dependeny/dependency version pairs. - * - * @param action - */ - public void forEach(BiConsumer action) { - versions.forEach(action); - } - - /** - * Returns whether dependency versions are configured. - * - * @return - */ - public boolean isEmpty() { - return versions.isEmpty(); - } - - public Collection getDependencies() { - return versions.keySet(); - } - - @Override - public String toString() { - return toString(0); - } - - public String toString(int indentation) { - - StringBuilder result = new StringBuilder(); - - for (Map.Entry entry : versions.entrySet()) { - - result.append(StringUtils.repeat("\t", indentation)).append(StringUtils.rightPad(entry.getKey().toString(), 40)) - .append(" = ").append(entry.getValue()).append(System.lineSeparator()); - } - - return result.toString(); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/infra/InfrastructureCommands.java b/release-tools/src/main/java/org/springframework/data/release/infra/InfrastructureCommands.java deleted file mode 100644 index d07c0dc..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/infra/InfrastructureCommands.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.infra; - -import lombok.AccessLevel; -import lombok.RequiredArgsConstructor; -import lombok.experimental.FieldDefaults; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.List; - -import org.bouncycastle.util.Strings; - -import org.springframework.data.release.CliComponent; -import org.springframework.data.release.TimedCommand; -import org.springframework.data.release.git.GitOperations; -import org.springframework.data.release.io.JavaRuntimes; -import org.springframework.data.release.model.Projects; -import org.springframework.data.release.model.TrainIteration; -import org.springframework.data.release.utils.Logger; -import org.springframework.shell.core.annotation.CliCommand; -import org.springframework.shell.core.annotation.CliOption; -import org.springframework.shell.support.table.Table; -import org.springframework.shell.support.table.TableHeader; - -/** - * Shell commands for dependency management. - * - * @author Mark Paluch - */ -@CliComponent -@RequiredArgsConstructor -@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) -public class InfrastructureCommands extends TimedCommand { - - DependencyOperations operations; - GitOperations git; - InfrastructureOperations infra; - Logger logger; - - @CliCommand(value = "infra jdk list") - public Table listJdkVersions() { - - List jdks = JavaRuntimes.getJdks(); - StringBuilder builder = new StringBuilder("Available Java versions" + Strings.lineSeparator()); - - Table table = new Table(); - table.addHeader(1, new TableHeader("Version", 15)); - table.addHeader(2, new TableHeader("Vendor", 20)); - table.addHeader(3, new TableHeader("Home")); - - for (JavaRuntimes.JdkInstallation jdk : jdks) { - table.addRow(jdk.getVersion().toString(), jdk.getImplementor(), jdk.getHome().toString()); - } - - return table; - } - - @CliCommand(value = "infra maven check") - public void check(@CliOption(key = "", mandatory = true) TrainIteration iteration, - @CliOption(key = "all", mandatory = false) Boolean reportAll) throws IOException { - - git.checkout(iteration.getTrain()); - - DependencyUpgradeProposals proposals = operations.getMavenWrapperDependencyUpgradeProposals(iteration); - - Files.write(Paths.get(InfrastructureOperations.MAVEN_PROPERTIES), proposals.asProperties(iteration).getBytes()); - - Table summary = proposals.toTable(reportAll == null ? false : reportAll); - - logger.log(Projects.BUILD, "Upgrade summary:" + System.lineSeparator() + System.lineSeparator() + summary); - logger.log(iteration, "Upgrade proposals written to " + InfrastructureOperations.MAVEN_PROPERTIES); - } - - @CliCommand(value = "infra maven upgrade") - public void upgradeMavenVersion(@CliOption(key = "", mandatory = true) TrainIteration iteration) - throws IOException, InterruptedException { - - logger.log(iteration, "Applying Maven wrapper upgrades to Spring Dataโ€ฆ"); - - infra.upgradeMavenVersion(iteration); - } - - @CliCommand(value = "infra distribute ci-properties") - public void distributeCiProperties(@CliOption(key = "", mandatory = true) TrainIteration iteration) - throws IOException, InterruptedException { - - logger.log(iteration, "Distributing CI properties for Spring Dataโ€ฆ"); - - git.checkout(iteration.getTrain(), true); - - infra.distributeCiProperties(iteration); - } - -} diff --git a/release-tools/src/main/java/org/springframework/data/release/infra/InfrastructureOperations.java b/release-tools/src/main/java/org/springframework/data/release/infra/InfrastructureOperations.java deleted file mode 100644 index 2e8a9c7..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/infra/InfrastructureOperations.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.infra; - -import lombok.AccessLevel; -import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; -import lombok.experimental.FieldDefaults; - -import java.io.File; -import java.io.FileInputStream; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.List; -import java.util.Optional; -import java.util.Properties; -import java.util.concurrent.ExecutorService; - -import org.apache.commons.io.FileUtils; - -import org.springframework.data.release.TimedCommand; -import org.springframework.data.release.git.Branch; -import org.springframework.data.release.git.GitOperations; -import org.springframework.data.release.io.Workspace; -import org.springframework.data.release.issues.Tickets; -import org.springframework.data.release.model.Module; -import org.springframework.data.release.model.ModuleIteration; -import org.springframework.data.release.model.Project; -import org.springframework.data.release.model.Projects; -import org.springframework.data.release.model.Train; -import org.springframework.data.release.model.TrainIteration; -import org.springframework.data.release.utils.ExecutionUtils; -import org.springframework.data.release.utils.Logger; -import org.springframework.data.util.Streamable; -import org.springframework.stereotype.Component; - -/** - * @author Mark Paluch - */ -@Component -@RequiredArgsConstructor -@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) -public class InfrastructureOperations extends TimedCommand { - - public static final String CI_PROPERTIES = "ci/pipeline.properties"; - - public static final String MAVEN_PROPERTIES = "dependency-upgrade-maven.properties"; - - DependencyOperations dependencies; - Workspace workspace; - GitOperations git; - ExecutorService executor; - Logger logger; - - /** - * Distribute {@link #CI_PROPERTIES} from {@link Projects#BUILD} to all modules within {@link TrainIteration}. - * - * @param iteration - */ - void distributeCiProperties(TrainIteration iteration) { - - File master = workspace.getFile(CI_PROPERTIES, Projects.BUILD); - - if (!master.exists()) { - throw new IllegalStateException(String.format("CI Properties file %s does not exist", master)); - } - - ExecutionUtils.run(executor, iteration, module -> { - - Project project = module.getProject(); - Branch branch = Branch.from(module); - - git.update(project); - git.checkout(project, branch); - }); - - verifyExistingPropertyFiles(iteration.getTrain(), master); - - ExecutionUtils.run(executor, Streamable.of(iteration.getModulesExcept(Projects.BUILD)), module -> { - - File target = workspace.getFile(CI_PROPERTIES, module.getProject()); - target.delete(); - - FileUtils.copyFile(master, target); - - git.add(module.getProject(), CI_PROPERTIES); - git.commit(module, "Update CI properties.", Optional.empty(), false); - git.push(module); - }); - } - - private void verifyExistingPropertyFiles(Train train, File master) { - - for (Module module : train) { - File target = workspace.getFile(CI_PROPERTIES, module.getProject()); - - if (!target.exists()) { - throw new IllegalStateException(String.format("CI Properties file %s does not exist", master)); - } - } - } - - public void upgradeMavenVersion(TrainIteration iteration) { - - DependencyVersions dependencyVersions = loadDependencyUpgrades(iteration); - - if (dependencyVersions.isEmpty()) { - throw new IllegalStateException("No version to upgrade found!"); - } - - git.checkout(iteration.getTrain(), false); - - List projectsToUpgrade = dependencies - .getProjectsToUpgradeMavenWrapper(dependencyVersions.get(Dependencies.MAVEN), iteration); - - ExecutionUtils.run(executor, Streamable.of(projectsToUpgrade), project -> { - - ModuleIteration module = iteration.getModule(project); - Tickets tickets = dependencies.getOrCreateUpgradeTickets(module, dependencyVersions); - dependencies.upgradeMavenWrapperVersion(tickets, module, dependencyVersions); - git.push(module); - - // Allow GitHub to catch up with ticket notifications. - Thread.sleep(1500); - - dependencies.closeUpgradeTickets(module, tickets); - }); - } - - @SneakyThrows - private DependencyVersions loadDependencyUpgrades(TrainIteration iteration) { - - if (!Files.exists(Paths.get(MAVEN_PROPERTIES))) { - logger.log(iteration, "Cannot upgrade dependencies: " + MAVEN_PROPERTIES + " does not exist."); - } - - Properties properties = new Properties(); - try (FileInputStream fis = new FileInputStream(MAVEN_PROPERTIES)) { - properties.load(fis); - } - - return DependencyUpgradeProposals.fromProperties(iteration, properties); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/infra/LicenseHeaderCommands.java b/release-tools/src/main/java/org/springframework/data/release/infra/LicenseHeaderCommands.java deleted file mode 100644 index 343c206..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/infra/LicenseHeaderCommands.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright 2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.infra; - -import lombok.AccessLevel; -import lombok.RequiredArgsConstructor; -import lombok.experimental.FieldDefaults; - -import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; -import java.util.function.Function; - -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.filefilter.AbstractFileFilter; -import org.apache.commons.io.filefilter.IOFileFilter; -import org.apache.commons.io.filefilter.NameFileFilter; -import org.apache.commons.io.filefilter.NotFileFilter; - -import org.springframework.data.release.CliComponent; -import org.springframework.data.release.TimedCommand; -import org.springframework.data.release.git.GitOperations; -import org.springframework.data.release.io.Workspace; -import org.springframework.data.release.issues.IssueTracker; -import org.springframework.data.release.issues.Ticket; -import org.springframework.data.release.issues.TicketOperations; -import org.springframework.data.release.model.ModuleIteration; -import org.springframework.data.release.model.Project; -import org.springframework.data.release.model.Projects; -import org.springframework.data.release.model.TrainIteration; -import org.springframework.data.release.utils.ExecutionUtils; -import org.springframework.data.release.utils.Logger; -import org.springframework.data.util.Streamable; -import org.springframework.shell.core.annotation.CliCommand; -import org.springframework.shell.core.annotation.CliOption; -import org.springframework.util.AntPathMatcher; - -/** - * @author Mark Paluch - */ -@CliComponent -@RequiredArgsConstructor -@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) -public class LicenseHeaderCommands extends TimedCommand { - - GitOperations git; - - Workspace workspace; - - Executor executor; - - Logger logger; - - TicketOperations tickets; - - List filePatterns = Arrays.asList("pom.xml", "**/*.java", "**/*.kt", "**/*.adoc"); - - /** - * Process all files matching {@link #filePatterns} and update the Apache license header year range, extending to - * {@code year}. Rewrites single-year and year-range formats. - * - * @param iteration - * @param year - */ - @CliCommand(value = "update license-headers") - public void updateLicenseHeaders(@CliOption(key = "", mandatory = true) TrainIteration iteration, - @CliOption(key = "year", mandatory = true) int year, - @CliOption(key = "project", mandatory = false) String projectName) { - - git.prepare(iteration); - - Streamable modules = iteration; - - if (projectName != null) { - Project project = Projects.requiredByName(projectName); - modules = modules.filter(it -> it.getProject().equals(project)); - } - - ExecutionUtils.run(executor, modules, module -> { - - String summary = String.format("Extend license header copyright years to %d", year); - - int updated = replaceInFiles(module.getProject(), content -> { - - String contentToUse = content; - - contentToUse = contentToUse.replaceAll("(C) ([\\d]{4})-([\\d]{4})", "(C) $1-" + year); - - contentToUse = contentToUse.replaceAll("Copyright ([\\d]{4}) the original author or authors", - "Copyright $1-" + year + " the original author or authors"); - - contentToUse = contentToUse.replaceAll("Copyright ([\\d]{4})-([\\d]{4}) the original author or authors", - "Copyright $1-" + year + " the original author or authors"); - - return contentToUse; - }); - - if (updated > 0) { - commitAndPushWithTicket(module, summary); - } - }); - } - - private void commitAndPushWithTicket(ModuleIteration module, String ticketSummary) throws InterruptedException { - - Ticket ticket = tickets.getOrCreateTicketsWithSummary(module, IssueTracker.TicketType.Task, ticketSummary); - git.commit(module, ticket, ticketSummary, Optional.empty(), true); - - try { - git.push(module); - } catch (Exception e) { - logger.warn(module, e); - } - - TimeUnit.SECONDS.sleep(1); - - tickets.closeTicket(module, ticket); - } - - /** - * Replace content in files by applying {@link Function contentFunction} and return the number of updated files. - * - * @param project - * @param contentFunction - * @return - */ - private int replaceInFiles(Project project, Function contentFunction) { - - File projectDirectory = workspace.getProjectDirectory(project); - IOFileFilter fileFilter = new AntPathFileFilter(projectDirectory, filePatterns); - - int files = 0; - int modified = 0; - Iterator fileIterator = FileUtils.iterateFiles(projectDirectory, fileFilter, - new NotFileFilter(new NameFileFilter(".git"))); - - while (fileIterator.hasNext()) { - - File file = fileIterator.next(); - files++; - - try { - if (doReplace(file, contentFunction)) { - modified++; - } - } catch (IOException e) { - throw new IllegalStateException(String.format("Cannot modify contents of %s", file), e); - } - } - - logger.log(project, "Found %s files, updated %s files", files, modified); - - return modified; - } - - private boolean doReplace(File file, Function modifyFunction) throws IOException { - - String content = FileUtils.readFileToString(file, StandardCharsets.UTF_8); - String modified = modifyFunction.apply(content); - - if (!content.equals(modified)) { - - FileUtils.write(file, modified, StandardCharsets.UTF_8); - return true; - } - - return false; - } - - private static class AntPathFileFilter extends AbstractFileFilter { - - private final URI projectDirectory; - private final List filePatterns; - - public AntPathFileFilter(File basePath, List filePatterns) { - this.projectDirectory = basePath.toURI(); - this.filePatterns = filePatterns; - } - - @Override - public boolean accept(File file) { - - String relativePath = projectDirectory.relativize(file.toURI()).getPath(); - - AntPathMatcher matcher = new AntPathMatcher(); - for (String pattern : filePatterns) { - - if (matcher.match(pattern, relativePath)) { - return true; - } - } - - return false; - } - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/infra/ProjectDependencies.java b/release-tools/src/main/java/org/springframework/data/release/infra/ProjectDependencies.java deleted file mode 100644 index bd49970..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/infra/ProjectDependencies.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright 2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.infra; - -import lombok.Value; - -import java.util.Iterator; -import java.util.List; - -import org.springframework.data.release.model.Project; -import org.springframework.data.release.model.Projects; -import org.springframework.data.util.Streamable; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; - -/** - * Configuration of dependencies for a specific project. - * - * @author Mark Paluch - */ -public class ProjectDependencies implements Streamable { - - private static final MultiValueMap config = new LinkedMultiValueMap<>(); - - static { - - config.add(Projects.BUILD, ProjectDependency.ofProperty("apt", Dependencies.APT)); - config.add(Projects.BUILD, ProjectDependency.ofProperty("aspectj", Dependencies.ASPECTJ)); - config.add(Projects.BUILD, ProjectDependency.ofProperty("assertj", Dependencies.ASSERTJ)); - config.add(Projects.BUILD, ProjectDependency.ofProperty("jackson", Dependencies.JACKSON)); - config.add(Projects.BUILD, ProjectDependency.ofProperty("jacoco", Dependencies.JACOCO)); - config.add(Projects.BUILD, ProjectDependency.ofProperty("jodatime", Dependencies.JODA_TIME)); - config.add(Projects.BUILD, ProjectDependency.ofProperty("junit5", Dependencies.JUNIT5)); - config.add(Projects.BUILD, ProjectDependency.ofProperty("jmolecules", Dependencies.JMOLECULES)); - config.add(Projects.BUILD, - ProjectDependency.ofProperty("jmolecules-integration", Dependencies.JMOLECULES_INTEGRATION)); - config.add(Projects.BUILD, ProjectDependency.ofProperty("junit", Dependencies.JUNIT4)); - config.add(Projects.BUILD, ProjectDependency.ofProperty("kotlin", Dependencies.KOTLIN)); - config.add(Projects.BUILD, ProjectDependency.ofProperty("kotlin-coroutines", Dependencies.KOTLIN_COROUTINES)); - config.add(Projects.BUILD, ProjectDependency.ofProperty("micrometer", Dependencies.MICROMETER)); - config.add(Projects.BUILD, ProjectDependency.ofProperty("micrometer-tracing", Dependencies.MICROMETER_TRACING)); - config.add(Projects.BUILD, ProjectDependency.ofProperty("mockito", Dependencies.MOCKITO)); - config.add(Projects.BUILD, ProjectDependency.ofProperty("mockk", Dependencies.MOCKK)); - config.add(Projects.BUILD, ProjectDependency.ofProperty("querydsl", Dependencies.QUERYDSL)); - config.add(Projects.BUILD, ProjectDependency.ofProperty("rxjava", Dependencies.RXJAVA1)); - config.add(Projects.BUILD, ProjectDependency.ofProperty("rxjava2", Dependencies.RXJAVA2)); - config.add(Projects.BUILD, ProjectDependency.ofProperty("rxjava3", Dependencies.RXJAVA3)); - config.add(Projects.BUILD, ProjectDependency.ofProperty("rxjava-reactive-streams", Dependencies.RXJAVA_RS)); - config.add(Projects.BUILD, ProjectDependency.ofProperty("smallrye-mutiny", Dependencies.SMALLRYE_MUTINY)); - config.add(Projects.BUILD, ProjectDependency.ofProperty("spring-hateoas", Dependencies.SPRING_HATEOAS)); - config.add(Projects.BUILD, ProjectDependency.ofProperty("spring-plugin", Dependencies.SPRING_PLUGIN)); - config.add(Projects.BUILD, ProjectDependency.ofProperty("testcontainers", Dependencies.TESTCONTAINERS)); - config.add(Projects.BUILD, ProjectDependency.ofProperty("threetenbp", Dependencies.THREE_TEN_BP)); - config.add(Projects.BUILD, ProjectDependency.ofProperty("webbeans", Dependencies.OPEN_WEB_BEANS)); - - config.add(Projects.COMMONS, ProjectDependency.ofProperty("vavr", Dependencies.VAVR)); - config.add(Projects.COMMONS, ProjectDependency.ofProperty("xmlbeam", Dependencies.XML_BEAM)); - - config.add(Projects.MONGO_DB, ProjectDependency.ofProperty("mongo.reactivestreams", Dependencies.MONGODB_RS)); - config.add(Projects.MONGO_DB, ProjectDependency.ofProperty("mongo", Dependencies.MONGODB_LEGACY)); - config.add(Projects.MONGO_DB, ProjectDependency.ofProperty("mongo", Dependencies.MONGODB_CORE)); - config.add(Projects.MONGO_DB, ProjectDependency.ofProperty("mongo", Dependencies.MONGODB_SYNC)); - config.add(Projects.MONGO_DB, ProjectDependency.ofProperty("mongo", Dependencies.MONGODB_ASYNC)); - - config.add(Projects.REDIS, ProjectDependency.ofProperty("lettuce", Dependencies.LETTUCE)); - config.add(Projects.REDIS, ProjectDependency.ofProperty("jedis", Dependencies.JEDIS)); - - config.add(Projects.CASSANDRA, - ProjectDependency.ofProperty("cassandra-driver.version", Dependencies.CASSANDRA_DRIVER3)); - config.add(Projects.CASSANDRA, - ProjectDependency.ofProperty("cassandra-driver.version", Dependencies.CASSANDRA_DRIVER4)); - - config.add(Projects.NEO4J, ProjectDependency.ofProperty("neo4j.ogm.version", Dependencies.NEO4J_OGM)); - config.add(Projects.NEO4J, ProjectDependency.ofProperty("neo4j-java-driver.version", Dependencies.NEO4J_DRIVER)); - - config.add(Projects.COUCHBASE, ProjectDependency.ofProperty("couchbase", Dependencies.COUCHBASE)); - - config.add(Projects.ELASTICSEARCH, ProjectDependency.ofProperty("elasticsearch", Dependencies.ELASTICSEARCH_RHLC)); - config.add(Projects.ELASTICSEARCH, - ProjectDependency.ofProperty("elasticsearch-rhlc", Dependencies.ELASTICSEARCH_RHLC)); - config.add(Projects.ELASTICSEARCH, - ProjectDependency.ofProperty("elasticsearch-java", Dependencies.ELASTICSEARCH_REST_CLIENT)); - - config.add(Projects.LDAP, ProjectDependency.ofProperty("spring-ldap", Dependencies.SPRING_LDAP)); - } - - private final List dependencies; - - private ProjectDependencies(List dependencies) { - this.dependencies = dependencies; - } - - /** - * Retrieve upgradable dependencies for a {@link Project}. - * - * @param project - * @return - * @throws IllegalArgumentException if the project has no upgradable dependencies. - */ - public static ProjectDependencies get(Project project) { - - if (!containsProject(project)) { - throw new IllegalArgumentException(String.format("No dependency configuration for %s!", project)); - } - - return new ProjectDependencies(config.get(project)); - } - - /** - * Check whether the {@link Project} has upgradable dependencies. - * - * @param project - * @return - */ - public static boolean containsProject(Project project) { - return config.containsKey(project); - } - - public String getVersionPropertyFor(Dependency dependency) { - - for (ProjectDependency projectDependency : dependencies) { - - if (projectDependency.getDependency().equals(dependency)) { - return projectDependency.getProperty(); - } - } - - throw new IllegalArgumentException("Dependency " + dependency + " is not a dependency of this project!"); - } - - @Override - public Iterator iterator() { - return dependencies.iterator(); - } - - @Value - public static class ProjectDependency { - - String property; - - Dependency dependency; - - public static ProjectDependency ofProperty(String pomProperty, Dependency dependency) { - return new ProjectDependency(pomProperty, dependency); - } - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/io/CommandResult.java b/release-tools/src/main/java/org/springframework/data/release/io/CommandResult.java deleted file mode 100644 index 6cc5ea8..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/io/CommandResult.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.io; - -import lombok.Value; - -/** - * @author Oliver Gierke - */ -@Value -public class CommandResult { - - private final int status; - private final String output; - private final Exception exception; - - public boolean hasError() { - return status != 0; - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/io/IoProperties.java b/release-tools/src/main/java/org/springframework/data/release/io/IoProperties.java deleted file mode 100644 index 1fff619..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/io/IoProperties.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2015-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.io; - -import lombok.Data; -import lombok.extern.slf4j.Slf4j; - -import java.io.File; - -import org.apache.commons.io.FileUtils; - -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.stereotype.Component; - -/** - * @author Oliver Gierke - * @author Mark Paluch - * @author Christoph Strobl - */ -@Slf4j -@Data -@Component -@ConfigurationProperties(prefix = "io") -class IoProperties { - - private File workDir, logs; - - public void setWorkDir(String workDir) { - - log.info(String.format("๐Ÿ”ง Using %s as working directory!", workDir)); - this.workDir = new File(workDir.replace("~", FileUtils.getUserDirectoryPath())); - } - -} diff --git a/release-tools/src/main/java/org/springframework/data/release/io/JavaRuntimes.java b/release-tools/src/main/java/org/springframework/data/release/io/JavaRuntimes.java deleted file mode 100644 index b1dc8e9..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/io/JavaRuntimes.java +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Copyright 2022-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.io; - -import lombok.SneakyThrows; -import lombok.Value; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileFilter; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.NoSuchElementException; -import java.util.concurrent.TimeUnit; -import java.util.function.Predicate; -import java.util.function.Supplier; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.filefilter.RegexFileFilter; - -import org.springframework.boot.system.SystemProperties; -import org.springframework.data.release.model.JavaVersion; -import org.springframework.data.release.model.Version; -import org.springframework.data.util.Lazy; -import org.springframework.util.ObjectUtils; -import org.springframework.util.StreamUtils; - -import com.dd.plist.NSArray; -import com.dd.plist.NSDictionary; -import com.dd.plist.XMLPropertyListParser; - -/** - * Utility to detect a Java runtime version. - * - * @author Mark Paluch - */ -public class JavaRuntimes { - - private static final List DETECTORS = Arrays.asList(new SDKmanJdkDetector(), new MacNativeJdkDetector(), - new JavaHomeJdkDetector()); - private static final Lazy> JDKS = Lazy.of(() -> { - - List jdks = DETECTORS.stream() // - .filter(JdkDetector::isAvailable) // - .flatMap(it -> it.detect().stream()) // - .sorted() // - .collect(Collectors.toList()); - - Collections.reverse(jdks); - - return Collections.unmodifiableList(jdks); - }); - - /** - * Lookup a {@link JdkInstallation} by detecting installed JDKs and applying the {@link Predicate filter}. Returns the - * first matching one or throws {@link NoSuchElementException}. - * - * @param filter - * @return - */ - public static JdkInstallation getJdk(Predicate filter) { - return getJdk(filter, () -> "Cannot obtain required JDK"); - } - - /** - * Lookup a {@link JdkInstallation} by detecting installed JDKs and applying the {@link Predicate filter}. Returns the - * first matching one or throws {@link NoSuchElementException}. - * - * @param filter - * @param message - * @return - */ - public static JdkInstallation getJdk(Predicate filter, Supplier message) { - - List jdks = JDKS.get(); - - return jdks.stream().filter(filter).findFirst() - .orElseThrow(() -> new NoSuchElementException(String.format("%s%nAvailable JDK: %s", message.get(), jdks))); - } - - public static List getJdks() { - return JDKS.get(); - } - - static boolean isDirectory(File file) { - return file.exists() && file.isDirectory(); - } - - /** - * JDK detection strategy. - */ - interface JdkDetector { - - /** - * @return {@code true} if the detector strategy is available. - */ - boolean isAvailable(); - - /** - * @return a list of JDK installations. - */ - List detect(); - - } - - /** - * Selector to determine a {@link JdkInstallation}. - */ - public static class Selector { - - private String notFoundMessage; - private Predicate predicate; - - private Selector() { - - } - - public static Selector builder() { - return new Selector(); - } - - public static Selector from(JavaVersion javaVersion) { - - return builder() - .and(it -> javaVersion.getVersionDetector().test(it.getVersion()) - && javaVersion.getImplementor().test(it.getImplementor())) - .message("Cannot find Java " + javaVersion.getName()); - } - - public Selector and(Predicate predicate) { - this.predicate = this.predicate == null ? predicate : this.predicate.and(predicate); - return this; - } - - public Selector notGraalVM() { - this.notFoundMessage += " (Not GraalVM)"; - return and(it -> !it.getName().contains("GraalVM")); - } - - public Selector message(String notFoundMessage) { - this.notFoundMessage = notFoundMessage; - return this; - } - - public JdkInstallation getRequiredJdkInstallation() { - return JavaRuntimes.getJdk(predicate, () -> notFoundMessage); - } - - } - - /** - * Detector using the SDKman utility storing Java installations in {@code ~/.sdkman/candidates/java}. - */ - static class SDKmanJdkDetector implements JdkDetector { - - private static final File sdkManJavaHome = new File(FileUtils.getUserDirectoryPath(), ".sdkman/candidates/java"); - - private static final Pattern CANDIDATE = Pattern.compile("(\\d+[\\.\\d+]+)[.-][-a-zA-Z]*"); - - @Override - public boolean isAvailable() { - return isDirectory(sdkManJavaHome); - } - - @Override - public List detect() { - - File[] files = sdkManJavaHome.listFiles((FileFilter) new RegexFileFilter(CANDIDATE)); - - return Arrays.stream(files).map(it -> { - - Matcher matcher = CANDIDATE.matcher(it.getName()); - if (!matcher.find()) { - throw new IllegalArgumentException("Cannot determine JVM version number from SDKman candidate name " - + it.getName() + ". This should not happen in an ideal world, check the CANDIDATE regex."); - } - - String candidateVersion = matcher.group(1); - Version version = Version.parse(candidateVersion); - if (version.getMajor() <= 8) { - candidateVersion = "1." + candidateVersion; - version = Version.parse(candidateVersion); - } - - String implementor = normalizeImplementor(parseImplementor(it)); - - return new JdkInstallation(version, toDisplayName(implementor, candidateVersion), implementor, it); - - }).collect(Collectors.toList()); - } - - @SneakyThrows - private String parseImplementor(File candidateHome) { - - List release = FileUtils.readLines(new File(candidateHome, "release")); - - for (String line : release) { - - if (line.startsWith("IMPLEMENTOR=")) { - String substring = line.substring(line.indexOf("=\"")); - substring = substring.substring(2, substring.length() - 1); - return substring; - } - } - - return "?"; - } - } - - /** - * Detector using the {@code java.home} system property. - */ - static class JavaHomeJdkDetector implements JdkDetector { - - private static final File javaHome = new File(System.getProperty("java.home")); - private static final String javaVersion = System.getProperty("java.version"); - private static final String javaVendor = System.getProperty("java.vendor"); - - @Override - public boolean isAvailable() { - return isDirectory(javaHome); - } - - @Override - public List detect() { - - return Collections.singletonList(new JdkInstallation(JavaVersion.parse(javaVersion), - toDisplayName(javaVendor, javaVersion), normalizeImplementor(javaVendor), javaHome)); - } - } - - /** - * Detector using the {@code /usr/libexec/java_home} utility storing Java installations in {@code /Libraries/Java} on - * the Mac. - */ - static class MacNativeJdkDetector implements JdkDetector { - - private static final File javaHomeBinary = new File("/usr/libexec/java_home"); - - private static final File nativeInstallationDirectory = new File("/Library/Java/JavaVirtualMachines"); - - private static final Pattern VERSION = Pattern.compile("((:?\\d+(:?\\.\\d+)*)(:?_+\\d+)?)"); - - @Override - public boolean isAvailable() { - return isDirectory(nativeInstallationDirectory) && !ObjectUtils.isEmpty(nativeInstallationDirectory.listFiles()) - && javaHomeBinary.exists() && SystemProperties.get("os.name").contains("Mac"); - } - - @Override - @SneakyThrows - public List detect() { - - Process process = new ProcessBuilder(javaHomeBinary.toString(), "-X").redirectOutput(ProcessBuilder.Redirect.PIPE) - .start(); - - process.waitFor(5, TimeUnit.SECONDS); - byte[] out = StreamUtils.copyToByteArray(process.getInputStream()); - - if (process.exitValue() != 0) { - - throw new IllegalStateException(javaHomeBinary + " failed with: " + System.lineSeparator() + new String(out) - + new String(StreamUtils.copyToByteArray(process.getErrorStream()))); - } - - NSArray array = (NSArray) XMLPropertyListParser.parse(new ByteArrayInputStream(out)); - - return Arrays.stream(array.getArray()).map(it -> { - - NSDictionary dict = (NSDictionary) it; - - String jvmHomePath = dict.get("JVMHomePath").toJavaObject(String.class); - String name = dict.get("JVMName").toJavaObject(String.class); - String version = dict.get("JVMVersion").toJavaObject(String.class); - String vendor = dict.get("JVMVendor").toJavaObject(String.class); - - Matcher matcher = VERSION.matcher(version); - if (!matcher.find()) { - throw new IllegalArgumentException("Cannot determine JVM version number from JVMVersion " + version - + ". This should not happen in an ideal world, check the VERSION regex."); - } - - String implementor = normalizeImplementor(vendor); - return new JdkInstallation(JavaVersion.parse(matcher.group(1)), toDisplayName(implementor, version), - implementor, new File(jvmHomePath)); - - }).collect(Collectors.toList()); - } - } - - @Value - public static class JdkInstallation implements Comparable { - - Version version; - String name; - String implementor; - File home; - - @Override - public int compareTo(JdkInstallation o) { - return this.version.compareTo(o.version); - } - } - - static String normalizeImplementor(String implementor) { - - if (implementor.equals("Eclipse Adoptium")) { - return "Eclipse Temurin"; - } - - if (implementor.equals("Eclipse Foundation")) { - return "Eclipse Temurin"; - } - - return implementor; - } - - static String toDisplayName(String implementor, String version) { - - if (implementor.startsWith("Oracle")) { - implementor = "Oracle Java"; - } - - return implementor + " " + version; - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/io/Workspace.java b/release-tools/src/main/java/org/springframework/data/release/io/Workspace.java deleted file mode 100644 index e2cd713..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/io/Workspace.java +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.io; - -import static org.springframework.data.release.utils.StreamUtils.*; - -import lombok.AccessLevel; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import lombok.experimental.FieldDefaults; - -import java.io.File; -import java.io.IOException; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.nio.file.FileVisitResult; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.Arrays; -import java.util.Collections; -import java.util.Optional; -import java.util.Scanner; -import java.util.function.Predicate; -import java.util.stream.Stream; - -import javax.annotation.PostConstruct; - -import org.springframework.core.io.Resource; -import org.springframework.core.io.support.ResourcePatternResolver; -import org.springframework.data.release.model.Project; -import org.springframework.data.release.utils.Logger; -import org.springframework.stereotype.Component; -import org.springframework.util.Assert; - -/** - * Abstraction of the workspace that is used to work with the {@link Project}'s repositories, execute builds, etc. - * - * @author Oliver Gierke - */ -@Component -@RequiredArgsConstructor -@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) -public class Workspace { - - private static final Charset UTF_8 = StandardCharsets.UTF_8; - - @NonNull IoProperties ioProperties; - @NonNull ResourcePatternResolver resolver; - @NonNull Logger logger; - - /** - * Returns the current working directory. - * - * @return - */ - public File getWorkingDirectory() { - return ioProperties.getWorkDir(); - } - - /** - * Returns the current logs directory. - * - * @return - */ - public File getLogsDirectory() { - return ioProperties.getLogs(); - } - - /** - * Cleans up the working directory by removing all files and folders in it. - * - * @throws IOException - */ - public void cleanup() throws IOException { - - delete(getWorkingDirectory().toPath(), "workspace"); - delete(getLogsDirectory().toPath(), "logs"); - } - - private void delete(Path path, String type) throws IOException { - - logger.log("Workspace", "Cleaning up %s directory at %s.", type, path.toAbsolutePath()); - - Files.walkFileTree(path, new SimpleFileVisitor() { - - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - Files.delete(file); - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { - - if (!path.equals(dir)) { - Files.delete(dir); - } - - return FileVisitResult.CONTINUE; - } - }); - } - - public void purge(Path path, Predicate filter) throws IOException { - - Files.walkFileTree(path, new SimpleFileVisitor() { - - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - - if (filter.test(file)) { - Files.delete(file); - } - - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { - - if (filter.test(dir)) { - Files.delete(dir); - } - - return FileVisitResult.CONTINUE; - } - }); - } - - /** - * Returns the directory for the given {@link Project}. - * - * @param project must not be {@literal null}. - * @return - */ - public File getProjectDirectory(Project project) { - - Assert.notNull(project, "Project must not be null!"); - return new File(getWorkingDirectory(), project.getFolderName()); - } - - /** - * Returns whether the project directory for the given project already exists. - * - * @param project must not be {@literal null}. - * @return - */ - public boolean hasProjectDirectory(Project project) { - - Assert.notNull(project, "Project must not be null!"); - return getProjectDirectory(project).exists(); - } - - /** - * Returns a file with the given name relative to the working directory for the given {@link Project}. - * - * @param name must not be {@literal null} or empty. - * @param project must not be {@literal null}. - * @return - */ - public File getFile(String name, Project project) { - - Assert.hasText(name, "Filename must not be null or empty!"); - Assert.notNull(project, "Project must not be null!"); - - return new File(getProjectDirectory(project), name); - } - - public Stream getFiles(String pattern, Project project) { - - File projectDirectory = getProjectDirectory(project); - String patternToLookup = String.format("file:%s/%s", projectDirectory.getAbsolutePath(), pattern); - - try { - return Arrays.stream(resolver.getResources(patternToLookup)).map(wrap(Resource::getFile)); - } catch (IOException o_O) { - throw new RuntimeException(o_O); - } - } - - public boolean processFile(String filename, Project project, LineCallback callback) { - - File file = getFile(filename, project); - - if (!file.exists()) { - return false; - } - - StringBuilder builder = new StringBuilder(); - - try (Scanner scanner = new Scanner(file)) { - - long number = 0; - - while (scanner.hasNextLine()) { - callback.doWith(scanner.nextLine(), number++).ifPresent(it -> builder.append(it).append("\n")); - } - - writeContentToFile(filename, project, builder.toString()); - - } catch (Exception o_O) { - throw new RuntimeException(o_O); - } - - return true; - } - - private void writeContentToFile(String name, Project project, String content) throws IOException { - - File file = getFile(name, project); - Files.write(file.toPath(), Collections.singleton(content), UTF_8); - } - - /** - * Initializes the working directory and creates the folders if necessary. - * - * @throws IOException - */ - @PostConstruct - public void setUp() throws IOException { - - Path path = getWorkingDirectory().toPath(); - - if (!java.nio.file.Files.exists(path)) { - java.nio.file.Files.createDirectories(path); - } - } - - public interface LineCallback { - Optional doWith(String line, long number); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/io/WorkspaceCommands.java b/release-tools/src/main/java/org/springframework/data/release/io/WorkspaceCommands.java deleted file mode 100644 index e1bbcf9..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/io/WorkspaceCommands.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2016-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.io; - -import lombok.AccessLevel; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import lombok.experimental.FieldDefaults; - -import java.io.IOException; - -import org.springframework.data.release.CliComponent; -import org.springframework.data.release.TimedCommand; -import org.springframework.data.release.utils.Logger; -import org.springframework.shell.core.annotation.CliCommand; - -/** - * @author Oliver Gierke - */ -@CliComponent -@RequiredArgsConstructor -@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) -class WorkspaceCommands extends TimedCommand { - - @NonNull Workspace workspace; - @NonNull Logger logger; - - @CliCommand("workspace cleanup") - public void cleanup() throws IOException { - workspace.cleanup(); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/issues/Changelog.java b/release-tools/src/main/java/org/springframework/data/release/issues/Changelog.java deleted file mode 100644 index ec54c3d..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/issues/Changelog.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.issues; - -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -import java.util.Date; -import java.util.Locale; - -import org.apache.commons.io.IOUtils; - -import org.springframework.data.release.model.ArtifactVersion; -import org.springframework.data.release.model.ModuleIteration; -import org.springframework.format.datetime.DateFormatter; - -/** - * @author Oliver Gierke - */ -@RequiredArgsConstructor(staticName = "of") -@EqualsAndHashCode -public class Changelog { - - @Getter private final ModuleIteration module; - private final Tickets tickets; - - /* - * (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return toString(true, ""); - - } - - public String toString(boolean header, String indentation) { - ArtifactVersion version = ArtifactVersion.of(module); - - String headline = String.format("Changes in version %s (%s)", version, - new DateFormatter("YYYY-MM-dd").print(new Date(), Locale.US)); - - StringBuilder builder = new StringBuilder(); - - if (header) { - - builder.append(indentation).append(headline).append(IOUtils.LINE_SEPARATOR).append(indentation); - - for (int i = 0; i < headline.length(); i++) { - builder.append("-"); - } - - builder.append(IOUtils.LINE_SEPARATOR); - } - - for (Ticket ticket : tickets) { - - String summary = ticket.getSummary(); - - builder.append(indentation).append("* ").append(ticket.getId()).append(" - ") - .append(summary != null ? summary.trim() : ""); - - if (!summary.endsWith(".")) { - builder.append("."); - } - - builder.append(IOUtils.LINE_SEPARATOR); - } - - return builder.toString(); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/issues/IssueTracker.java b/release-tools/src/main/java/org/springframework/data/release/issues/IssueTracker.java deleted file mode 100644 index 2379586..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/issues/IssueTracker.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.issues; - -import java.util.Collection; -import java.util.List; -import java.util.stream.Collectors; - -import org.springframework.data.release.model.Iteration; -import org.springframework.data.release.model.ModuleIteration; -import org.springframework.data.release.model.Project; -import org.springframework.data.release.model.Train; -import org.springframework.data.release.model.TrainIteration; -import org.springframework.plugin.core.Plugin; - -/** - * Interface for issue tracker operations. - * - * @author Oliver Gierke - * @author Mark Paluch - */ -public interface IssueTracker extends Plugin { - - /** - * Reset internal state (cache, etc). - */ - void reset(); - - /** - * Returns all {@link Tickets} for the given {@link TrainIteration}. - * - * @param iteration must not be {@literal null}. - * @return - */ - Tickets getTicketsFor(TrainIteration iteration); - - /** - * Returns all {@link Tickets} for the given {@link ModuleIteration}. - * - * @param iteration must not be {@literal null}. - * @return - */ - Tickets getTicketsFor(ModuleIteration iteration); - - /** - * Returns all {@link Tickets} for the given {@link Train} and {@link Iteration}. - * - * @param iteration must not be {@literal null}. - * @param forCurrentUser - * @return - */ - Tickets getTicketsFor(TrainIteration iteration, boolean forCurrentUser); - - /** - * Returns the {@link Ticket} that tracks modifications in the context of a release. - * - * @param module the module to lookup the {@link Ticket} for, must not be {@literal null}. - * @return - */ - Ticket getReleaseTicketFor(ModuleIteration module); - - /** - * Query the issue tracker for multiple {@link Ticket#id ticket Ids}. Tickets that are not found are not returned - * within the result. - * - * @param project must not be {@literal null}. - * @param ticketIds collection of {@link Ticket#id ticket Ids}, must not be {@literal null}. - * @return - */ - Collection findTickets(Project project, Collection ticketIds); - - /** - * Query the issue tracker for multiple {@link Ticket#id ticket Ids}. Tickets that are not found are not returned. The - * implementation ensures to resolve only references that match the issue tracker scheme this issue tracker is - * responsible for. - * - * @param moduleIteration must not be {@literal null}. - * @param ticketReferences must not be {@literal null}. - * @return - */ - Tickets findTickets(ModuleIteration moduleIteration, Collection ticketIds); - - /** - * Creates a release version if release version is missing. - * - * @param module must not be {@literal null}. - */ - void createReleaseVersion(ModuleIteration module); - - /** - * Retire the release version from the active versions for a {@link ModuleIteration}. - * - * @param module must not be {@literal null}. - */ - void archiveReleaseVersion(ModuleIteration module); - - /** - * Create release ticket if release ticket is missing. - *

- * TODO: Return created ticket - * - * @param module must not be {@literal null}. - */ - void createReleaseTicket(ModuleIteration module); - - /** - * Creates a ticket for the given {@link ModuleIteration} and summary {@code text}. - * - * @param module must not be {@literal null}. - * @param text the text to use. - * @param ticketType the ticket type. - * @param assignToCurrentUser - * @return the created ticket. - */ - Ticket createTicket(ModuleIteration module, String text, TicketType ticketType, boolean assignToCurrentUser); - - /** - * Assigns the ticket to the current user. - * - * @param project must not be {@literal null}. - * @param ticket must not be {@literal null}. - */ - Ticket assignTicketToMe(Project project, Ticket ticket); - - /** - * Assigns the release ticket for the given {@link ModuleIteration} to the current user. - * - * @param module must not be {@literal null}. - * @return - */ - Ticket assignReleaseTicketToMe(ModuleIteration module); - - /** - * Start progress on release tickets. - * - * @param module - * @return - */ - Ticket startReleaseTicketProgress(ModuleIteration module); - - /** - * Returns the {@link Changelog} for the given {@link ModuleIteration}. - * - * @param module must not be {@literal null}. - * @return - */ - Changelog getChangelogFor(ModuleIteration module); - - /** - * Returns the {@link Changelog} for the given {@link ModuleIteration} using {@link TicketReference}s. - * - * @param module must not be {@literal null}. - * @return - */ - default Changelog getChangelogFor(ModuleIteration module, List ticketReferences) { - - Tickets tickets = findTickets(module, - ticketReferences.stream().map(TicketReference::getId).collect(Collectors.toList())); - return Changelog.of(module, tickets); - } - - /** - * Closes the given {@link ModuleIteration}. - * - * @param module must not be {@literal null}. - */ - void closeIteration(ModuleIteration module); - - /** - * Resolve a {@link Ticket}. - * - * @param module must not be {@literal null}. - * @param ticket must not be {@literal null}. - */ - void closeTicket(ModuleIteration module, Ticket ticket); - - enum TicketType { - Task, DependencyUpgrade; - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/issues/IssueTrackerCommands.java b/release-tools/src/main/java/org/springframework/data/release/issues/IssueTrackerCommands.java deleted file mode 100644 index d12751d..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/issues/IssueTrackerCommands.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.issues; - -import static org.springframework.data.release.utils.ExecutionUtils.*; - -import lombok.AccessLevel; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import lombok.experimental.FieldDefaults; - -import java.util.concurrent.Executor; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.StreamSupport; - -import org.springframework.data.release.CliComponent; -import org.springframework.data.release.TimedCommand; -import org.springframework.data.release.model.ModuleIteration; -import org.springframework.data.release.model.Project; -import org.springframework.data.release.model.Projects; -import org.springframework.data.release.model.TrainIteration; -import org.springframework.data.release.utils.ExecutionUtils; -import org.springframework.plugin.core.PluginRegistry; -import org.springframework.shell.core.annotation.CliCommand; -import org.springframework.shell.core.annotation.CliOption; -import org.springframework.util.StringUtils; - -/** - * @author Oliver Gierke - * @author Mark Paluch - */ -@CliComponent -@RequiredArgsConstructor -@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) -public class IssueTrackerCommands extends TimedCommand { - - @NonNull PluginRegistry tracker; - @NonNull Executor executor; - - @CliCommand("tracker evict") - public void evict() { - StreamSupport.stream(tracker.spliterator(), false).forEach(IssueTracker::reset); - } - - @CliCommand(value = "tracker tickets") - public String jira(@CliOption(key = "", mandatory = true) TrainIteration iteration, // - @CliOption(key = "for-current-user", specifiedDefaultValue = "true", - unspecifiedDefaultValue = "false") boolean forCurrentUser) { - - return tracker.getPlugins().stream().// - flatMap(it -> it.getTicketsFor(iteration, forCurrentUser).stream()).// - collect(Tickets.toTicketsCollector()).toString(); - } - - @CliCommand(value = "tracker releasetickets") - public String releaseTickets(@CliOption(key = "", mandatory = true) TrainIteration iteration) { - return runAndReturn(executor, iteration, module -> getTrackerFor(module).getReleaseTicketFor(module), - Tickets.toTicketsCollector()).toString(); - } - - /** - * Prepare this release by self-assigning release tickets and setting them to in-progress. - * - * @param iteration - * @return - */ - @CliCommand(value = "tracker prepare") - public String trackerPrepare(@CliOption(key = "", mandatory = true) TrainIteration iteration) { - - jiraSelfAssignReleaseTickets(iteration); - - return jiraStartProgress(iteration); - } - - /** - * Prepare a new, upcoming release by creating release versions and release tickets. - * - * @param iteration - * @return - */ - @CliCommand(value = "tracker setup-next") - public String trackerSetupNext(@CliOption(key = "", mandatory = true) TrainIteration iteration) { - - jiraCreateReleaseVersions(iteration); - - return createReleaseTickets(iteration); - } - - @CliCommand(value = "tracker self-assign releasetickets") - public String jiraSelfAssignReleaseTickets(@CliOption(key = "", mandatory = true) TrainIteration iteration) { - - return runAndReturn(executor, iteration, module -> getTrackerFor(module).assignReleaseTicketToMe(module), - Tickets.toTicketsCollector()).toString(); - } - - public String jiraStartProgress(@CliOption(key = "", mandatory = true) TrainIteration iteration) { - - return runAndReturn(executor, iteration, module -> getTrackerFor(module).startReleaseTicketProgress(module), - Tickets.toTicketsCollector()).toString(); - } - - @CliCommand(value = "tracker create releaseversions") - public void jiraCreateReleaseVersions(@CliOption(key = "", mandatory = true) TrainIteration iteration) { - run(executor, iteration, module -> getTrackerFor(module).createReleaseVersion(module)); - } - - @CliCommand(value = "tracker create releasetickets") - public String createReleaseTickets(@CliOption(key = "", mandatory = true) TrainIteration iteration) { - - run(executor, iteration, module -> getTrackerFor(module).createReleaseTicket(module)); - evict(); - - return releaseTickets(iteration); - } - - @CliCommand(value = "tracker create tickets") - public String createTickets(@CliOption(key = "iteration", mandatory = true) TrainIteration iteration, - @CliOption(key = "text", mandatory = true) String text) { - - return iteration.stream().// - map(module -> getTrackerFor(module).createTicket(module, text, IssueTracker.TicketType.Task, false)) - .collect(Tickets.toTicketsCollector()) - .toString(); - } - - @CliCommand("tracker open-tickets") - public String openTickets(@CliOption(key = "", mandatory = true) TrainIteration iteration, // - @CliOption(key = "module") String moduleName, - @CliOption(key = "filter-release-tickets") Boolean filterReleaseTickets) { - - Predicate notResolved = it -> !it.isResolved(); - - return getTickets(iteration, moduleName, - notResolved.and(getFilterPredicate(filterReleaseTickets == null || filterReleaseTickets))); - } - - @CliCommand("tracker all-tickets") - public String allTickets(@CliOption(key = "", mandatory = true) TrainIteration iteration, // - @CliOption(key = "module") String moduleName, - @CliOption(key = "filter-release-tickets") Boolean filterReleaseTickets) { - - return getTickets(iteration, moduleName, getFilterPredicate(filterReleaseTickets == null || filterReleaseTickets)); - } - - private static Predicate getFilterPredicate(boolean filterReleaseTickets) { - return it -> filterReleaseTickets == !it.isReleaseTicket(); - } - - private String getTickets(TrainIteration iteration, String moduleName, Predicate ticketPredicate) { - - if (StringUtils.hasText(moduleName)) { - return getTicketsForProject(iteration, Projects.requiredByName(moduleName), ticketPredicate); - } - - return ExecutionUtils.runAndReturn(executor, iteration, - moduleIteration -> { - return getTicketsForProject(iteration, moduleIteration.getModule().getProject(), ticketPredicate); - }) - .stream() // - .filter(StringUtils::hasText) // - .collect(Collectors.joining("\n")); - } - - private String getTicketsForProject(TrainIteration iteration, Project project, Predicate ticketPredicate) { - - ModuleIteration module = iteration.getModule(project); - - return getTrackerFor(module).getTicketsFor(module) // - .stream() // - .filter(ticketPredicate) // - .collect(Tickets.toTicketsCollector()) // - .toString(false); - } - - @CliCommand("tracker close") - public void closeIteration(@CliOption(key = "", mandatory = true) TrainIteration iteration) { - run(executor, iteration, module -> getTrackerFor(module).closeIteration(module)); - } - - @CliCommand("tracker archive") - public void archiveIteration(@CliOption(key = "", mandatory = true) TrainIteration iteration) { - run(executor, iteration, module -> getTrackerFor(module).archiveReleaseVersion(module)); - } - - private IssueTracker getTrackerFor(ModuleIteration moduleIteration) { - - return tracker.getRequiredPluginFor(moduleIteration.getProject(), - () -> String.format("No issue tracker found for module %s!", moduleIteration)); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/issues/IssueTrackerConfiguration.java b/release-tools/src/main/java/org/springframework/data/release/issues/IssueTrackerConfiguration.java deleted file mode 100644 index e30263f..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/issues/IssueTrackerConfiguration.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.issues; - -import java.io.IOException; -import java.net.URI; -import java.util.List; - -import org.apache.http.HttpHost; -import org.apache.http.auth.AuthScope; -import org.apache.http.auth.UsernamePasswordCredentials; -import org.apache.http.client.AuthCache; -import org.apache.http.client.CredentialsProvider; -import org.apache.http.client.protocol.HttpClientContext; -import org.apache.http.impl.auth.BasicScheme; -import org.apache.http.impl.client.BasicAuthCache; -import org.apache.http.impl.client.BasicCredentialsProvider; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; - -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.web.client.RestTemplateBuilder; -import org.springframework.cache.CacheManager; -import org.springframework.cache.annotation.EnableCaching; -import org.springframework.cache.concurrent.ConcurrentMapCacheManager; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.release.issues.github.GitHubProperties; -import org.springframework.data.release.model.Project; -import org.springframework.data.release.utils.HttpBasicCredentials; -import org.springframework.data.util.Lazy; -import org.springframework.http.HttpMethod; -import org.springframework.http.client.ClientHttpRequest; -import org.springframework.http.client.ClientHttpRequestFactory; -import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.plugin.core.OrderAwarePluginRegistry; -import org.springframework.plugin.core.PluginRegistry; - -import com.fasterxml.jackson.annotation.JsonCreator.Mode; -import com.fasterxml.jackson.annotation.JsonInclude.Include; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.introspect.AnnotatedMember; -import com.fasterxml.jackson.databind.introspect.AnnotatedMethod; -import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector; -import com.fasterxml.jackson.databind.module.SimpleModule; -import com.fasterxml.jackson.module.paramnames.ParameterNamesModule; - -/** - * @author Oliver Gierke - * @author Mark Paluch - */ -@Configuration(proxyBeanMethods = false) -@EnableCaching(proxyTargetClass = true) -class IssueTrackerConfiguration { - - @Bean - CacheManager cacheManager() { - return new ConcurrentMapCacheManager(); - } - - @Bean - ObjectMapper jacksonObjectMapper() { - - ObjectMapper mapper = new ObjectMapper(); - - mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - mapper.setSerializationInclusion(Include.NON_NULL); - mapper.registerModule(new ParameterNamesModule(Mode.PROPERTIES)); - mapper.registerModule(new SyntheticLambdaFactoryMethodIgnoringModule()); - - return mapper; - } - - @Bean - HttpComponentsClientHttpRequestFactory clientHttpRequestFactory(GitHubProperties gitHubProperties) { - - // Preemptive auth - CredentialsProvider credsProvider = new BasicCredentialsProvider(); - AuthCache authCache = new BasicAuthCache(); - - addPreemptiveAuth(credsProvider, authCache, gitHubProperties.getApiUrl(), gitHubProperties.getHttpCredentials()); - - Lazy lazy = Lazy - .of(() -> HttpClientBuilder.create().setDefaultCredentialsProvider(credsProvider).build()); - - HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory() { - @Override - public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException { - setHttpClient(lazy.get()); - return super.createRequest(uri, httpMethod); - } - }; - - factory.setHttpContextFactory((httpMethod, uri) -> { - HttpClientContext context = HttpClientContext.create(); - context.setAuthCache(authCache); - return context; - }); - - return factory; - } - - @Bean - @Qualifier("tracker") - RestTemplateBuilder restTemplate(ClientHttpRequestFactory clientHttpRequestFactory, - ObjectMapper jacksonObjectMapper) { - - MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); - converter.setObjectMapper(jacksonObjectMapper); - - return new RestTemplateBuilder().messageConverters(converter).requestFactory(() -> clientHttpRequestFactory); - } - - @Bean - PluginRegistry issueTrackers(List plugins) { - return OrderAwarePluginRegistry.of(plugins); - } - - private static void addPreemptiveAuth(CredentialsProvider credsProvider, AuthCache authCache, String requestUrl, - HttpBasicCredentials credentials) { - HttpHost jiraHost = HttpHost.create(requestUrl); - - credsProvider.setCredentials(new AuthScope(jiraHost), - new UsernamePasswordCredentials(credentials.getUsername(), credentials.getPassword().toString())); - - authCache.put(jiraHost, new BasicScheme()); - } - - /** - * @author Oliver Gierke - */ - static class SyntheticLambdaFactoryMethodIgnoringModule extends SimpleModule { - - private static final long serialVersionUID = -3075786389813846512L; - - @Override - public void setupModule(SetupContext context) { - - context.insertAnnotationIntrospector(new NopAnnotationIntrospector() { - - private static final long serialVersionUID = 479313244908256455L; - - @Override - public boolean hasIgnoreMarker(AnnotatedMember m) { - - if (!(m instanceof AnnotatedMethod)) { - return super.hasIgnoreMarker(m); - } - - AnnotatedMethod method = (AnnotatedMethod) m; - - return method.getName().startsWith("lambda$") ? true : super.hasIgnoreMarker(m); - } - }); - } - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/issues/Ticket.java b/release-tools/src/main/java/org/springframework/data/release/issues/Ticket.java deleted file mode 100644 index ab07e73..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/issues/Ticket.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.issues; - -import lombok.Value; - -import org.springframework.data.release.model.ModuleIteration; -import org.springframework.data.release.model.Tracker; -import org.springframework.data.release.model.TrainIteration; -import org.springframework.util.Assert; - -/** - * Value object to represent a {@link Ticket}. - * - * @author Oliver Gierke - * @author Mark Paluch - */ -@Value -public class Ticket { - - String id, summary; - String url; - String assignee; - TicketStatus ticketStatus; - - public Ticket(String id, String summary, String url, String assignee, TicketStatus ticketStatus) { - this.id = id; - this.summary = summary; - this.url = url; - this.assignee = assignee; - this.ticketStatus = ticketStatus; - } - - public Ticket(String id, String summary, TicketStatus ticketStatus) { - this.id = id; - this.summary = summary; - this.assignee = null; - this.url = null; - this.ticketStatus = ticketStatus; - } - - /* - * (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return String.format("%14s - %s (%s)", id, summary, url); - } - - public boolean isResolved() { - return ticketStatus.isResolved(); - } - - /** - * Returns whether the current {@link Ticket} is the release ticket for the given {@link ModuleIteration}. - * - * @param module must not be {@literal null}. - * @return - */ - public boolean isReleaseTicketFor(ModuleIteration module) { - - Assert.notNull(module, "Module must not be null!"); - return summary.startsWith(Tracker.releaseTicketSummary(module)); - } - - /** - * Returns whether the current {@link Ticket} is the release ticket by checking the summary prefix. - * - * @return - */ - public boolean isReleaseTicket() { - return summary.startsWith(Tracker.RELEASE_PREFIX); - } - - /** - * Returns whether the current {@link Ticket} is a release ticket for the given {@link TrainIteration}. - * - * @param train must not be {@literal null}. - * @return - */ - public boolean isReleaseTicketFor(TrainIteration train) { - return train.stream().anyMatch(this::isReleaseTicketFor); - } - - public boolean isAssignedTo(String username) { - return username.equals(assignee); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/issues/TicketOperations.java b/release-tools/src/main/java/org/springframework/data/release/issues/TicketOperations.java deleted file mode 100644 index 90f7800..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/issues/TicketOperations.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.issues; - -import lombok.AccessLevel; -import lombok.RequiredArgsConstructor; -import lombok.experimental.FieldDefaults; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -import org.springframework.data.release.model.ModuleIteration; -import org.springframework.data.release.model.Project; -import org.springframework.data.release.utils.Logger; -import org.springframework.plugin.core.PluginRegistry; -import org.springframework.stereotype.Component; - -/** - * @author Mark Paluch - */ -@Component -@RequiredArgsConstructor -@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) -public class TicketOperations { - - Logger logger; - - PluginRegistry tracker; - - /** - * Create or look up ticket with a particular summary. - * - * @param module - * @param summary - * @return - */ - public Ticket getOrCreateTicketsWithSummary(ModuleIteration module, IssueTracker.TicketType ticketType, - String summary) { - return getOrCreateTicketsWithSummary(module, ticketType, Collections.singletonList(summary)).getTickets().get(0); - } - - /** - * Create or look up tickets with a particular summary. - * - * @param module - * @param ticketType - * @param summaries - * @return - */ - public Tickets getOrCreateTicketsWithSummary(ModuleIteration module, IssueTracker.TicketType ticketType, - List summaries) { - - Project project = module.getProject(); - - IssueTracker tracker = this.tracker.getRequiredPluginFor(project); - Tickets tickets = tracker.getTicketsFor(module); - List results = new ArrayList<>(); - - for (String summary : summaries) { - - Optional upgradeTicket = findBySummary(tickets, summary); - - if (upgradeTicket.isPresent()) { - logger.log(project, "Found ticket %s", upgradeTicket.get()); - upgradeTicket.ifPresent(it -> { - tracker.assignTicketToMe(project, it); - results.add(it); - }); - } else { - - logger.log(module, "Creating ticket for %s", summary); - Ticket ticket = tracker.createTicket(module, summary, ticketType, true); - results.add(ticket); - } - } - - return new Tickets(results); - } - - private Optional findBySummary(Tickets tickets, String summary) { - - List result = tickets.filter(it -> it.getSummary().equals(summary)).toList(); - - if (result.size() > 1) { - throw new IllegalStateException("Multiple tickets found: " + result); - } - - return Optional.ofNullable(result.isEmpty() ? null : result.get(0)); - } - - public void closeTicket(ModuleIteration module, Ticket ticket) { - closeTickets(module, new Tickets(Collections.singletonList(ticket))); - } - - public void closeTickets(ModuleIteration module, Tickets tickets) { - - IssueTracker tracker = this.tracker.getRequiredPluginFor(module.getProject()); - - for (Ticket ticket : tickets) { - tracker.closeTicket(module, ticket); - } - } - -} diff --git a/release-tools/src/main/java/org/springframework/data/release/issues/TicketReference.java b/release-tools/src/main/java/org/springframework/data/release/issues/TicketReference.java deleted file mode 100644 index 939756b..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/issues/TicketReference.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2020-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.issues; - -import lombok.Value; - -/** - * @author Mark Paluch - */ -@Value -public class TicketReference implements Comparable { - - String id; - String message; - Style style; - - public TicketReference(String id, String message, Style style) { - this.id = normalize(id); - this.message = message; - this.style = style; - } - - private static String normalize(String id) { - - if (id.toLowerCase().startsWith("gh-")) { - return "#" + id.substring(3); - } - - return id.toUpperCase().replaceAll(" ", ""); - } - - @Override - public int compareTo(TicketReference o) { - - if (id.startsWith("#") && o.id.startsWith("#")) { - return Integer.compare(Integer.parseInt(id.substring(1)), Integer.parseInt(o.id.substring(1))); - } - - return id.compareToIgnoreCase(o.id); - } - - public enum Style { - GitHub, Jira; - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/issues/TicketStatus.java b/release-tools/src/main/java/org/springframework/data/release/issues/TicketStatus.java deleted file mode 100644 index 40c2e95..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/issues/TicketStatus.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2016-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.data.release.issues; - -/** - * @author Mark Paluch - */ -public interface TicketStatus { - - /** - * Returns - * - * @return - */ - String getLabel(); - - /** - * Returns whether the ticket is marked as resolved. - * - * @return - */ - boolean isResolved(); -} diff --git a/release-tools/src/main/java/org/springframework/data/release/issues/Tickets.java b/release-tools/src/main/java/org/springframework/data/release/issues/Tickets.java deleted file mode 100644 index 2b76737..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/issues/Tickets.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.issues; - -import lombok.RequiredArgsConstructor; -import lombok.Value; - -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collector; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.apache.commons.io.IOUtils; - -import org.springframework.data.release.model.ModuleIteration; -import org.springframework.data.release.model.Tracker; -import org.springframework.data.release.model.TrainIteration; -import org.springframework.data.release.utils.ListWrapperCollector; -import org.springframework.data.util.Streamable; - -/** - * Value object to represent a list of {@link Ticket}s. - * - * @author Oliver Gierke - * @author Mark Paluch - */ -@Value -@RequiredArgsConstructor -public class Tickets implements Streamable { - - List tickets; - int overallTotal; - - public Tickets(List tickets) { - this(Collections.unmodifiableList(tickets), tickets.size()); - } - - /* - * (non-Javadoc) - * @see java.lang.Iterable#iterator() - */ - @Override - public Iterator iterator() { - return tickets.iterator(); - } - - public boolean hasReleaseTicket(ModuleIteration moduleIteration) { - return findReleaseTicket(moduleIteration).isPresent(); - } - - public Ticket getReleaseTicket(ModuleIteration moduleIteration) { - - return findReleaseTicket(moduleIteration).orElseThrow( - () -> new IllegalArgumentException(String.format("Did not find a release ticket for %s containing %s!", - moduleIteration, Tracker.releaseTicketSummary(moduleIteration)))); - } - - public Tickets getIssueTickets(ModuleIteration moduleIteration) { - return tickets.stream(). // - filter(ticket -> !ticket.isReleaseTicketFor(moduleIteration)).// - collect(toTicketsCollector()); - } - - public Tickets getReleaseTickets(TrainIteration iteration) { - - return stream().// - filter(ticket -> ticket.isReleaseTicketFor(iteration)).// - distinct().// - collect(toTicketsCollector()); - } - - private Optional findReleaseTicket(ModuleIteration moduleIteration) { - - return stream().// - filter(ticket -> ticket.isReleaseTicketFor(moduleIteration)).// - findFirst(); - } - - /* - * (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return toString(true); - } - - public String toString(boolean header) { - - StringBuilder builder = new StringBuilder(); - - if (header) { - builder.append(String.format("Train only tickets: %s of %s", tickets.size(), overallTotal)); - builder.append(IOUtils.LINE_SEPARATOR); - } - builder.append(tickets.stream().map(it -> "\t" + it).collect(Collectors.joining(IOUtils.LINE_SEPARATOR))); - - return builder.toString(); - } - - /** - * Returns a new collector to toTicketsCollector {@link Ticket} as {@link Tickets} using the {@link Stream} API. - * - * @return - */ - public static Collector toTicketsCollector() { - return ListWrapperCollector.collectInto(Tickets::new); - } -} diff --git a/release-tools/src/main/java/org/springframework/data/release/issues/github/ChangelogGenerator.java b/release-tools/src/main/java/org/springframework/data/release/issues/github/ChangelogGenerator.java deleted file mode 100644 index d617e4c..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/issues/github/ChangelogGenerator.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2020-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.data.release.issues.github; - -import lombok.Getter; - -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.BiFunction; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import org.springframework.stereotype.Component; - -/** - * Generates a changelog markdown file which includes bug fixes, enhancements and contributors for a given milestone. - * - * @author Madhura Bhave - * @author Phillip Webb - */ -@Component -public class ChangelogGenerator { - - private static final Pattern ghUserMentionPattern = Pattern.compile("(^|[^\\w`])(@[\\w-]+)"); - - @Getter - private final Set excludeLabels; - - @Getter - private final Set excludeContributors; - - @Getter - private final String contributorsTitle; - - private final ChangelogSections sections; - - public ChangelogGenerator() { - this.excludeLabels = new HashSet<>(Arrays.asList("type: task")); - this.excludeContributors = new LinkedHashSet<>(); - this.contributorsTitle = null; - this.sections = new ChangelogSections(); - } - - /** - * Generates a file at the given path which includes bug fixes, enhancements and contributors for the given milestone. - * - * @param issues the issues to generate the changelog for - * @param sectionContentPostProcessor the postprocessor for a changelog section - */ - public String generate(List issues, - BiFunction sectionContentPostProcessor) { - return generateContent(issues, sectionContentPostProcessor); - } - - private boolean isExcluded(GitHubReadIssue issue) { - return issue.getLabels().stream().anyMatch(this::isExcluded); - } - - private boolean isExcluded(Label label) { - return this.excludeLabels.contains(label.getName()); - } - - private String generateContent(List issues, - BiFunction sectionContentPostProcessor) { - StringBuilder content = new StringBuilder(); - addSectionContent(content, this.sections.collate(issues), sectionContentPostProcessor); - Set contributors = getContributors(issues); - if (!contributors.isEmpty()) { - addContributorsContent(content, contributors); - } - return content.toString(); - } - - private void addSectionContent(StringBuilder result, Map> sectionIssues, - BiFunction sectionContentPostProcessor) { - - sectionIssues.forEach((section, issues) -> { - - issues.sort(Comparator.reverseOrder()); - - StringBuilder content = new StringBuilder(); - - content.append((content.length() != 0) ? String.format("%n") : ""); - content.append("## ").append(section).append(String.format("%n%n")); - issues.stream().map(this::getFormattedIssue).forEach(content::append); - - result.append(sectionContentPostProcessor.apply(section, content.toString())); - }); - } - - private String getFormattedIssue(GitHubReadIssue issue) { - String title = issue.getTitle(); - title = ghUserMentionPattern.matcher(title).replaceAll("$1`$2`"); - return String.format("- %s %s%n", title, getLinkToIssue(issue)); - } - - private String getLinkToIssue(GitHubIssue issue) { - return "[" + issue.getId() + "]" + "(" + issue.getUrl() + ")"; - } - - private Set getContributors(List issues) { - if (this.excludeContributors.contains("*")) { - return Collections.emptySet(); - } - return issues.stream().filter((issue) -> issue.getPullRequest() != null).map(GitHubReadIssue::getUser) - .filter(this::isIncludedContributor).collect(Collectors.toSet()); - } - - private boolean isIncludedContributor(GitHubUser user) { - return !this.excludeContributors.contains(user.getName()); - } - - private void addContributorsContent(StringBuilder content, Set contributors) { - content.append(String.format("%n## ")); - content.append((this.contributorsTitle != null) ? this.contributorsTitle : ":heart: Contributors"); - content.append(String.format("%n%nWe'd like to thank all the contributors who worked on this release!%n%n")); - contributors.stream().map(this::formatContributors).forEach(content::append); - } - - private String formatContributors(GitHubUser c) { - return String.format("- [@%s](%s)%n", c.getName(), c.getUrl()); - } - -} diff --git a/release-tools/src/main/java/org/springframework/data/release/issues/github/ChangelogSection.java b/release-tools/src/main/java/org/springframework/data/release/issues/github/ChangelogSection.java deleted file mode 100644 index 4ff7e10..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/issues/github/ChangelogSection.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2020-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.data.release.issues.github; - -import java.util.Arrays; -import java.util.LinkedHashSet; -import java.util.Set; - -import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; - -/** - * A single section of a changelog report. - * - * @author Phillip Webb - */ -class ChangelogSection { - - private final String title; - - private final String group; - - private final Set labels; - - ChangelogSection(String title, String group, String... labels) { - this(title, group, new LinkedHashSet<>(Arrays.asList(labels))); - } - - ChangelogSection(String title, String group, Set labels) { - Assert.hasText(title, "Title must not be empty"); - Assert.isTrue(!CollectionUtils.isEmpty(labels), "Labels must not be empty"); - this.title = title; - this.group = group; - this.labels = labels; - } - - String getGroup() { - return this.group; - } - - boolean isMatchFor(GitHubReadIssue issue) { - for (String candidate : this.labels) { - for (Label label : issue.getLabels()) { - if (label.getName().contains(candidate)) { - return true; - } - } - } - return false; - } - - public boolean hasLabel(String label) { - return this.labels.contains(label); - } - - @Override - public String toString() { - return this.title; - } - -} diff --git a/release-tools/src/main/java/org/springframework/data/release/issues/github/ChangelogSections.java b/release-tools/src/main/java/org/springframework/data/release/issues/github/ChangelogSections.java deleted file mode 100644 index fabc3ef..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/issues/github/ChangelogSections.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2020-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.issues.github; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.SortedMap; -import java.util.TreeMap; - -/** - * Manages sections of the changelog report. - * - * @author Phillip Webb - */ -class ChangelogSections { - - private static final List DEFAULT_SECTIONS; - static { - List sections = new ArrayList<>(); - add(sections, ":star: New Features", "enhancement"); - add(sections, ":lady_beetle: Bug Fixes", "bug", "regression"); - add(sections, ":notebook_with_decorative_cover: Documentation", "documentation"); - add(sections, ":hammer: Dependency Upgrades", "dependency-upgrade"); - DEFAULT_SECTIONS = Collections.unmodifiableList(sections); - } - - private static void add(List sections, String title, String... labels) { - sections.add(new ChangelogSection(title, null, labels)); - } - - private final List sections; - - ChangelogSections() { - this.sections = DEFAULT_SECTIONS; - } - - Map> collate(List issues) { - - SortedMap> collated = new TreeMap<>( - Comparator.comparing(this.sections::indexOf)); - - for (GitHubReadIssue issue : issues) { - List sections = getSections(issue); - for (ChangelogSection section : sections) { - collated.computeIfAbsent(section, (key) -> new ArrayList<>()); - collated.get(section).add(issue); - } - } - return collated; - } - - private List getSections(GitHubReadIssue issue) { - List result = new ArrayList<>(); - Set groupClaimes = new HashSet<>(); - for (ChangelogSection section : this.sections) { - if (section.isMatchFor(issue) && groupClaimes.add(section.getGroup())) { - result.add(section); - } - } - return result; - } - -} diff --git a/release-tools/src/main/java/org/springframework/data/release/issues/github/GitHub.java b/release-tools/src/main/java/org/springframework/data/release/issues/github/GitHub.java deleted file mode 100644 index 9c0e424..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/issues/github/GitHub.java +++ /dev/null @@ -1,733 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.issues.github; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.web.client.RestTemplateBuilder; -import org.springframework.cache.annotation.CacheEvict; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.data.release.git.GitProject; -import org.springframework.data.release.git.Tag; -import org.springframework.data.release.git.VersionTags; -import org.springframework.data.release.issues.Changelog; -import org.springframework.data.release.issues.IssueTracker; -import org.springframework.data.release.issues.Ticket; -import org.springframework.data.release.issues.Tickets; -import org.springframework.data.release.model.ArtifactVersion; -import org.springframework.data.release.model.DocumentationMetadata; -import org.springframework.data.release.model.Iteration; -import org.springframework.data.release.model.ModuleIteration; -import org.springframework.data.release.model.Project; -import org.springframework.data.release.model.Projects; -import org.springframework.data.release.model.Tracker; -import org.springframework.data.release.model.TrainIteration; -import org.springframework.data.release.utils.Logger; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Component; -import org.springframework.util.Assert; -import org.springframework.web.client.HttpStatusCodeException; - -/** - * @author Oliver Gierke - * @author Mark Paluch - */ -@Component -public class GitHub extends GitHubSupport implements IssueTracker { - - private static final String MILESTONE_URI = "/repos/spring-projects/{repoName}/milestones?state={state}"; - private static final String ISSUES_BY_MILESTONE_AND_ASSIGNEE_URI_TEMPLATE = "/repos/spring-projects/{repoName}/issues?milestone={id}&state=all&assignee={assignee}"; - private static final String ISSUES_BY_MILESTONE_URI_TEMPLATE = "/repos/spring-projects/{repoName}/issues?milestone={id}&state=all"; - private static final String MILESTONES_URI_TEMPLATE = "/repos/spring-projects/{repoName}/milestones"; - private static final String MILESTONE_BY_ID_URI_TEMPLATE = "/repos/spring-projects/{repoName}/milestones/{id}"; - private static final String ISSUE_BY_ID_URI_TEMPLATE = "/repos/spring-projects/{repoName}/issues/{id}"; - private static final String ISSUES_URI_TEMPLATE = "/repos/spring-projects/{repoName}/issues"; - private static final String RELEASE_BY_TAG_URI_TEMPLATE = "/repos/spring-projects/{repoName}/releases/tags/{tag}"; - private static final String RELEASE_URI_TEMPLATE = "/repos/spring-projects/{repoName}/releases"; - private static final String RELEASE_BY_ID_URI_TEMPLATE = "/repos/spring-projects/{repoName}/releases/{id}"; - - private static final ParameterizedTypeReference> MILESTONES_TYPE = new ParameterizedTypeReference>() {}; - private static final ParameterizedTypeReference> ISSUES_TYPE = new ParameterizedTypeReference>() {}; - private static final ParameterizedTypeReference ISSUE_TYPE = new ParameterizedTypeReference() {}; - - private static final Map TICKET_LABELS = new HashMap<>(); - - static { - - TICKET_LABELS.put(TicketType.Task, LabelConfiguration.TYPE_TASK); - TICKET_LABELS.put(TicketType.DependencyUpgrade, LabelConfiguration.TYPE_DEPENDENCY_UPGRADE); - } - - private final Logger logger; - private final GitHubProperties properties; - - - public GitHub(@Qualifier("tracker") RestTemplateBuilder templateBuilder, Logger logger, GitHubProperties properties) { - - super(createOperations(templateBuilder, properties)); - this.logger = logger; - this.properties = properties; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.release.jira.JiraConnector#flushTickets() - */ - @Override - @CacheEvict(value = { "tickets", "release-tickets", "milestone" }, allEntries = true) - public void reset() { - - } - - /* - * (non-Javadoc) - * @see org.springframework.data.release.jira.IssueTracker#getReleaseTicketFor(org.springframework.data.release.model.ModuleIteration) - */ - @Override - @Cacheable("release-tickets") - public Ticket getReleaseTicketFor(ModuleIteration module) { - return getTicketsFor(module).getReleaseTicket(module); - } - - /* - * (non-Javadoc) - * - * @see IssueTracker#findTickets(Project, Collection) - */ - @Override - @Cacheable("tickets") - public Collection findTickets(Project project, Collection ticketIds) { - - String repositoryName = GitProject.of(project).getRepositoryName(); - List tickets = new ArrayList<>(); - - ticketIds.forEach(ticketId -> { - - GitHubReadIssue ticket = findTicket(repositoryName, ticketId); - if (ticket != null) { - tickets.add(toTicket(ticket)); - } - }); - - return tickets; - } - - @Override - public Tickets findTickets(ModuleIteration moduleIteration, Collection ticketIds) { - - return findGitHubIssues(moduleIteration, ticketIds).stream().map(GitHub::toTicket) - .collect(Tickets.toTicketsCollector()); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.release.jira.IssueTracker#getChangelogFor(org.springframework.data.release.model.ModuleIteration) - */ - @Override - @Cacheable("changelogs") - public Changelog getChangelogFor(ModuleIteration moduleIteration) { - - Tickets tickets = getIssuesFor(moduleIteration, false, false).// - map(issue -> toTicket(issue)).// - collect(Tickets.toTicketsCollector()); - - logger.log(moduleIteration, "Created changelog with %s entries.", tickets.getOverallTotal()); - - return Changelog.of(moduleIteration, tickets); - } - - /* - * (non-Javadoc) - * @see org.springframework.plugin.core.Plugin#supports(java.lang.Object) - */ - @Override - public boolean supports(Project project) { - return project.uses(Tracker.GITHUB); - } - - @Override - public Tickets getTicketsFor(TrainIteration iteration) { - return getTicketsFor(iteration, false); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.release.jira.GitHubIssueConnector#getTicketsFor(org.springframework.data.release.model.TrainIteration, boolean) - */ - @Override - public Tickets getTicketsFor(TrainIteration trainIteration, boolean forCurrentUser) { - - if (forCurrentUser) { - logger.log(trainIteration, "Retrieving tickets (for user %s)โ€ฆ", properties.getUsername()); - } else { - logger.log(trainIteration, "Retrieving ticketsโ€ฆ"); - } - - Tickets tickets = trainIteration.stream(). // - filter(moduleIteration -> supports(moduleIteration.getProject())). // - flatMap(moduleIteration -> getTicketsFor(moduleIteration, forCurrentUser).stream()). // - collect(Tickets.toTicketsCollector()); - - return tickets; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.release.tracker.IssueTracker#createReleaseVersion(org.springframework.data.release.model.ModuleIteration) - */ - @Override - public void createReleaseVersion(ModuleIteration moduleIteration) { - - Assert.notNull(moduleIteration, "ModuleIteration must not be null."); - - String repositoryName = GitProject.of(moduleIteration.getProject()).getRepositoryName(); - Optional milestone = findMilestone(moduleIteration, repositoryName); - - if (milestone.isPresent()) { - return; - } - - GithubMilestone githubMilestone = new GithubMilestone(moduleIteration); - logger.log(moduleIteration, "Creating GitHub milestone %s", githubMilestone); - - HttpHeaders httpHeaders = new HttpHeaders(); - Map parameters = newUrlTemplateVariables(); - parameters.put("repoName", repositoryName); - - operations.exchange(MILESTONES_URI_TEMPLATE, HttpMethod.POST, - new HttpEntity(githubMilestone.toMilestone(), httpHeaders), Milestone.class, parameters); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.release.tracker.IssueTracker#retireReleaseVersion(org.springframework.data.release.model.ModuleIteration) - */ - @Override - public void archiveReleaseVersion(ModuleIteration module) { - logger.log(module, "Skipping milestone archival"); - } - - /* - * - * (non-Javadoc) - * @see org.springframework.data.release.tracker.IssueTracker#createReleaseTicket(org.springframework.data.release.model.ModuleIteration) - */ - @Override - public void createReleaseTicket(ModuleIteration moduleIteration) { - - Assert.notNull(moduleIteration, "ModuleIteration must not be null."); - - Tickets tickets = getTicketsFor(moduleIteration); - if (tickets.hasReleaseTicket(moduleIteration)) { - return; - } - - logger.log(moduleIteration, "Creating release ticketโ€ฆ"); - - doCreateTicket(moduleIteration, Tracker.releaseTicketSummary(moduleIteration), TicketType.Task, false); - } - - @Override - public Ticket createTicket(ModuleIteration moduleIteration, String text, TicketType ticketType, - boolean assignToCurrentUser) { - - logger.log(moduleIteration, "Creating ticketโ€ฆ"); - - return doCreateTicket(moduleIteration, text, ticketType, assignToCurrentUser); - } - - private Ticket doCreateTicket(ModuleIteration moduleIteration, String text, TicketType ticketType, - boolean assignToCurrentUser) { - - String repositoryName = GitProject.of(moduleIteration.getProject()).getRepositoryName(); - Milestone milestone = getMilestone(moduleIteration, repositoryName); - - Label label = TICKET_LABELS.get(ticketType); - - GitHubWriteIssue gitHubIssue = GitHubWriteIssue.of(text, milestone).withLabel(label.getName()); - - if (assignToCurrentUser) { - gitHubIssue = gitHubIssue.withAssignees(Collections.singletonList(properties.getUsername())); - } - - Map parameters = newUrlTemplateVariables(); - parameters.put("repoName", repositoryName); - - GitHubReadIssue body = operations.exchange(ISSUES_URI_TEMPLATE, HttpMethod.POST, - new HttpEntity(gitHubIssue), GitHubReadIssue.class, parameters).getBody(); - - return toTicket(body); - } - - @Cacheable("tickets") - public Tickets getTicketsFor(ModuleIteration iteration) { - return getTicketsFor(iteration, false); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.release.jira.IssueTracker#assignTicketToMe(org.springframework.data.release.jira.Ticket) - */ - @Override - public Ticket assignTicketToMe(Project project, Ticket ticket) { - - Assert.notNull(ticket, "Ticket must not be null."); - - if (ticket.isAssignedTo(properties.getUsername())) { - logger.log("Ticket", "Skipping self-assignment of %s", ticket); - return ticket; - } - - String repositoryName = GitProject.of(project).getRepositoryName(); - Map parameters = newUrlTemplateVariables(); - parameters.put("repoName", repositoryName); - parameters.put("id", stripHash(ticket)); - - GitHubWriteIssue edit = GitHubWriteIssue.assignedTo(properties.getUsername()); - - GitHubReadIssue response = operations.exchange(ISSUE_BY_ID_URI_TEMPLATE, HttpMethod.PATCH, - new HttpEntity<>(edit, new HttpHeaders()), ISSUE_TYPE, parameters).getBody(); - - return toTicket(response); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.release.jira.IssueTracker#assignReleaseTicketToMe(org.springframework.data.release.model.ModuleIteration) - */ - @Override - public Ticket assignReleaseTicketToMe(ModuleIteration module) { - - Assert.notNull(module, "ModuleIteration must not be null."); - - return assignTicketToMe(module.getProject(), getReleaseTicketFor(module)); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.release.jira.IssueTracker#startReleaseTicketProgress(org.springframework.data.release.model.ModuleIteration) - */ - @Override - public Ticket startReleaseTicketProgress(ModuleIteration module) { - return getReleaseTicketFor(module); - } - - /** - * Close the release ticket. - * - * @param module - * @return - */ - public Ticket closeReleaseTicket(ModuleIteration module) { - - Assert.notNull(module, "ModuleIteration must not be null."); - - Ticket releaseTicketFor = getReleaseTicketFor(module); - GitHubReadIssue response = close(module, releaseTicketFor); - - return toTicket(response); - } - - private GitHubReadIssue close(ModuleIteration module, Ticket ticket) { - - String repositoryName = GitProject.of(module.getProject()).getRepositoryName(); - - Map parameters = newUrlTemplateVariables(); - parameters.put("repoName", repositoryName); - parameters.put("id", stripHash(ticket)); - - GitHubWriteIssue edit = GitHubWriteIssue.assignedTo(properties.getUsername()).close(); - - return operations.exchange(ISSUE_BY_ID_URI_TEMPLATE, HttpMethod.PATCH, - new HttpEntity<>(edit, new HttpHeaders()), ISSUE_TYPE, parameters).getBody(); - } - - private String stripHash(Ticket ticket) { - return ticket.getId().startsWith("#") ? ticket.getId().substring(1) : ticket.getId(); - } - - private Map newUrlTemplateVariables() { - - Map parameters = new HashMap<>(); - return parameters; - } - - private Optional findMilestone(ModuleIteration moduleIteration, String repositoryName) { - return doFindMilestone(moduleIteration, repositoryName, m -> m.matches(moduleIteration)); - } - - private Optional doFindMilestone(ModuleIteration moduleIteration, String repositoryName, - Predicate milestonePredicate) { - - AtomicReference milestoneRef = new AtomicReference<>(); - - for (String state : Arrays.asList("open", "closed")) { - - Map parameters = newUrlTemplateVariables(); - parameters.put("repoName", repositoryName); - parameters.put("state", state); - - logger.log(moduleIteration, "Looking up milestoneโ€ฆ"); - - doWithPaging(MILESTONE_URI, HttpMethod.GET, parameters, new HttpEntity<>(new HttpHeaders()), - MILESTONES_TYPE, milestones -> { - - Optional milestone = milestones.stream(). // - filter(milestonePredicate). // - findFirst(). // - map(m -> { - logger.log(moduleIteration, "Found milestone %s.", m); - return m; - }); - - if (milestone.isPresent()) { - milestoneRef.set(milestone.get()); - return false; - } - - return true; - }); - - if (milestoneRef.get() != null) { - break; - } - } - - return Optional.ofNullable(milestoneRef.get()); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.release.issues.IssueTracker#closeIteration(org.springframework.data.release.model.ModuleIteration) - */ - @Override - public void closeIteration(ModuleIteration module) { - - // for each module - - // - close all tickets - // -- make sure only one ticket is open - // -- resolve open ticket - // -- close tickets - - // - mark version as released - - HttpHeaders httpHeaders = new HttpHeaders(); - - GitProject project = GitProject.of(module.getProject()); - - findMilestone(module, project.getRepositoryName()) // - .filter(Milestone::isOpen) // - .map(Milestone::markReleased) // - .ifPresent(milestone -> { - - logger.log(module, "Marking milestone %s as released.", milestone); - - Map parameters = newUrlTemplateVariables(); - parameters.put("repoName", project.getRepositoryName()); - parameters.put("id", milestone.getNumber()); - - operations.exchange(MILESTONE_BY_ID_URI_TEMPLATE, HttpMethod.PATCH, - new HttpEntity(milestone, httpHeaders), Map.class, parameters); - }); - - // - if no next version exists, create - - closeReleaseTicket(module); - } - - @Override - public void closeTicket(ModuleIteration module, Ticket ticket) { - close(module, ticket); - } - - List findGitHubIssues(ModuleIteration moduleIteration, Collection ticketIds) { - - logger.log(moduleIteration, "Looking up GitHub issues from milestone โ€ฆ"); - - Map issues = getIssuesFor(moduleIteration, false, true) - .collect(Collectors.toMap(GitHubIssue::getId, Function.identity())); - - String repositoryName = GitProject.of(moduleIteration.getProject()).getRepositoryName(); - - logger.log(moduleIteration, "Looking up GitHub issues โ€ฆ"); - Collection foundIssues = ticketIds.stream().filter(it -> it.startsWith("#")).flatMap(it -> { - - GitHubReadIssue ticket = getTicket(issues, repositoryName, it); - - if (ticket != null) { - return Stream.of(ticket); - } - - return Stream.empty(); - }).collect(Collectors.toList()); - - List gitHubIssues = foundIssues.stream().filter(it -> { - Ticket ticket = toTicket(it); - return !ticket.isReleaseTicketFor(moduleIteration) && !ticket.isReleaseTicket(); - }).collect(Collectors.toList()); - - logger.log(moduleIteration, "Found %s tickets.", gitHubIssues.size()); - - return gitHubIssues; - } - - private GitHubReadIssue getTicket(Map cache, String repositoryName, - String ticketId) { - - if (cache.containsKey(ticketId)) { - return cache.get(ticketId); - } - - return findTicket(repositoryName, ticketId); - } - - private Tickets getTicketsFor(ModuleIteration moduleIteration, boolean forCurrentUser) { - - return getIssuesFor(moduleIteration, forCurrentUser, false).// - map(GitHub::toTicket).// - collect(Tickets.toTicketsCollector()); - } - - /** - * @param repositoryName - * @param ticketId - * @return - */ - private GitHubReadIssue findTicket(String repositoryName, String ticketId) { - - Map parameters = newUrlTemplateVariables(); - parameters.put("repoName", repositoryName); - parameters.put("id", ticketId.startsWith("#") ? ticketId.substring(1) : ticketId); - - try { - - return operations.exchange(ISSUE_BY_ID_URI_TEMPLATE, HttpMethod.GET, - new HttpEntity<>(new HttpHeaders()), ISSUE_TYPE, parameters).getBody(); - } catch (HttpStatusCodeException e) { - - if (e.getStatusCode() == HttpStatus.NOT_FOUND) { - return null; - } - - throw e; - } - } - - /** - * @param module - * @param ticketReferences - */ - public void createOrUpdateRelease(ModuleIteration module, List ticketIds) { - - logger.log(module, "Preparing GitHub Release โ€ฆ"); - - List gitHubIssues = findGitHubIssues(module, ticketIds); - - ArtifactVersion version = ArtifactVersion.of(module); - DocumentationMetadata documentation = DocumentationMetadata.of(module.getProject(), version, false); - - ChangelogGenerator generator = new ChangelogGenerator(); - generator.getExcludeContributors().addAll(properties.getTeam()); - - String releaseBody = generator.generate(gitHubIssues, (changelogSection, s) -> s); - String documentationLinks = getDocumentationLinks(module, documentation); - - if (module.getProject() == Projects.BOM || module.getProject() == Projects.BUILD) { - // We don't ship Javadoc/reference doc for build and BOM - createOrUpdateRelease(module, String.format("%s%n", documentationLinks, releaseBody)); - } else { - createOrUpdateRelease(module, String.format("## :green_book: Links%n%s%n%s%n", documentationLinks, releaseBody)); - } - - logger.log(module, "GitHub Release up to date"); - } - - /** - * Verify GitHub authentication. - */ - public void verifyAuthentication() { - - logger.log("GitHub", "Verifying GitHub Authenticationโ€ฆ"); - - String repositoryName = GitProject.of(Projects.BUILD).getRepositoryName(); - - Map parameters = newUrlTemplateVariables(); - parameters.put("repoName", repositoryName); - - // /user requires authentication - ResponseEntity entity = operations.getForEntity("/user", Object.class); - - if (!entity.getStatusCode().is2xxSuccessful()) { - throw new IllegalStateException(String.format("Cannot obtain /user. Status: %s", entity.getStatusCode())); - } - - logger.log("GitHub", "Authentication verified!"); - } - - private String getDocumentationLinks(ModuleIteration module, DocumentationMetadata documentation) { - - if (module.getProject() == Projects.BUILD || module.getProject() == Projects.BOM) { - return ""; - } - - String referenceDocUrl = documentation.getReferenceDocUrl(); - String apiDocUrl = documentation.getApiDocUrl(); - - String reference = String.format("* [%s %s Reference documentation](%s)", module.getProject().getFullName(), - module.getVersion().toString(), referenceDocUrl); - - String apidoc = String.format("* [%s %s Javadoc](%s)", module.getProject().getFullName(), - module.getVersion().toString(), apiDocUrl); - - return String.format("%s%n%s%n", reference, apidoc); - } - - private void createOrUpdateRelease(ModuleIteration module, String body) { - - String repositoryName = GitProject.of(module.getProject()).getRepositoryName(); - Tag tag = VersionTags.empty(module.getProject()).createTag(module); - logger.log(module, "Looking up GitHub Release โ€ฆ"); - - Iteration iteration = module.getTrainIteration().getIteration(); - boolean prerelase = iteration.isPreview(); - GitHubRelease release = findRelease(repositoryName, tag.getName()); - - if (release == null) { - release = new GitHubRelease(null, tag.getName(), tag.getName(), body, false, prerelase); - logger.log(module, "Creating new Release โ€ฆ"); - createRelease(repositoryName, release); - } else { - release = release.withPrerelease(prerelase).withBody(body); - logger.log(module, "Updating new Release โ€ฆ"); - updateRelease(repositoryName, release); - } - } - - private GitHubRelease findRelease(String repositoryName, String tagName) { - - Map parameters = newUrlTemplateVariables(); - parameters.put("repoName", repositoryName); - parameters.put("tag", tagName); - - try { - return operations.exchange(RELEASE_BY_TAG_URI_TEMPLATE, HttpMethod.GET, new HttpEntity<>(new HttpHeaders()), - GitHubRelease.class, parameters).getBody(); - } catch (HttpStatusCodeException e) { - - if (e.getStatusCode() == HttpStatus.NOT_FOUND) { - return null; - } - - throw e; - } - } - - private void createRelease(String repositoryName, GitHubRelease release) { - - Map parameters = newUrlTemplateVariables(); - parameters.put("repoName", repositoryName); - - operations.exchange(RELEASE_URI_TEMPLATE, HttpMethod.POST, new HttpEntity<>(release), GitHubRelease.class, - parameters); - } - - private void updateRelease(String repositoryName, GitHubRelease release) { - - Map parameters = newUrlTemplateVariables(); - parameters.put("repoName", repositoryName); - parameters.put("id", release.getId()); - - operations.exchange(RELEASE_BY_ID_URI_TEMPLATE, HttpMethod.PATCH, new HttpEntity<>(release), GitHubRelease.class, - parameters); - } - - private Stream getIssuesFor(ModuleIteration moduleIteration, boolean forCurrentUser, - boolean ignoreMissingMilestone) { - - String repositoryName = GitProject.of(moduleIteration.getProject()).getRepositoryName(); - - Optional optionalMilestone = findMilestone(moduleIteration, repositoryName); - - if (ignoreMissingMilestone && !optionalMilestone.isPresent()) { - return Stream.empty(); - } - - Milestone milestone = optionalMilestone.orElseThrow(() -> noSuchMilestone(moduleIteration)); - - Map parameters = newUrlTemplateVariables(); - parameters.put("repoName", repositoryName); - parameters.put("id", milestone.getNumber()); - - if (forCurrentUser) { - parameters.put("assignee", properties.getUsername()); - - return getForIssues(ISSUES_BY_MILESTONE_AND_ASSIGNEE_URI_TEMPLATE, parameters); - } - - return getForIssues(ISSUES_BY_MILESTONE_URI_TEMPLATE, parameters); - } - - private Stream getForIssues(String template, Map parameters) { - - List issues = new ArrayList<>(); - doWithPaging(template, HttpMethod.GET, parameters, new HttpEntity<>(new HttpHeaders()), ISSUES_TYPE, - tickets -> { - issues.addAll(tickets); - return true; - }); - - return issues.stream(); - } - - private Milestone getMilestone(ModuleIteration moduleIteration, String repositoryName) { - - Optional milestone = findMilestone(moduleIteration, repositoryName); - - return milestone - .orElseThrow(() -> noSuchMilestone(moduleIteration)); - } - - private IllegalStateException noSuchMilestone(ModuleIteration moduleIteration) { - return new IllegalStateException(String.format("No milestone for %s found containing %s!", // - moduleIteration.getProject().getFullName(), // - new GithubMilestone(moduleIteration))); - } - - private static Ticket toTicket(GitHubIssue issue) { - return new Ticket(issue.getId(), issue.getTitle(), issue.getUrl(), - issue.getAssignees().isEmpty() ? null : issue.getAssignees().get(0), new GithubTicketStatus(issue.getState())); - } - -} diff --git a/release-tools/src/main/java/org/springframework/data/release/issues/github/GitHubCommands.java b/release-tools/src/main/java/org/springframework/data/release/issues/github/GitHubCommands.java deleted file mode 100644 index 41367e9..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/issues/github/GitHubCommands.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2014-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.issues.github; - -import lombok.AccessLevel; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import lombok.experimental.FieldDefaults; - -import java.util.List; -import java.util.concurrent.Executor; -import java.util.stream.Collectors; - -import org.springframework.data.release.CliComponent; -import org.springframework.data.release.TimedCommand; -import org.springframework.data.release.git.GitOperations; -import org.springframework.data.release.issues.IssueTracker; -import org.springframework.data.release.issues.TicketReference; -import org.springframework.data.release.model.Project; -import org.springframework.data.release.model.Tracker; -import org.springframework.data.release.model.TrainIteration; -import org.springframework.data.release.utils.ExecutionUtils; -import org.springframework.plugin.core.PluginRegistry; -import org.springframework.shell.core.annotation.CliCommand; -import org.springframework.shell.core.annotation.CliOption; - -/** - * Component to execute GitHub related operations. - * - * @author Mark Paluch - */ -@CliComponent -@RequiredArgsConstructor -@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) -public class GitHubCommands extends TimedCommand { - - @NonNull PluginRegistry tracker; - @NonNull GitHub gitHub; - @NonNull GitOperations git; - @NonNull GitHubLabels gitHubLabels; - @NonNull Executor executor; - - @CliCommand(value = "github update labels") - public void createOrUpdateLabels(@CliOption(key = "", mandatory = true) Project project) { - gitHubLabels.createOrUpdateLabels(project); - } - - @CliCommand(value = "github push") - public void push(@CliOption(key = "", mandatory = true) TrainIteration iteration) { - - git.push(iteration); - git.pushTags(iteration.getTrain()); - - createOrUpdateRelease(iteration); - } - - @CliCommand(value = "github create release") - public void createOrUpdateRelease(@CliOption(key = "", mandatory = true) TrainIteration iteration) { - - TrainIteration previousIteration = git.getPreviousIteration(iteration); - - ExecutionUtils.run(executor, iteration, it -> { - - if (it.getProject().getTracker() == Tracker.GITHUB) { - - List ticketReferences = git.getTicketReferencesBetween(it.getProject(), previousIteration, iteration) - .stream().map(TicketReference::getId).collect(Collectors.toList()); - gitHub.createOrUpdateRelease(it, ticketReferences); - } - }); - } - -} diff --git a/release-tools/src/main/java/org/springframework/data/release/issues/github/GitHubIssue.java b/release-tools/src/main/java/org/springframework/data/release/issues/github/GitHubIssue.java deleted file mode 100644 index 4a39f28..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/issues/github/GitHubIssue.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2020-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.issues.github; - -import java.util.List; - -/** - * @author Mark Paluch - */ -public interface GitHubIssue { - - String getNumber(); - - default String getId() { - return getNumber() == null ? null : "#".concat(getNumber()); - } - - String getTitle(); - - String getState(); - - List getAssignees(); - - /** - * @return HTML URL. - */ - String getUrl(); - -} diff --git a/release-tools/src/main/java/org/springframework/data/release/issues/github/GitHubLabels.java b/release-tools/src/main/java/org/springframework/data/release/issues/github/GitHubLabels.java deleted file mode 100644 index e57fa96..0000000 --- a/release-tools/src/main/java/org/springframework/data/release/issues/github/GitHubLabels.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2020-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.release.issues.github; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.web.client.RestTemplateBuilder; -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.data.release.git.GitProject; -import org.springframework.data.release.model.Project; -import org.springframework.data.release.utils.Logger; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.stereotype.Component; - -import com.fasterxml.jackson.databind.JsonNode; - -/** - * Methods to interact with GitHub labels. - * - * @author Mark Paluch - */ -@Component -public class GitHubLabels extends GitHubSupport { - - private static final String LABELS_URI = "/repos/spring-projects/{repoName}/labels"; - private static final String LABEL_URI = "/repos/spring-projects/{repoName}/labels/{label}"; - - private static final ParameterizedTypeReference> LABELS_TYPE = new ParameterizedTypeReference>() {}; - - private final Logger logger; - - public GitHubLabels(@Qualifier("tracker") RestTemplateBuilder templateBuilder, Logger logger, - GitHubProperties properties) { - - super(createOperations(templateBuilder, properties)); - this.logger = logger; - } - - /** - * Creates or updates GitHub labels for a {@link Project}. The actual {@link LabelConfiguration} is obtained from - * {@link ProjectLabelConfiguration}. - * - * @param project the project to process. - */ - public void createOrUpdateLabels(Project project) { - - logger.log(project, "Obtaining labelsโ€ฆ"); - Map parameters = Collections.singletonMap("repoName", GitProject.of(project).getRepositoryName()); - - List