Skip to content

feat: Rework the creation of the Tuleap SCM Source job. #248

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

Merged
merged 1 commit into from
Jul 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@
<dependency>
<groupId>io.jenkins.plugins</groupId>
<artifactId>tuleap-api</artifactId>
<version>2.2.0</version>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
Expand All @@ -148,7 +148,7 @@
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.9.0</version>
<version>2.10.0</version>
<scope>compile</scope>
</dependency>
<!-- tests -->
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.jenkinsci.plugins.tuleap_git_branch_source;

import jenkins.plugins.git.AbstractGitSCMSource;
import jenkins.scm.api.SCMHead;

public class TuleapBranchSCMRevision extends AbstractGitSCMSource.SCMRevisionImpl {
public TuleapBranchSCMRevision(SCMHead head, String hash) {
super(head, hash);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import hudson.ExtensionList;
import hudson.Util;
import hudson.model.Action;
import hudson.model.Actionable;
Expand All @@ -26,7 +27,10 @@
import jenkins.scm.api.trait.SCMSourceRequest;
import jenkins.scm.api.trait.SCMSourceTrait;
import org.jenkinsci.Symbol;
import org.jenkinsci.plugins.tuleap_git_branch_source.config.TuleapSCMFileSystem;
import org.jenkinsci.plugins.tuleap_git_branch_source.trait.TuleapBranchDiscoveryTrait;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.AncestorInPath;
Expand Down Expand Up @@ -139,9 +143,16 @@ protected void retrieve(@CheckForNull SCMSourceCriteria criteria, @NonNull SCMHe
.call();
if (file.get().getName() != null) {
request.listener().getLogger().format("Search at '%s'", branch.getName());
TuleapBranchSCMHead head = new TuleapBranchSCMHead(branch.getName());
if (request.process(head, new SCMRevisionImpl(head, branch.getCommit().getId()),
TuleapSCMSource.this::fromSCMFileSystem, new OFWitness(listener))) {
TuleapBranchSCMHead tuleapBranchSCMHead = new TuleapBranchSCMHead(branch.getName());
if (request.process(tuleapBranchSCMHead, (SCMSourceRequest.RevisionLambda<TuleapBranchSCMHead, TuleapBranchSCMRevision>) head ->
new TuleapBranchSCMRevision(head, branch.getCommit().getId()),
new SCMSourceRequest.ProbeLambda<TuleapBranchSCMHead, TuleapBranchSCMRevision>() {
@NotNull
@Override
public SCMSourceCriteria.Probe create(@NotNull TuleapBranchSCMHead head, @Nullable TuleapBranchSCMRevision revisionInfo) throws IOException, InterruptedException {
return createProbe(head, revisionInfo);
}
}, new OFWitness(listener))) {
request.listener().getLogger()
.format("%n %d branches were processed (query completed)%n", count).println();
}
Expand All @@ -155,6 +166,47 @@ protected void retrieve(@CheckForNull SCMSourceCriteria criteria, @NonNull SCMHe
}
}

@NotNull
@Override
protected SCMProbe createProbe(@NonNull final SCMHead head, SCMRevision revision) throws IOException {
TuleapSCMFileSystem.BuilderImpl tuleapFileSystemBuilder = ExtensionList.lookup(SCMFileSystem.Builder.class).get(TuleapSCMFileSystem.BuilderImpl.class);
if (tuleapFileSystemBuilder == null) {
throw new IOException("Error while retrieving the tfs");
}
final SCMFileSystem fileSystem = tuleapFileSystemBuilder.build(this, head, revision);

return new SCMProbe() {
@NotNull
@Override
public SCMProbeStat stat(@NotNull String path) throws IOException {
try {
return SCMProbeStat.fromType(fileSystem.child(path).getType());
} catch (InterruptedException e) {
throw new IOException("Interrupted", e);
}
}

@Override
public void close() throws IOException {
Objects.requireNonNull(fileSystem).close();
}

@Override
public String name() {
return head.getName();
}

@Override
public long lastModified() {
try {
return fileSystem == null ? 0L : fileSystem.lastModified();
} catch (IOException | InterruptedException exception) {
return 0L;
}
}
};
}

@Override
protected SCMRevision retrieve(SCMHead head, TaskListener listener) throws IOException, InterruptedException {
Optional<String> revision = Optional.empty();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package org.jenkinsci.plugins.tuleap_git_branch_source.config;

import edu.umd.cs.findbugs.annotations.NonNull;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Base64;
import java.util.List;
import java.util.stream.Collectors;

import io.jenkins.plugins.tuleap_api.client.GitApi;
import io.jenkins.plugins.tuleap_api.client.GitFileContent;
import io.jenkins.plugins.tuleap_api.client.GitTreeContent;
import io.jenkins.plugins.tuleap_api.client.exceptions.git.FileContentNotFoundException;
import io.jenkins.plugins.tuleap_api.client.exceptions.git.TreeNotFoundException;
import io.jenkins.plugins.tuleap_credentials.TuleapAccessToken;
import jenkins.scm.api.SCMFile;

public class TuleapSCMFile extends SCMFile {

private final GitApi gitApi;
private final String repositoryId;
private final String ref;
private final TuleapAccessToken tuleapAccessToken;
private final boolean isDirectory;

public TuleapSCMFile(GitApi gitApi, String repositoryId, String ref, TuleapAccessToken tuleapAccessToken) {
super();
this.gitApi = gitApi;
this.repositoryId = repositoryId;
this.ref = ref;
this.tuleapAccessToken = tuleapAccessToken;

this.isDirectory = true;
this.type(Type.DIRECTORY);
}

private TuleapSCMFile(TuleapSCMFile parent, String name, Type type) {
super(parent, name);
this.gitApi = parent.gitApi;
this.repositoryId = parent.repositoryId;
this.tuleapAccessToken = parent.tuleapAccessToken;
this.ref = parent.ref;
this.isDirectory = type == Type.DIRECTORY;
this.type(type);
}

private TuleapSCMFile(TuleapSCMFile parent, String name, Boolean isDirectory) {
super(parent, name);
this.gitApi = parent.gitApi;
this.tuleapAccessToken = parent.tuleapAccessToken;
this.repositoryId = parent.repositoryId;
this.ref = parent.ref;
this.isDirectory = isDirectory;
}


@NonNull
@Override
protected SCMFile newChild(@NonNull String name, boolean isDirectory) {
return new TuleapSCMFile(this, name, isDirectory);
}

@NonNull
@Override
public Iterable<SCMFile> children() throws IOException, InterruptedException {
if (!this.isDirectory()) {
throw new IOException("Cannot get children from a regular file");
}

List<GitTreeContent> treeContent;
try {
treeContent = this.fetchTree();
} catch (TreeNotFoundException e) {
throw new IOException("Tree content cannot be retrieved: " + e.getMessage());
}
return treeContent
.stream()
.map(item -> {
Type type;

switch (item.getType()) {
case TREE:
type = Type.DIRECTORY;
break;
case BLOB:
type = Type.REGULAR_FILE;
break;
case SYMLINK:
type = Type.LINK;
break;
default:
type = Type.OTHER;
}

return new TuleapSCMFile(this, item.getName(), type);
})
.collect(Collectors.toList());
}

@Override
public long lastModified() throws IOException, InterruptedException {
// Unsupported
return 0L;
}

@NonNull
@Override
protected Type type() throws IOException, InterruptedException {
if (isDirectory) {
return Type.DIRECTORY;
}
try {
this.gitApi.getFileContent(this.repositoryId, this.ref, this.getPath(), this.tuleapAccessToken);
return Type.REGULAR_FILE;
} catch (FileContentNotFoundException e) {
try {
this.gitApi.getTree(repositoryId, ref, this.getPath(), tuleapAccessToken);
return Type.DIRECTORY;
} catch (TreeNotFoundException treeNotFoundException) {
return Type.NONEXISTENT;
}
}
}

@NonNull
@Override
public InputStream content() throws IOException, InterruptedException {
if (this.isDirectory()) {
throw new IOException("Cannot get raw content from a directory");
}
try {
byte[] decodedByteContent = Base64.getDecoder().decode(this.fetchFile().getContent());
return new ByteArrayInputStream(decodedByteContent);
} catch (FileContentNotFoundException e) {
throw new IOException("File not found: " + e.getMessage());
}
}

private List<GitTreeContent> fetchTree() throws TreeNotFoundException {
return gitApi.getTree(repositoryId, ref, this.getPath(), tuleapAccessToken);
}

private GitFileContent fetchFile() throws FileContentNotFoundException {
return this.gitApi.getFileContent(this.repositoryId, this.ref, this.getPath(), this.tuleapAccessToken);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package org.jenkinsci.plugins.tuleap_git_branch_source.config;

import com.google.inject.Guice;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import hudson.model.Item;
import hudson.scm.SCM;
import hudson.scm.SCMDescriptor;
import io.jenkins.plugins.tuleap_api.client.GitApi;
import io.jenkins.plugins.tuleap_api.client.TuleapApiGuiceModule;
import io.jenkins.plugins.tuleap_credentials.TuleapAccessToken;
import jenkins.scm.api.*;
import org.jenkinsci.plugins.tuleap_git_branch_source.TuleapBranchSCMHead;
import org.jenkinsci.plugins.tuleap_git_branch_source.TuleapSCMSource;
import org.jetbrains.annotations.NotNull;

import java.io.IOException;

public class TuleapSCMFileSystem extends SCMFileSystem {

private GitApi gitApi;
private final String repositoryId;
private final String commitReference;
private TuleapAccessToken tuleapAccessToken;

protected TuleapSCMFileSystem(GitApi gitApi, String repositoryId, String commitReference, TuleapAccessToken tuleapAccessToken, SCMRevision rev) {
super(rev);
this.gitApi = gitApi;
this.repositoryId = repositoryId;
this.commitReference = commitReference;
this.tuleapAccessToken = tuleapAccessToken;
}

@Override
public long lastModified() throws IOException, InterruptedException {
return this.gitApi.getCommit(this.repositoryId, this.commitReference, this.tuleapAccessToken).getCommitDate().toInstant().toEpochMilli();
}

@NotNull
@Override
public SCMFile getRoot() {
return new TuleapSCMFile(this.gitApi, this.repositoryId, this.commitReference, this.tuleapAccessToken);
}

@Extension
public static class BuilderImpl extends Builder {

private GitApi getGitApi() {
return Guice.createInjector(new TuleapApiGuiceModule()).getInstance(GitApi.class);
}

@Override
public boolean supports(SCM source) {
return false;
}

@Override
public boolean supports(SCMSource source) {
return source instanceof TuleapSCMSource;
}

@Override
protected boolean supportsDescriptor(SCMDescriptor descriptor) {
return false;
}

@Override
protected boolean supportsDescriptor(SCMSourceDescriptor descriptor) {
return descriptor instanceof TuleapSCMSource.DescriptorImpl;
}

@Override
public SCMFileSystem build(@NotNull Item owner, @NotNull SCM scm, SCMRevision rev) throws IOException, InterruptedException {
return null;
}

@Override
public SCMFileSystem build(@NonNull SCMSource source, @NonNull SCMHead head,
@CheckForNull SCMRevision rev) {
GitApi gitApi = this.getGitApi();
TuleapSCMSource tuleapSCMSource = (TuleapSCMSource) source;
TuleapAccessToken tuleapAccessToken = this.getAccessKey(tuleapSCMSource);
if (!(head instanceof TuleapBranchSCMHead)) {
return null;
}
String ref = head.getName();
return new TuleapSCMFileSystem(gitApi, Integer.toString(tuleapSCMSource.getTuleapGitRepository().getId()), ref, tuleapAccessToken, rev);
}

private TuleapAccessToken getAccessKey(TuleapSCMSource source) {
return TuleapConnector.lookupScanCredentials(
source.getOwner(),
source.getApiBaseUri(),
source.getCredentialsId()
);
}
}
}
Loading