blob: c7ab92d9e934b9ee442bd23ddeed1caec99255b6 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to You 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
*
* http://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.apache.sling.tooling.lc;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.apache.sling.provisioning.model.Artifact;
import org.apache.sling.provisioning.model.Model;
import org.apache.sling.provisioning.model.ModelUtility;
import org.apache.sling.provisioning.model.io.ModelReader;
import org.apache.sling.tooling.lc.aether.AetherSetup;
import org.apache.sling.tooling.lc.aether.ArtifactKey;
import org.apache.sling.tooling.lc.aether.Artifacts;
import org.apache.sling.tooling.lc.aether.VersionChange;
import org.apache.sling.tooling.lc.jira.IssueFinder;
import org.apache.sling.tooling.lc.svn.SvnChangeLogFinder;
import org.tmatesoft.svn.core.SVNException;
import com.google.common.collect.Sets;
public class LaunchpadComparer {
private static final Pattern JIRA_KEY_PATTERN = Pattern.compile("^(SLING-\\d+).*");
private final String firstVersion;
private final String secondVersion;
public LaunchpadComparer(String firstVersion, String secondVersion) {
this.firstVersion = firstVersion;
this.secondVersion = secondVersion;
}
public void run() throws Exception {
System.out.format("Computing differences between Launchpad versions %s and %s...%n",
firstVersion, secondVersion);
// 1. download artifacts
AetherSetup aether = new AetherSetup();
File fromFile = aether.download(Artifacts.launchpadCoordinates(firstVersion));
File toFile = aether.download(Artifacts.launchpadCoordinates(secondVersion));
// 2. parse artifact definitions
Map<ArtifactKey, Artifact> from = readArtifactsFromModel(fromFile);
Map<ArtifactKey, Artifact> to = readArtifactsFromModel(toFile);
// 3. generate added / removed / changed
Set<Artifact> removed = Sets.difference(from.keySet(), to.keySet()).stream()
.map( k -> from.get(k))
.collect(Collectors.toSet());
Set<Artifact> added = Sets.difference(to.keySet(), from.keySet()).stream()
.map( k -> to.get(k))
.collect(Collectors.toSet());
Map<ArtifactKey, VersionChange> changed = to.values().stream()
.filter( k -> !added.contains(k) && !removed.contains(k))
.map( k -> new ArtifactKey(k))
.filter( k -> !Objects.equals(to.get(k).getVersion(), from.get(k).getVersion()))
.collect(Collectors.toMap( Function.identity(), k -> new VersionChange(from.get(k).getVersion(), to.get(k).getVersion())));
// 4. output changes
System.out.println("Added ");
added.stream().sorted().forEach(LaunchpadComparer::outputFormatted);
System.out.println("Removed ");
removed.stream().sorted().forEach(LaunchpadComparer::outputFormatted);
System.out.println("Changed");
changed.entrySet().stream()
.sorted( (a, b) -> a.getKey().compareTo(b.getKey()) )
.forEach(LaunchpadComparer::outputFormatted);
}
private Map<ArtifactKey, Artifact> readArtifactsFromModel(File toFile) throws IOException {
Model fromModel;
try (BufferedReader reader = Files.newBufferedReader(toFile.toPath())) {
fromModel = ModelUtility.getEffectiveModel(ModelReader.read(reader, null));
}
Map<ArtifactKey, Artifact> to = fromModel.getFeatures().stream()
.flatMap( f -> f.getRunModes().stream())
.flatMap( r -> r.getArtifactGroups().stream())
.flatMap( g -> StreamSupport.stream(g.spliterator(), false))
.collect(Collectors.toMap( a -> new ArtifactKey(a), Function.identity()));
return to;
}
private static void outputFormatted(Artifact a) {
System.out.format(" %-30s : %-55s : %s%n", a.getGroupId(), a.getArtifactId(), a.getVersion());
}
private static void outputFormatted(Map.Entry<ArtifactKey, VersionChange> e) {
ArtifactKey artifact = e.getKey();
VersionChange versionChange = e.getValue();
System.out.format(" %-30s : %-55s : %s -> %s%n", artifact.getGroupId(), artifact.getArtifactId(), versionChange.getFrom(), versionChange.getTo());
if ( !artifact.getGroupId().equals("org.apache.sling")) {
return;
}
SvnChangeLogFinder svn = new SvnChangeLogFinder();
String fromTag = artifact.getArtifactId()+"-"+versionChange.getFrom();
String toTag = artifact.getArtifactId()+"-"+ versionChange.getTo();
try {
List<String> issues = svn.getChanges(fromTag, toTag)
.stream()
.map( m -> m.split(System.lineSeparator())[0])
.map(LaunchpadComparer::toJiraKey)
.filter( k -> k != null)
.collect(Collectors.toList());
IssueFinder issueFinder = new IssueFinder();
issueFinder.findIssues(issues).
forEach( i -> System.out.format(" %-10s - %s%n", i.getKey(), i.getSummary()));
} catch (SVNException | IOException e1) {
System.err.println("Failed retrieving changes : " + e1.getMessage());
}
}
private static String toJiraKey(String message) {
Matcher matcher = JIRA_KEY_PATTERN.matcher(message);
if ( !matcher.matches() ) {
return null;
}
return matcher.group(1);
}
}