-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
See https://libyear.com/ Example: ``` [cstamas@angeleyes toolbox (libyear)]$ java -jar toolbox/target/toolbox-0.1.11-SNAPSHOT-cli.jar libyear org.apache.maven:maven-core:3.9.6 Outdated versions with known age 4.17 years from org.codehaus.plexus:plexus-component-annotations:jar 2.1.0 (2019-10-23) => 2.2.0 (2023-12-24) 2.73 years from org.apache.commons:commons-lang3:jar 3.12.0 (2021-02-26) => 3.14.0 (2023-11-18) 2.12 years from org.codehaus.plexus:plexus-cipher:jar 2.0 (2021-09-08) => 2.1.0 (2023-10-22) 2.04 years from org.apache.maven.shared:maven-shared-utils:jar 3.3.4 (2021-04-26) => 3.4.2 (2023-05-11) 1.90 years from org.slf4j:slf4j-api:jar 1.7.36 (2022-02-08) => 2.1.0-alpha1 (2024-01-02) 1.29 years from com.google.inject:guice:jar 5.1.0 (2022-01-24) => 7.0.0 (2023-05-12) 1.12 years from org.codehaus.plexus:plexus-utils:jar 3.5.1 (2023-03-02) => 4.0.1 (2024-04-13) 0.42 years from org.apache.maven.resolver:maven-resolver-util:jar 1.9.18 (2023-11-22) => 2.0.0-alpha-11 (2024-04-26) 0.42 years from org.apache.maven.resolver:maven-resolver-spi:jar 1.9.18 (2023-11-22) => 2.0.0-alpha-11 (2024-04-26) 0.42 years from org.apache.maven.resolver:maven-resolver-named-locks:jar 1.9.18 (2023-11-22) => 2.0.0-alpha-11 (2024-04-26) 0.42 years from org.apache.maven.resolver:maven-resolver-impl:jar 1.9.18 (2023-11-22) => 2.0.0-alpha-11 (2024-04-26) 0.42 years from org.apache.maven.resolver:maven-resolver-api:jar 1.9.18 (2023-11-22) => 2.0.0-alpha-11 (2024-04-26) 0.27 years from org.apache.maven:maven-settings:jar 3.9.6 (2023-11-28) => 4.0.0-alpha-13 (2024-03-06) 0.27 years from org.apache.maven:maven-settings-builder:jar 3.9.6 (2023-11-28) => 4.0.0-alpha-13 (2024-03-06) 0.27 years from org.apache.maven:maven-resolver-provider:jar 3.9.6 (2023-11-28) => 4.0.0-alpha-13 (2024-03-06) 0.27 years from org.apache.maven:maven-repository-metadata:jar 3.9.6 (2023-11-28) => 4.0.0-alpha-13 (2024-03-06) 0.27 years from org.apache.maven:maven-plugin-api:jar 3.9.6 (2023-11-28) => 4.0.0-alpha-13 (2024-03-06) 0.27 years from org.apache.maven:maven-model:jar 3.9.6 (2023-11-28) => 4.0.0-alpha-13 (2024-03-06) 0.27 years from org.apache.maven:maven-model-builder:jar 3.9.6 (2023-11-28) => 4.0.0-alpha-13 (2024-03-06) 0.27 years from org.apache.maven:maven-core:jar 3.9.6 (2023-11-28) => 4.0.0-alpha-13 (2024-03-06) 0.27 years from org.apache.maven:maven-builder-support:jar 3.9.6 (2023-11-28) => 4.0.0-alpha-13 (2024-03-06) 0.27 years from org.apache.maven:maven-artifact:jar 3.9.6 (2023-11-28) => 4.0.0-alpha-13 (2024-03-06) Outdated versions com.google.guava:failureaccess:jar 1.0.1 (?) => 1.0.2 (?) com.google.guava:guava:jar 32.0.1-jre (?) => 33.2.0-jre (?) org.codehaus.plexus:plexus-classworlds:jar 2.7.0 (?) => 2.8.0 (?) org.codehaus.plexus:plexus-interpolation:jar 1.26 (?) => 1.27 (?) Total of 20.17 years from 26 outdated dependencies [cstamas@angeleyes toolbox (libyear)]$ ```
- Loading branch information
Showing
7 changed files
with
445 additions
and
48 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
230 changes: 230 additions & 0 deletions
230
shared/src/main/java/eu/maveniverse/maven/toolbox/shared/internal/LibYearSink.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,230 @@ | ||
/* | ||
* Copyright (c) 2023-2024 Maveniverse Org. | ||
* All rights reserved. This program and the accompanying materials | ||
* are made available under the terms of the Eclipse Public License v2.0 | ||
* which accompanies this distribution, and is available at | ||
* https://www.eclipse.org/legal/epl-v20.html | ||
*/ | ||
package eu.maveniverse.maven.toolbox.shared.internal; | ||
|
||
import static java.util.Objects.requireNonNull; | ||
|
||
import eu.maveniverse.maven.mima.context.Context; | ||
import eu.maveniverse.maven.toolbox.shared.ArtifactSink; | ||
import eu.maveniverse.maven.toolbox.shared.Output; | ||
import java.io.IOException; | ||
import java.io.UncheckedIOException; | ||
import java.time.Instant; | ||
import java.time.LocalDate; | ||
import java.time.ZoneId; | ||
import java.time.temporal.ChronoUnit; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
import java.util.TreeSet; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
import java.util.concurrent.ConcurrentMap; | ||
import java.util.function.BiFunction; | ||
import org.apache.maven.search.api.SearchBackend; | ||
import org.apache.maven.search.api.SearchRequest; | ||
import org.apache.maven.search.api.SearchResponse; | ||
import org.eclipse.aether.artifact.Artifact; | ||
import org.eclipse.aether.deployment.DeploymentException; | ||
import org.eclipse.aether.repository.RemoteRepository; | ||
import org.eclipse.aether.resolution.VersionRangeResolutionException; | ||
import org.eclipse.aether.util.artifact.ArtifactIdUtils; | ||
import org.eclipse.aether.version.Version; | ||
|
||
/** | ||
* Construction to calculate "libyear". | ||
* | ||
* @see <a href="https://libyear.com/">libyear</a> | ||
*/ | ||
public final class LibYearSink implements ArtifactSink { | ||
public static final class LibYear { | ||
private final String currentVersion; | ||
private final Instant currentVersionInstant; | ||
private final String latestVersion; | ||
private final Instant latestVersionInstant; | ||
|
||
private LibYear( | ||
String currentVersion, | ||
Instant currentVersionInstant, | ||
String latestVersion, | ||
Instant latestVersionInstant) { | ||
this.currentVersion = currentVersion; | ||
this.currentVersionInstant = currentVersionInstant; | ||
this.latestVersion = latestVersion; | ||
this.latestVersionInstant = latestVersionInstant; | ||
} | ||
|
||
public String getCurrentVersion() { | ||
return currentVersion; | ||
} | ||
|
||
public Instant getCurrentVersionInstant() { | ||
return currentVersionInstant; | ||
} | ||
|
||
public String getLatestVersion() { | ||
return latestVersion; | ||
} | ||
|
||
public Instant getLatestVersionInstant() { | ||
return latestVersionInstant; | ||
} | ||
} | ||
|
||
/** | ||
* Creates libYear sink. | ||
*/ | ||
public static LibYearSink libYear( | ||
Output output, | ||
Context context, | ||
ToolboxResolverImpl toolboxResolver, | ||
ToolboxSearchApiImpl toolboxSearchApi, | ||
boolean quiet, | ||
boolean allowSnapshots, | ||
BiFunction<Artifact, List<Version>, Version> versionSelector) { | ||
return new LibYearSink( | ||
output, context, toolboxResolver, toolboxSearchApi, quiet, allowSnapshots, versionSelector); | ||
} | ||
|
||
private final Output output; | ||
private final Context context; | ||
private final ToolboxResolverImpl toolboxResolver; | ||
private final ToolboxSearchApiImpl toolboxSearchApi; | ||
private final boolean quiet; | ||
private final boolean allowSnapshots; | ||
private final BiFunction<Artifact, List<Version>, Version> versionSelector; | ||
private final ConcurrentMap<Artifact, LibYear> libYear; | ||
|
||
private LibYearSink( | ||
Output output, | ||
Context context, | ||
ToolboxResolverImpl toolboxResolver, | ||
ToolboxSearchApiImpl toolboxSearchApi, | ||
boolean quiet, | ||
boolean allowSnapshots, | ||
BiFunction<Artifact, List<Version>, Version> versionSelector) { | ||
this.output = requireNonNull(output, "output"); | ||
this.context = requireNonNull(context, "context"); | ||
this.toolboxResolver = requireNonNull(toolboxResolver, "toolboxResolver"); | ||
this.toolboxSearchApi = requireNonNull(toolboxSearchApi, "toolboxSearchApi"); | ||
this.quiet = quiet; | ||
this.allowSnapshots = allowSnapshots; | ||
this.versionSelector = requireNonNull(versionSelector); | ||
this.libYear = new ConcurrentHashMap<>(); | ||
} | ||
|
||
public Map<Artifact, LibYear> getLibYear() { | ||
return libYear; | ||
} | ||
|
||
@Override | ||
public void accept(Artifact artifact) throws IOException { | ||
requireNonNull(artifact, "artifact"); | ||
libYear.computeIfAbsent(artifact, a -> { | ||
String currentVersion = artifact.getVersion(); | ||
Instant currentVersionInstant = null; | ||
String latestVersion = currentVersion; | ||
Instant latestVersionInstant = null; | ||
try { | ||
currentVersionInstant = artifactPublishDate(artifact); | ||
latestVersion = versionSelector | ||
.apply(artifact, toolboxResolver.findNewerVersions(artifact, allowSnapshots)) | ||
.toString(); | ||
latestVersionInstant = artifactPublishDate(artifact.setVersion(latestVersion)); | ||
} catch (VersionRangeResolutionException e) { | ||
// ignore | ||
} catch (IOException e) { | ||
throw new UncheckedIOException(e); | ||
} | ||
return new LibYear(currentVersion, currentVersionInstant, latestVersion, latestVersionInstant); | ||
}); | ||
} | ||
|
||
@Override | ||
public void close() throws DeploymentException { | ||
float totalLibYears = 0; | ||
int totalLibOutdated = 0; | ||
TreeSet<String> timedOnes = new TreeSet<>(Collections.reverseOrder()); | ||
TreeSet<String> outdated = new TreeSet<>(); | ||
if (!quiet) { | ||
for (Map.Entry<Artifact, LibYear> entry : getLibYear().entrySet()) { | ||
if (entry.getValue() != null) { | ||
LibYear value = entry.getValue(); | ||
if (Objects.equals(value.getCurrentVersion(), value.getLatestVersion())) { | ||
continue; | ||
} | ||
totalLibOutdated++; | ||
|
||
LocalDate currentVersionDate = value.getCurrentVersionInstant() != null | ||
? value.getCurrentVersionInstant() | ||
.atZone(ZoneId.systemDefault()) | ||
.toLocalDate() | ||
: null; | ||
LocalDate latestVersionDate = value.getLatestVersionInstant() != null | ||
? value.getLatestVersionInstant() | ||
.atZone(ZoneId.systemDefault()) | ||
.toLocalDate() | ||
: null; | ||
|
||
if (currentVersionDate != null && latestVersionDate != null) { | ||
long libWeeksOutdated = ChronoUnit.WEEKS.between(currentVersionDate, latestVersionDate); | ||
float libYearsOutdated = libWeeksOutdated / 52f; | ||
totalLibYears += libYearsOutdated; | ||
timedOnes.add(String.format( | ||
"%.2f years from %s %s (%s) => %s (%s)", | ||
libYearsOutdated, | ||
ArtifactIdUtils.toVersionlessId(entry.getKey()), | ||
value.getCurrentVersion(), | ||
currentVersionDate, | ||
value.getLatestVersion(), | ||
latestVersionDate)); | ||
} else { | ||
outdated.add(String.format( | ||
"%s %s (?) => %s (?)", | ||
ArtifactIdUtils.toVersionlessId(entry.getKey()), | ||
value.getCurrentVersion(), | ||
value.getLatestVersion())); | ||
} | ||
} | ||
} | ||
} | ||
|
||
String indent = ""; | ||
output.normal("{}Outdated versions with known age", indent); | ||
timedOnes.forEach(l -> output.normal("{}{}", indent, l)); | ||
output.normal("{}", indent); | ||
output.normal("{}Outdated versions", indent); | ||
outdated.forEach(l -> output.normal("{}{}", indent, l)); | ||
output.normal("{}", indent); | ||
output.normal( | ||
"{}Total of {} years from {} outdated dependencies", | ||
indent, | ||
String.format("%.2f", totalLibYears), | ||
totalLibOutdated); | ||
output.normal("{}", indent); | ||
} | ||
|
||
private Instant artifactPublishDate(Artifact artifact) throws IOException { | ||
for (RemoteRepository remoteRepository : context.remoteRepositories()) { | ||
try (SearchBackend backend = toolboxSearchApi.getSmoBackend(remoteRepository)) { | ||
SearchRequest searchRequest = new SearchRequest(toolboxSearchApi.toSmoQuery(artifact)); | ||
SearchResponse searchResponse = backend.search(searchRequest); | ||
if (searchResponse.getCurrentHits() > 0) { | ||
Long lastUpdated = | ||
searchResponse.getPage().iterator().next().getLastUpdated(); | ||
if (lastUpdated != null) { | ||
return Instant.ofEpochMilli(lastUpdated); | ||
} | ||
} | ||
} catch (IllegalArgumentException e) { | ||
// most likely not SMO service (remote repo is not CENTRAL); ignore | ||
} | ||
} | ||
return null; | ||
} | ||
} |
Oops, something went wrong.