Revise repository declarations handling.

We now rewrite the <repositories> section of each parent pom to leave the milestone repo for milestone releases, remove all repositories if the release is a GA or service release and put both repositories back after a release.

Closes #39
This commit is contained in:
Mark Paluch
2023-06-07 09:09:20 +02:00
parent 58576d4eb7
commit 81d8b0ad20
11 changed files with 214 additions and 134 deletions

View File

@@ -89,6 +89,11 @@
<version>1.3</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>

View File

@@ -17,7 +17,6 @@ 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;
@@ -53,11 +52,6 @@ public class AnnouncementOperations {
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(" - ");

View File

@@ -36,7 +36,6 @@ 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;
/**
@@ -49,14 +48,12 @@ public class MavenArtifact {
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;
}
@@ -80,12 +77,4 @@ public class MavenArtifact {
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);
}
}

View File

@@ -112,13 +112,12 @@ class MavenBuildSystem implements BuildSystem {
if (updater.isBuildProject()) {
if (information.isBomInBuildProject()) {
updateBom(information, "bom/pom.xml", BUILD);
updateBom(updater, information, "bom/pom.xml", BUILD);
}
updateParentPom(information);
updateParentPom(updater, information);
} else if (updater.isBomProject()) {
updateBom(information, "bom/pom.xml", BOM);
updateBom(updater, information, "bom/pom.xml", BOM);
} else {
doWithProjection(workspace.getFile(POM_XML, updater.getProject()), pom -> {
@@ -458,7 +457,7 @@ class MavenBuildSystem implements BuildSystem {
return module;
}
private void updateBom(UpdateInformation updateInformation, String file, Project project) {
private void updateBom(PomUpdater updater, UpdateInformation updateInformation, String file, Project project) {
TrainIteration iteration = updateInformation.getTrain();
@@ -499,10 +498,12 @@ class MavenBuildSystem implements BuildSystem {
throw new IllegalStateException(String.format("Found snapshot dependencies %s!", snapshotDependencies));
}
}
updater.updateRepository(pom);
});
}
private void updateParentPom(UpdateInformation information) {
private void updateParentPom(PomUpdater updater, UpdateInformation information) {
// Fix version of shared resources to to-be-released version.
doWithProjection(workspace.getFile("parent/pom.xml", BUILD), ParentPom.class, pom -> {
@@ -512,6 +513,8 @@ class MavenBuildSystem implements BuildSystem {
logger.log(BUILD, "Setting releasetrain property to %s.", information.getReleaseTrainVersion());
pom.setReleaseTrain(information.getReleaseTrainVersion());
updater.updateRepository(pom);
});
}
@@ -622,6 +625,9 @@ class MavenBuildSystem implements BuildSystem {
s = s.replaceAll(Pattern.quote("standalone=\"no\"?><"), "standalone=\"no\"?>" + IOUtils.LINE_SEPARATOR + "<");
}
s = s.replace(String.format("<repositories>%n\t\t%n\t</repositories>"),
String.format("<repositories>%n\t</repositories>"));
if (!s.endsWith(IOUtils.LINE_SEPARATOR)) {
s += IOUtils.LINE_SEPARATOR;
}

View File

@@ -15,9 +15,20 @@
*/
package org.springframework.data.release.build;
import lombok.SneakyThrows;
import java.util.Arrays;
import java.util.List;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.release.model.ArtifactVersion;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xmlbeam.annotation.XBDelete;
import org.xmlbeam.annotation.XBRead;
import org.xmlbeam.annotation.XBValue;
import org.xmlbeam.annotation.XBWrite;
@@ -57,6 +68,12 @@ public interface Pom {
@XBWrite("/project/repositories/repository[id=\"{0}\"]/url")
void setRepositoryUrl(String id, @XBValue String url);
@XBDelete("/project/repositories/*")
void deleteRepositories();
@XBWrite("/project/repositories")
void setRepositories(Element repositories);
/**
* Sets the version of the dependency with the given artifact identifier to the given {@link ArtifactVersion}.
*
@@ -78,13 +95,93 @@ public interface Pom {
@XBRead("//dependency[substring(version, string-length(version) - string-length('-SNAPSHOT') + 1) = '-SNAPSHOT']")
List<Artifact> getSnapshotDependencies();
public interface Repository {
class RepositoryElementFactory {
@XBRead("child::id")
String getId();
public static Element of(Repository... repositories) {
return of(Arrays.asList(repositories));
}
public static Element of(List<Repository> repositories) {
Document doc = createDocument();
Element repos = doc.createElement("repositories");
repos.appendChild(doc.createTextNode(IOUtils.LINE_SEPARATOR));
repos.appendChild(doc.createTextNode("\t\t"));
for (int i = 0; i < repositories.size(); i++) {
Repository repo = repositories.get(i);
repos.appendChild(toElement(doc, repo));
repos.appendChild(doc.createTextNode(IOUtils.LINE_SEPARATOR));
if (i + 1 == repositories.size()) {
repos.appendChild(indent(doc, 1));
} else {
repos.appendChild(indent(doc, 2));
}
}
return repos;
}
private static Element toElement(Document doc, Repository repo) {
Element repository = doc.createElement("repository");
repository.appendChild(doc.createTextNode(IOUtils.LINE_SEPARATOR));
repository.appendChild(indent(doc, 3));
repository.appendChild(createElement(doc, "id", repo.getId()));
repository.appendChild(doc.createTextNode(IOUtils.LINE_SEPARATOR));
repository.appendChild(indent(doc, 3));
repository.appendChild(createElement(doc, "url", repo.getUrl()));
repository.appendChild(doc.createTextNode(IOUtils.LINE_SEPARATOR));
if (repo.getSnapshots() != null) {
appendEnabledConfig(doc, "snapshots", repo.getSnapshots(), repository);
}
if (repo.getReleases() != null) {
appendEnabledConfig(doc, "releases", repo.getReleases(), repository);
}
repository.appendChild(indent(doc, 2));
return repository;
}
private static void appendEnabledConfig(Document doc, String tagName, boolean value, Element repository) {
Element snapshots = doc.createElement(tagName);
snapshots.appendChild(doc.createTextNode(IOUtils.LINE_SEPARATOR));
snapshots.appendChild(indent(doc, 4));
snapshots.appendChild(createElement(doc, "enabled", Boolean.toString(value)));
snapshots.appendChild(doc.createTextNode(IOUtils.LINE_SEPARATOR));
snapshots.appendChild(indent(doc, 3));
repository.appendChild(indent(doc, 3));
repository.appendChild(snapshots);
repository.appendChild(doc.createTextNode(IOUtils.LINE_SEPARATOR));
}
private static Element createElement(Document doc, String name, String content) {
Element url = doc.createElement(name);
url.setTextContent(content);
return url;
}
private static Node indent(Document doc, int indentSize) {
return doc.createTextNode(StringUtils.repeat("\t", indentSize));
}
@SneakyThrows
static Document createDocument() {
return DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
}
@XBRead("child::url")
String getUrl();
}
public interface Artifact {

View File

@@ -15,12 +15,14 @@
*/
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 java.util.List;
import org.springframework.data.release.build.Pom.RepositoryElementFactory;
import org.springframework.data.release.model.ArtifactVersion;
import org.springframework.data.release.model.Project;
import org.springframework.data.release.utils.Logger;
@@ -28,6 +30,7 @@ import org.springframework.util.Assert;
/**
* @author Oliver Gierke
* @author Mark Paluch
*/
@RequiredArgsConstructor
class PomUpdater {
@@ -71,7 +74,7 @@ class PomUpdater {
/**
* Updates the version of the parent project in the given {@link Pom}.
*
*
* @param pom must not be {@literal null}.
*/
public void updateParentVersion(Pom pom) {
@@ -86,29 +89,21 @@ class PomUpdater {
/**
* 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());
List<Repository> repositories = information.getRepositories();
pom.deleteRepositories();
if (repositories.isEmpty()) {
logger.log(project, "Removing <repositories> declaration.");
} else {
logger.log(project, message, repository.getSnapshotId(), repository.getSnapshotUrl());
pom.setRepositoryId(repository.getId(), repository.getSnapshotId());
pom.setRepositoryUrl(repository.getSnapshotId(), repository.getSnapshotUrl());
logger.log(project, "Switching to Spring repositories %s.", repositories);
pom.setRepositories(RepositoryElementFactory.of(repositories));
}
}
}

View File

@@ -17,10 +17,6 @@ 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
*/
@@ -28,49 +24,12 @@ import org.springframework.util.Assert;
@Value
public class Repository {
private static final String ID_BASE = "spring-libs-";
private static final String BASE = "https://repo.spring.io/libs-";
static Repository SNAPSHOT = new Repository("spring-snapshot", "https://repo.spring.io/snapshot", true, false);
static Repository MILESTONE = new Repository("spring-milestone", "https://repo.spring.io/milestone", null, null);
String id, url;
Boolean snapshots;
Boolean releases;
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));
}
}

View File

@@ -21,6 +21,10 @@ import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.springframework.data.release.model.ArtifactVersion;
import org.springframework.data.release.model.Phase;
import org.springframework.data.release.model.Project;
@@ -83,13 +87,20 @@ public class UpdateInformation {
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());
public List<Repository> getRepositories() {
if (phase == Phase.PREPARE) {
if (train.getIteration().isMilestone()) {
return Collections.singletonList(Repository.MILESTONE);
}
}
if (phase == Phase.CLEANUP) {
return Arrays.asList(Repository.SNAPSHOT, Repository.MILESTONE);
}
return Collections.emptyList();
}
/**

View File

@@ -21,9 +21,8 @@ import java.io.InputStream;
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.release.build.Pom.RepositoryElementFactory;
import org.xmlbeam.XBProjector;
/**
@@ -49,4 +48,34 @@ class MavenBuildSystemUnitTests {
.endsWith(IOUtils.LINE_SEPARATOR);
}
}
@Test
void shouldRemoveRepositories() throws Exception {
ClassPathResource resource = new ClassPathResource("sample-pom.xml");
try (InputStream is = resource.getInputStream()) {
byte[] bytes = MavenBuildSystem.doWithProjection(projector, is, Pom.class, Pom::deleteRepositories);
assertThat(new String(bytes)).contains("<repositories>").doesNotContain("<repository>");
}
}
@Test
void shouldAddRepositories() throws Exception {
ClassPathResource resource = new ClassPathResource("sample-pom.xml");
try (InputStream is = resource.getInputStream()) {
byte[] bytes = MavenBuildSystem.doWithProjection(projector, is, Pom.class, pom -> {
pom.deleteRepositories();
pom.setRepositories(RepositoryElementFactory.of(Repository.SNAPSHOT, Repository.MILESTONE));
});
assertThat(new String(bytes)).containsSubsequence("repositories", "<id>spring-snapshot</id>", "<snapshots>",
"<enabled>true</enabled>", "<releases>", "<enabled>false</enabled>", "spring-milestone");
}
}
}

View File

@@ -17,11 +17,8 @@ package org.springframework.data.release.build;
import static org.assertj.core.api.Assertions.*;
import java.util.Arrays;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.data.release.model.Iteration;
import org.springframework.data.release.model.Phase;
import org.springframework.data.release.model.Projects;
@@ -48,22 +45,6 @@ class UpdateInformationUnitTests {
Assertions.assertThatIllegalArgumentException().isThrownBy(() -> UpdateInformation.of(hopperM1, null));
}
@Test
void exposesMilestoneRepositoryForMilestone() {
assertThat(UpdateInformation.of(hopperM1, Phase.PREPARE).getRepository().getId())
.isEqualTo("spring-libs-milestone");
}
@Test
void exposesReleaseRepositoryForGA() {
Arrays.asList(Iteration.GA, Iteration.SR1).forEach(iteration -> {
TrainIteration trainIteration = new TrainIteration(ReleaseTrains.HOPPER, iteration);
assertThat(UpdateInformation.of(trainIteration, Phase.PREPARE).getRepository().getId())
.isEqualTo("spring-libs-release");
});
}
@Test
void calculatesProjectVersionToSetCorrectly() {
@@ -74,6 +55,32 @@ class UpdateInformationUnitTests {
assertThat(updateInformation.getProjectVersionToSet(Projects.JPA).toString()).isEqualTo("1.10.0.BUILD-SNAPSHOT");
}
@Test
void milestoneReposContainedForMilestoneRelease() {
UpdateInformation updateInformation = UpdateInformation.of(hopperM1, Phase.PREPARE);
assertThat(updateInformation.getRepositories()).containsOnly(Repository.MILESTONE);
}
@Test
void noReposContainedForGaRelease() {
UpdateInformation updateInformation = UpdateInformation.of(new TrainIteration(ReleaseTrains.HOPPER, Iteration.GA),
Phase.PREPARE);
assertThat(updateInformation.getRepositories()).isEmpty();
}
@Test
void cleanupSetsMilestoneAndSnapshotRepos() {
UpdateInformation updateInformation = UpdateInformation.of(new TrainIteration(ReleaseTrains.HOPPER, Iteration.GA),
Phase.CLEANUP);
assertThat(updateInformation.getRepositories()).contains(Repository.MILESTONE, Repository.SNAPSHOT);
}
@Test // #155
void calculatesProjectCalverVersionToSetCorrectly() {

View File

@@ -14,18 +14,6 @@
<properties>
<jar.mainclass>org.springframework.shell.Bootstrap</jar.mainclass>
</properties>
<profiles>
<profile>
<id>profile</id>
<repositories>
<repository>
<id>spring-libs-snapshot</id>
<url>https://repo.spring.io/libs-snapshot</url>
</repository>
</repositories>
</profile>
</profiles>
<dependencies>
@@ -38,7 +26,7 @@
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
@@ -54,7 +42,7 @@
<artifactId>spring-shell</artifactId>
<version>1.1.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.xmlbeam</groupId>
<artifactId>xmlprojector</artifactId>
@@ -67,7 +55,7 @@
<version>1.12.4</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
@@ -109,4 +97,4 @@
</repository>
</repositories>
</project>
</project>