diff --git a/src/main/java/jenkins/scm/api/SCMFileSystem.java b/src/main/java/jenkins/scm/api/SCMFileSystem.java index d5a34327..45d1df22 100644 --- a/src/main/java/jenkins/scm/api/SCMFileSystem.java +++ b/src/main/java/jenkins/scm/api/SCMFileSystem.java @@ -158,6 +158,75 @@ public boolean changesSince(@CheckForNull SCMRevision revision, @NonNull OutputS throw new UnsupportedOperationException(); } + /** + * Given a {@link SCM} this method will try to retrieve a corresponding {@link SCMFileSystem} instance. + * + * @param build the build of the {@link SCM} + * @param scm the {@link SCM}. + * @return the corresponding {@link SCMFileSystem} or {@code null} if there is none. + * @throws IOException if the attempt to create a {@link SCMFileSystem} failed due to an IO error + * (such as the remote system being unavailable) + * @throws InterruptedException if the attempt to create a {@link SCMFileSystem} was interrupted. + */ + @CheckForNull + public static SCMFileSystem of(@NonNull Run build, @NonNull SCM scm) throws IOException, InterruptedException { + return of(build, scm, null); + } + + /** + * Given a {@link SCM} this method will try to retrieve a corresponding {@link SCMFileSystem} instance that + * reflects the content at the specified {@link SCMRevision}. + * + * @param build the build of the {@link SCM} + * @param scm the {@link SCM}. + * @param rev the specified {@link SCMRevision}. + * @return the corresponding {@link SCMFileSystem} or {@code null} if there is none. + * @throws IOException if the attempt to create a {@link SCMFileSystem} failed due to an IO error + * (such as the remote system being unavailable) + * @throws InterruptedException if the attempt to create a {@link SCMFileSystem} was interrupted. + */ + @CheckForNull + public static SCMFileSystem of(@NonNull Run build, @NonNull SCM scm, @CheckForNull SCMRevision rev) + throws IOException, InterruptedException { + Objects.requireNonNull(scm); + SCMFileSystem fallBack = null; + Throwable failure = null; + for (Builder b : ExtensionList.lookup(Builder.class)) { + if (b.supports(scm)) { + try { + SCMFileSystem inspector = b.build(build, scm, rev); + if (inspector != null) { + if (inspector.isFixedRevision()) { + return inspector; + } + if (fallBack == null) { + fallBack = inspector; + } + } + } catch (IOException | InterruptedException | RuntimeException e) { + if (failure == null) { + failure = e; + } else { + failure.addSuppressed(e); + } + } + } + } + if (fallBack == null) { + if (failure instanceof IOException) { + throw (IOException) failure; + } + if (failure instanceof InterruptedException) { + throw (InterruptedException) failure; + } + //noinspection ConstantConditions + if (failure instanceof RuntimeException) { + throw (RuntimeException) failure; + } + } + return fallBack; + } + /** * Given a {@link SCM} this method will try to retrieve a corresponding {@link SCMFileSystem} instance. * @@ -483,6 +552,27 @@ public final boolean supports(SCMSourceDescriptor descriptor) { */ protected abstract boolean supportsDescriptor(SCMSourceDescriptor descriptor); + /** + * Given a {@link SCM} this should try to build a corresponding {@link SCMFileSystem} instance that + * reflects the content at the specified {@link SCMRevision}. If the {@link SCM} is supported but not + * for a fixed revision, best effort is acceptable as the most capable {@link SCMFileSystem} will be returned + * to the caller. + * + * @param build the build of the {@link SCM} + * @param scm the {@link SCM}. + * @param rev the specified {@link SCMRevision}. + * @return the corresponding {@link SCMFileSystem} or {@code null} if this builder cannot create a {@link + * SCMFileSystem} for the specified {@link SCM}. + * @throws IOException if the attempt to create a {@link SCMFileSystem} failed due to an IO error + * (such as the remote system being unavailable) + * @throws InterruptedException if the attempt to create a {@link SCMFileSystem} was interrupted. + */ + @CheckForNull + public SCMFileSystem build(@NonNull Run build, @NonNull SCM scm, @CheckForNull SCMRevision rev) + throws IOException, InterruptedException { + return build(build.getParent(), scm, rev); + } + /** * Given a {@link SCM} this should try to build a corresponding {@link SCMFileSystem} instance that * reflects the content at the specified {@link SCMRevision}. If the {@link SCM} is supported but not diff --git a/src/test/java/jenkins/scm/impl/SCMFileSystemTest.java b/src/test/java/jenkins/scm/impl/SCMFileSystemTest.java index 178cd5a6..ea130295 100644 --- a/src/test/java/jenkins/scm/impl/SCMFileSystemTest.java +++ b/src/test/java/jenkins/scm/impl/SCMFileSystemTest.java @@ -27,6 +27,7 @@ import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; import hudson.model.Item; +import hudson.model.Run; import hudson.scm.SCM; import hudson.scm.SCMDescriptor; import jenkins.scm.api.SCMFileSystem; @@ -117,6 +118,13 @@ public SCMFileSystem build(@NonNull Item owner, @NonNull SCM scm, @CheckForNull return null; } + @Override + @CheckForNull + public SCMFileSystem build(@NonNull Run build, @NonNull SCM scm, @CheckForNull SCMRevision rev) + throws IOException, InterruptedException { + return null; + } + } @TestExtension("filesystem_supports_false_implementation_for_descriptor") @@ -148,5 +156,12 @@ public SCMFileSystem build(@NonNull Item owner, @NonNull SCM scm, @CheckForNull return null; } + @Override + @CheckForNull + public SCMFileSystem build(@NonNull Run build, @NonNull SCM scm, @CheckForNull SCMRevision rev) + throws IOException, InterruptedException { + return null; + } + } }