-
Notifications
You must be signed in to change notification settings - Fork 78
#2026: Create UvRepository and UvBasedCommandlet #2064
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 8 commits
50a478c
3daa871
715de5e
11f60c2
83055ca
16546c8
2a02a83
80975b9
719f5e1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| package com.devonfw.tools.ide.tool.just; | ||
|
|
||
| import java.util.Set; | ||
|
|
||
| import com.devonfw.tools.ide.common.Tag; | ||
| import com.devonfw.tools.ide.context.IdeContext; | ||
| import com.devonfw.tools.ide.tool.uv.UvBasedCommandlet; | ||
|
|
||
| /** | ||
| * {@link UvBasedCommandlet} for <a href="https://github.com/casey/just">just</a>. | ||
| */ | ||
| public class Just extends UvBasedCommandlet { | ||
|
|
||
| /** | ||
| * The constructor. | ||
| * | ||
| * @param context the {@link com.devonfw.tools.ide.context.IdeContext}. | ||
| */ | ||
| public Just(IdeContext context) { | ||
| super(context, "just", Set.of(Tag.BUILD)); | ||
| } | ||
|
|
||
| @Override | ||
| public String getPackageName() { | ||
| return "rust-just"; | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| package com.devonfw.tools.ide.tool.uv; | ||
|
|
||
| import java.util.Objects; | ||
|
|
||
| import com.devonfw.tools.ide.tool.repository.SoftwareArtifact; | ||
|
|
||
| /** | ||
| * Simple type representing a uv tool artifact resolved from PyPI. | ||
| */ | ||
| public final class UvArtifact extends SoftwareArtifact { | ||
|
|
||
| private final String name; | ||
|
|
||
| /** | ||
| * The constructor. | ||
| * | ||
| * @param name the {@link #getName() name}. | ||
| * @param version the {@link #getVersion() version}. | ||
| */ | ||
| public UvArtifact(String name, String version) { | ||
| super(version); | ||
| this.name = name; | ||
| } | ||
|
|
||
| /** | ||
| * @return the package name in PyPi. | ||
| */ | ||
| public String getName() { | ||
| return this.name; | ||
| } | ||
|
|
||
| @Override | ||
| protected String computeKey() { | ||
| return this.name + "@" + this.version; | ||
| } | ||
|
|
||
| @Override | ||
| public boolean equals(Object obj) { | ||
| if (obj == this) { | ||
| return true; | ||
| } else if (obj instanceof UvArtifact other) { | ||
| return this.name.equals(other.name) && this.version.equals(other.version); | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| @Override | ||
| public int hashCode() { | ||
| return Objects.hash(this.name, this.version); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| package com.devonfw.tools.ide.tool.uv; | ||
|
|
||
| import java.util.Collections; | ||
| import java.util.Set; | ||
|
|
||
| import com.devonfw.tools.ide.os.OperatingSystem; | ||
| import com.devonfw.tools.ide.os.SystemArchitecture; | ||
| import com.devonfw.tools.ide.url.model.file.UrlChecksums; | ||
| import com.devonfw.tools.ide.url.model.file.UrlChecksumsEmpty; | ||
| import com.devonfw.tools.ide.url.model.file.UrlDownloadFileMetadata; | ||
| import com.devonfw.tools.ide.version.VersionIdentifier; | ||
|
|
||
| /** | ||
| * {@link UrlDownloadFileMetadata} representing metadata of a uv tool artifact from PyPI. | ||
| */ | ||
| public class UvArtifactMetadata implements UrlDownloadFileMetadata { | ||
|
|
||
| private final UvArtifact uvArtifact; | ||
| private final String tool; | ||
| private final String edition; | ||
| private final VersionIdentifier version; | ||
|
|
||
| /** | ||
| * The constructor. | ||
| * | ||
| * @param uvArtifact the {@link UvArtifact}. | ||
| * @param tool the tool name. | ||
| * @param edition the edition. | ||
| */ | ||
| public UvArtifactMetadata(UvArtifact uvArtifact, String tool, String edition) { | ||
| this.uvArtifact = uvArtifact; | ||
| this.version = VersionIdentifier.of(uvArtifact.getVersion()); | ||
| this.tool = tool; | ||
| this.edition = edition; | ||
| } | ||
|
|
||
| /** | ||
| * @return the {@link UvArtifact} | ||
| */ | ||
| public UvArtifact getUvArtifact() { | ||
| return this.uvArtifact; | ||
| } | ||
|
|
||
| @Override | ||
| public String getTool() { | ||
| return this.tool; | ||
| } | ||
|
|
||
| @Override | ||
| public String getEdition() { | ||
| return this.edition; | ||
| } | ||
|
|
||
| @Override | ||
| public VersionIdentifier getVersion() { | ||
| return this.version; | ||
| } | ||
|
|
||
| @Override | ||
| public Set<String> getUrls() { | ||
| return Collections.emptySet(); | ||
| } | ||
|
|
||
| @Override | ||
| public OperatingSystem getOs() { | ||
| return null; | ||
| } | ||
|
|
||
| @Override | ||
| public SystemArchitecture getArch() { | ||
| return null; | ||
| } | ||
|
|
||
| @Override | ||
| public UrlChecksums getChecksums() { | ||
| return UrlChecksumsEmpty.of(); | ||
| } | ||
|
|
||
| @Override | ||
| public String toString() { | ||
| return this.uvArtifact.toString(); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| package com.devonfw.tools.ide.tool.uv; | ||
|
|
||
| import java.nio.file.Files; | ||
| import java.util.Set; | ||
|
|
||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
|
|
||
| import com.devonfw.tools.ide.common.Tag; | ||
| import com.devonfw.tools.ide.context.IdeContext; | ||
| import com.devonfw.tools.ide.log.IdeLogLevel; | ||
| import com.devonfw.tools.ide.process.ProcessContext; | ||
| import com.devonfw.tools.ide.process.ProcessErrorHandling; | ||
| import com.devonfw.tools.ide.process.ProcessMode; | ||
| import com.devonfw.tools.ide.process.ProcessResult; | ||
| import com.devonfw.tools.ide.tool.PackageManagerBasedLocalToolCommandlet; | ||
| import com.devonfw.tools.ide.tool.PackageManagerRequest; | ||
| import com.devonfw.tools.ide.tool.repository.ToolRepository; | ||
| import com.devonfw.tools.ide.version.VersionIdentifier; | ||
|
|
||
| /** | ||
| * {@link PackageManagerBasedLocalToolCommandlet} for tools that are installed via {@code uv tool install} for PyPI. Concrete tools(e.g. {@code Just}) only need | ||
| * a constructor and to override {@link #getPackageName()} if the PyPI package name differs from the IDEasy tool name. | ||
| */ | ||
| public abstract class UvBasedCommandlet extends PackageManagerBasedLocalToolCommandlet<Uv> { | ||
|
|
||
| private static final Logger LOG = LoggerFactory.getLogger(UvBasedCommandlet.class); | ||
|
|
||
| /** | ||
| * The constructor. | ||
| * | ||
| * @param context the {@link IdeContext}. | ||
| * @param tool the {@link #getName() tool name}. | ||
| * @param tags the {@link #getTags() tags} classifying the tool. Should be created via {@link Set#of(Object) Set.of} method. | ||
| */ | ||
| public UvBasedCommandlet(IdeContext context, String tool, Set<Tag> tags) { | ||
| super(context, tool, tags); | ||
| } | ||
|
|
||
| @Override | ||
| protected Class<Uv> getPackageManagerClass() { | ||
| return Uv.class; | ||
| } | ||
|
|
||
| @Override | ||
| public ToolRepository getToolRepository() { | ||
| return this.context.getUvRepository(); | ||
| } | ||
|
|
||
| @Override | ||
| protected Uv getParentTool() { | ||
| return this.context.getCommandletManager().getCommandlet(Uv.class); | ||
| } | ||
|
|
||
| @Override | ||
| protected String appendVersion(String tool, VersionIdentifier version) { | ||
| return tool + "@" + version; | ||
| } | ||
|
|
||
| @Override | ||
| protected void completeRequestArgs(PackageManagerRequest request) { | ||
| request.addArg("tool"); | ||
| super.completeRequestArgs(request); | ||
| } | ||
|
|
||
| @Override | ||
| protected VersionIdentifier computeInstalledVersion() { | ||
| if (!Files.isDirectory(this.context.getSoftwarePath().resolve("uv"))) { | ||
| LOG.trace("Since uv is not installed, the tool {} cannot be installed either.", this.tool); | ||
| return null; | ||
| } | ||
| String packageName = getPackageName(); | ||
| PackageManagerRequest request = new PackageManagerRequest("list", packageName).addArg("tool").addArg("list") | ||
| .setProcessMode(ProcessMode.DEFAULT_CAPTURE); | ||
| ProcessContext pc = this.context.newProcess().errorHandling(ProcessErrorHandling.NONE); | ||
| request.setProcessContext(pc); | ||
| ProcessResult result = runPackageManager(request); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Version check can trigger an installationcomputeInstalledVersion calls the one-arg runPackageManager(request). The base class @implNote explicitly says to use runPackageManager(request, true) with skipInstallation=true for version checks, since the one-arg form calls pm.install(...) internally. |
||
| if (result.isSuccessful()) { | ||
| String prefix = packageName + "v"; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wrong prefix when parsing
|
||
| for (String line : result.getOut()) { | ||
| if (line.startsWith(prefix)) { | ||
| return VersionIdentifier.of(line.substring(prefix.length()).trim()); | ||
| } | ||
| } | ||
| } | ||
| LOG.debug("Could not determine installed version from 'uv tool list' output."); | ||
| result.log(IdeLogLevel.DEBUG); | ||
| return null; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing validation
UvArtifact is missing the requireNotEmpty(name, ...) validation that PipArtifact has. Since getPackageName() currently always returns a hardcoded string this is not an immediate problem, but future UvBasedCommandlet implementations returning null or empty would only fail at the HTTP request rather than immediately with a clear exception.