Skip to content

Commit

Permalink
Add support for discover new updatesite URLs when update a target
Browse files Browse the repository at this point in the history
As there is usually some versioning involved in updatesite URLs discover
updates can not work if not a composite URL is used in the first place.

This now adds a new 'strategy' to the mojo that allows try some ways to
find new updatesite urls, currently the only available is 'parent' that
looks in the parent URL context if it can find a composite repository
and check if there is a child that can supply an update for any UI

(cherry picked from commit d702901)
  • Loading branch information
laeubi authored and eclipse-tycho-bot committed Aug 13, 2024
1 parent 67426f5 commit d1aa635
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,24 @@

import static java.util.stream.Collectors.toList;

import java.lang.reflect.Method;
import java.net.URI;
import java.util.Collections;
import java.util.HashMap;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;
import javax.inject.Named;

import org.apache.maven.plugin.MojoFailureException;
import org.eclipse.tycho.TargetEnvironment;
import org.eclipse.tycho.core.resolver.P2ResolutionResult;
import org.eclipse.tycho.p2.target.facade.TargetPlatformConfigurationStub;
import org.eclipse.equinox.p2.core.IProvisioningAgent;
import org.eclipse.equinox.p2.core.ProvisionException;
import org.eclipse.equinox.p2.metadata.IInstallableUnit;
import org.eclipse.equinox.p2.metadata.Version;
import org.eclipse.equinox.p2.query.QueryUtil;
import org.eclipse.equinox.p2.repository.metadata.IMetadataRepository;
import org.eclipse.equinox.p2.repository.metadata.IMetadataRepositoryManager;
import org.eclipse.tycho.p2resolver.TargetDefinitionVariableResolver;
import org.eclipse.tycho.targetplatform.TargetDefinition;
import org.eclipse.tycho.targetplatform.TargetDefinition.FollowRepositoryReferences;
Expand All @@ -43,34 +48,30 @@ public class InstallableUnitLocationUpdater {

@Inject
private TargetDefinitionVariableResolver varResolver;
@Inject
private IProvisioningAgent agent;

public boolean update(Element iuLocation, UpdateTargetMojo context) throws MojoFailureException {
TargetDefinitionFile parsedTarget = TargetDefinitionFile.read(context.getFileToBeUpdated());
TargetPlatformConfigurationStub resolutionContext = new TargetPlatformConfigurationStub();
resolutionContext.setEnvironments(Collections.singletonList(TargetEnvironment.getRunningEnvironment()));
resolutionContext.addTargetDefinition(new LatestVersionTarget(parsedTarget, varResolver));
resolutionContext.setIgnoreLocalArtifacts(true);
P2ResolutionResult result = context.createResolver().getTargetPlatformAsResolutionResult(resolutionContext,
context.getExecutionEnvironment());

Map<String, String> ius = new HashMap<>();
for (P2ResolutionResult.Entry entry : result.getArtifacts()) {
ius.put(entry.getId(), entry.getVersion());
}
boolean update(Element iuLocation, UpdateTargetMojo context)
throws MojoFailureException, URISyntaxException, ProvisionException {
List<IU> units = iuLocation.getChildren("unit").stream()
.map(unit -> new IU(unit.getAttributeValue("id"), unit.getAttributeValue("version"), unit)).toList();
IMetadataRepository repository = getMetadataRepository(iuLocation, context, units);
boolean updated = false;
List<Element> children = iuLocation.getChildren("unit");
for (Element unit : children) {
for (Element unit : iuLocation.getChildren("unit")) {
String id = unit.getAttributeValue("id");
String version = ius.get(id);
if (version != null) {
IInstallableUnit latestUnit = repository
.query(QueryUtil.createLatestQuery(QueryUtil.createIUQuery(id)), null).stream().findFirst()
.orElse(null);
if (latestUnit != null) {
String newVersion = latestUnit.getVersion().toString();
String currentVersion = unit.getAttributeValue("version");
if (version.equals(currentVersion)) {
if (newVersion.equals(currentVersion)) {
context.getLog().debug("unit '" + id + "' is already up-to date");
} else {
updated = true;
context.getLog()
.info("Update version of unit '" + id + "' from " + currentVersion + " > " + version);
unit.setAttribute("version", version);
.info("Update version of unit '" + id + "' from " + currentVersion + " > " + newVersion);
unit.setAttribute("version", newVersion);
}
} else {
context.getLog().warn(
Expand All @@ -80,6 +81,104 @@ public boolean update(Element iuLocation, UpdateTargetMojo context) throws MojoF
return updated;
}

private IMetadataRepository getMetadataRepository(Element iuLocation, UpdateTargetMojo context, List<IU> units)
throws URISyntaxException, ProvisionException {
ResolvedRepository location = getResolvedLocation(iuLocation);
URI uri = new URI(location.getLocation());
String discovery = context.getUpdateSiteDiscovery();
IMetadataRepositoryManager repositoryManager = agent.getService(IMetadataRepositoryManager.class);
if (discovery != null && !discovery.isBlank()) {
for (String strategy : discovery.split(",")) {
if (strategy.trim().equals("parent")) {
String str = uri.toASCIIString();
if (!str.endsWith("/")) {
str = str + "/";
}
URI parentURI = new URI(str + "../");
try {
IMetadataRepository repository = repositoryManager.loadRepository(parentURI, null);
List<IU> bestUnits = units;
URI bestURI = null;
//we now need to find a repository that has all units and they must have the same or higher version
for (URI child : getChildren(repository)) {
List<IU> find = findBestUnits(bestUnits, repositoryManager, child, context);
if (find != null) {
bestUnits = find;
bestURI = child;
}
}
if (bestURI != null) {
location.element().setAttribute("location", bestURI.toString());
return repositoryManager.loadRepository(bestURI, null);
}
} catch (ProvisionException e) {
// if we can't load it we can't use it but this is maybe because that no parent exits.
context.getLog().debug(
"No parent repository found for location " + uri + " using " + parentURI + ": " + e);
}
}
}
}
//if nothing else is applicable return the original location repository...
return repositoryManager.loadRepository(uri, null);
}

@SuppressWarnings("unchecked")
private Collection<URI> getChildren(IMetadataRepository repository) {
try {
Method method = repository.getClass().getDeclaredMethod("getChildren");
if (method.invoke(repository) instanceof Collection<?> c) {
return (Collection<URI>) c;
}
} catch (Exception e) {
}
return List.of();
}

private List<IU> findBestUnits(List<IU> units, IMetadataRepositoryManager repositoryManager, URI child,
UpdateTargetMojo context) throws ProvisionException {
IMetadataRepository childRepository = repositoryManager.loadRepository(child, null);
List<IU> list = new ArrayList<>();
boolean hasLarger = false;
for (IU iu : units) {
IInstallableUnit unit = childRepository
.query(QueryUtil.createLatestQuery(QueryUtil.createIUQuery(iu.id())), null).stream().findFirst()
.orElse(null);
if (unit == null) {
//unit is not present in repo...
context.getLog().debug(
"Skip child " + child + " because unit " + iu.id() + " can't be found in the repository");
return null;
}
int cmp = unit.getVersion().compareTo(Version.create(iu.version()));
if (cmp < 0) {
//version is lower than we currently have!
context.getLog()
.debug("Skip child " + child + " because version of unit " + iu.id() + " in repository ("
+ unit.getVersion() + ") is smaller than current largest version (" + iu.version()
+ ").");
return null;
}
if (cmp > 0) {
hasLarger = true;
}
list.add(new IU(iu.id(), unit.getVersion().toString(), iu.element()));
}
if (hasLarger) {
return list;
} else {
context.getLog().debug("Skip child " + child + " because it has not produced any version updates");
return null;
}
}

private ResolvedRepository getResolvedLocation(Element iuLocation) {
Element element = iuLocation.getChild("repository");
String attribute = element.getAttributeValue("location");
String resolved = varResolver.resolve(attribute);
return new ResolvedRepository(element.getAttributeValue("id"), resolved, element);
}

private static final class LatestVersionTarget implements TargetDefinition {

private TargetDefinitionFile delegate;
Expand Down Expand Up @@ -132,7 +231,7 @@ public LatestVersionLocation(InstallableUnitLocation delegate, TargetDefinitionV
public List<? extends TargetDefinition.Repository> getRepositories() {
return delegate.getRepositories().stream().map(repo -> {
URI resolvedLocation = URI.create(varResolver.resolve(repo.getLocation()));
return new ResolvedRepository(repo.getId(), resolvedLocation.toString());
return new ResolvedRepository(repo.getId(), resolvedLocation.toString(), null);
}).collect(toList());
}

Expand Down Expand Up @@ -188,25 +287,22 @@ public String getVersion() {

}

private static final class ResolvedRepository implements TargetDefinition.Repository {

private final String id;
private final String uri;
private static final record ResolvedRepository(String id, String location, Element element)
implements TargetDefinition.Repository {

ResolvedRepository(String id, String uri) {
this.id = id;
this.uri = uri;
@Override
public String getLocation() {
return location();
}

@Override
public String getId() {
return id;
return id();
}

@Override
public String getLocation() {
return uri;
}
}

private static record IU(String id, String version, Element element) {

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import org.eclipse.aether.resolution.ArtifactResolutionException;
import org.eclipse.aether.resolution.VersionRangeResolutionException;
import org.eclipse.aether.version.InvalidVersionSpecificationException;
import org.eclipse.equinox.p2.core.ProvisionException;
import org.eclipse.tycho.targetplatform.TargetPlatformArtifactResolver;
import org.eclipse.tycho.targetplatform.TargetResolveException;

Expand Down Expand Up @@ -68,6 +69,17 @@ public class UpdateTargetMojo extends AbstractUpdateMojo {
@Parameter(property = "major", defaultValue = "true")
private boolean updateMajorVersion;

/**
* A comma separated list of update site discovery strategies, the following is currently
* supported:
* <ul>
* <li>parent - search the parent path for a composite that can be used to find newer
* versions</li>
* </ul>
*/
@Parameter(property = "discovery")
private String updateSiteDiscovery;

@Component
private MavenSession mavenSession;

Expand All @@ -80,7 +92,7 @@ public class UpdateTargetMojo extends AbstractUpdateMojo {
@Override
protected void doUpdate() throws IOException, URISyntaxException, ParserConfigurationException,
TargetResolveException, MojoFailureException, VersionRangeResolutionException, ArtifactResolutionException,
InvalidVersionSpecificationException {
InvalidVersionSpecificationException, ProvisionException {
File file = getFileToBeUpdated();
getLog().info("Update target file " + file);
//we use the descent xml parser here because we need to retain the formating of the original file
Expand Down Expand Up @@ -156,4 +168,8 @@ MavenSession getMavenSession() {
return mavenSession;
}

String getUpdateSiteDiscovery() {
return updateSiteDiscovery;
}

}

0 comments on commit d1aa635

Please sign in to comment.