Skip to content

Auto notify tuleap about current build status #195

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 3 commits into from
Apr 12, 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
2 changes: 1 addition & 1 deletion 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.1.3</version>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package org.jenkinsci.plugins.tuleap_git_branch_source;

import hudson.model.Run;
import hudson.plugins.git.util.BuildData;
import io.jenkins.plugins.tuleap_api.client.GitApi;
import io.jenkins.plugins.tuleap_api.client.internals.entities.TuleapBuildStatus;
import io.jenkins.plugins.tuleap_credentials.TuleapAccessToken;

import jenkins.scm.api.SCMSource;
import org.jenkinsci.plugins.tuleap_git_branch_source.config.TuleapConnector;
import org.jetbrains.annotations.Nullable;

import java.io.PrintStream;

public class TuleapPipelineStatusNotifier {
private final GitApi gitApi;

public TuleapPipelineStatusNotifier(GitApi gitApi) {
this.gitApi = gitApi;
}

@Nullable
private TuleapSCMSource getTuleapSCMSource(Run<?, ?> build) {
final SCMSource source = SCMSource.SourceByItem.findSource(build.getParent());
if (!(source instanceof TuleapSCMSource)) {
return null;
}
return (TuleapSCMSource) source;
}

public void sendBuildStatusToTuleap(Run<?, ?> build, PrintStream logger, TuleapBuildStatus status) {
final TuleapSCMSource source = getTuleapSCMSource(build);
if (source == null) {
// Not a TuleapSCMSource, abort
return;
}

final TuleapAccessToken token = getAccessKey(source);
if (token == null) {
throw new RuntimeException(
"Access key for project not found. Please check your project configuration."
);
}

final BuildData gitData = build.getAction(BuildData.class);
if (gitData == null) {
throw new RuntimeException(
"Failed to retrieve Git Data. Please check the configuration."
);
}

final int repository_id = source.getTuleapGitRepository().getId();
logger.printf(
"Notifying Tuleap about build status: %s",
status.toString()
);

this.gitApi.sendBuildStatus(
Integer.toString(repository_id),
gitData.lastBuild.getSHA1().name(),
status,
token
);
}

@Nullable
private TuleapAccessToken getAccessKey(TuleapSCMSource source) {
return TuleapConnector.lookupScanCredentials(
source.getOwner(),
source.getApiBaseUri(),
source.getCredentialsId()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package org.jenkinsci.plugins.tuleap_git_branch_source;

import com.google.inject.Guice;
import com.google.inject.Injector;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import hudson.FilePath;
import hudson.model.Result;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.model.listeners.RunListener;
import hudson.model.listeners.SCMListener;
import hudson.scm.SCM;
import hudson.scm.SCMRevisionState;
import io.jenkins.plugins.tuleap_api.client.GitApi;
import io.jenkins.plugins.tuleap_api.client.TuleapApiGuiceModule;
import io.jenkins.plugins.tuleap_api.client.internals.entities.TuleapBuildStatus;

import java.io.File;
import java.util.logging.Level;
import java.util.logging.Logger;

public class TuleapPipelineWatcher {
private static final Logger LOGGER = Logger
.getLogger(TuleapPipelineWatcher.class.getName());

private static TuleapPipelineStatusNotifier getNotifier() {
final Injector injector = Guice.createInjector(new TuleapApiGuiceModule());
return new TuleapPipelineStatusNotifier(
injector.getInstance(GitApi.class)
);
}

@Extension
public static class TuleapJobCheckOutListener extends SCMListener {
@Override
public void onCheckout(
Run<?, ?> build,
SCM scm,
FilePath workspace,
TaskListener listener,
File changelogFile,
SCMRevisionState pollingBaseline
) {
LOGGER.log(Level.INFO, String.format("Tuleap build: Checkout > %s", build.getFullDisplayName()));
getNotifier().sendBuildStatusToTuleap(build, listener.getLogger(), TuleapBuildStatus.pending);
}
}

@Extension
public static class TuleapJobCompletedListener extends RunListener<Run<?, ?>> {
@Override
public void onCompleted(Run<?, ?> build, @NonNull TaskListener listener) {
LOGGER.log(Level.INFO, String.format("Tuleap build: Complete > %s", build.getFullDisplayName()));

final Result buildResult = build.getResult();
final TuleapBuildStatus status = (buildResult == Result.SUCCESS) ? TuleapBuildStatus.success : TuleapBuildStatus.failure;

getNotifier().sendBuildStatusToTuleap(build, listener.getLogger(), status);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import hudson.Extension;
import hudson.Util;
import hudson.model.Action;
Expand Down Expand Up @@ -277,6 +278,10 @@ public String getGitBaseUri() {
return TuleapConfiguration.get().getGitBaseUrl();
}

public TuleapGitRepository getTuleapGitRepository() {
return this.repository;
}

@Symbol("Tuleap")
@Extension
public static class DescriptorImpl extends SCMSourceDescriptor {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package org.jenkinsci.plugins.tuleap_git_branch_source;

import hudson.model.FreeStyleBuild;
import hudson.model.FreeStyleProject;
import hudson.plugins.git.util.Build;
import hudson.plugins.git.util.BuildData;
import io.jenkins.plugins.tuleap_api.client.GitApi;
import io.jenkins.plugins.tuleap_api.client.internals.entities.TuleapBuildStatus;
import io.jenkins.plugins.tuleap_api.deprecated_client.api.TuleapGitRepository;
import io.jenkins.plugins.tuleap_credentials.TuleapAccessToken;
import jenkins.scm.api.SCMSource;
import org.eclipse.jgit.lib.ObjectId;
import org.jenkinsci.plugins.tuleap_git_branch_source.config.TuleapConnector;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.MockedStatic;
import org.mockito.Mockito;

import java.io.PrintStream;

import static org.mockito.Mockito.*;

public class TuleapPipelineStatusNotifierTest {
private GitApi gitApi;
private TuleapPipelineStatusNotifier notifier;
private TuleapAccessToken accessKey;
private MockedStatic<SCMSource.SourceByItem> sourceByItem;
private MockedStatic<TuleapConnector> tuleapConnector;

@Before
public void setUp() {
this.gitApi = mock(GitApi.class);
this.accessKey = mock(TuleapAccessToken.class);
this.sourceByItem = Mockito.mockStatic(SCMSource.SourceByItem.class);
this.tuleapConnector = Mockito.mockStatic(TuleapConnector.class);

this.notifier = new TuleapPipelineStatusNotifier(this.gitApi);
}

@After
public void tearDown() {
this.sourceByItem.close();
this.tuleapConnector.close();
}

@Test
public void testItDoesNotNotifyWhenItIsNotATuleapSCMBuild() {
final FreeStyleBuild build = mock(FreeStyleBuild.class);
final SCMSource source = mock(SCMSource.class);
final PrintStream logger = mock(PrintStream.class);
final FreeStyleProject freestyleProject = mock(FreeStyleProject.class);

when(build.getParent()).thenReturn(freestyleProject);

this.sourceByItem.when(() -> SCMSource.SourceByItem.findSource(freestyleProject)).thenReturn(source);

verify(this.gitApi, never()).sendBuildStatus(
"5",
"aeiouy123456",
TuleapBuildStatus.success,
this.accessKey
);

this.notifier.sendBuildStatusToTuleap(build, logger, TuleapBuildStatus.success);
}

@Test(expected = RuntimeException.class)
public void testItThrowsAnExceptionWhenAccessKeyNotFound() {
final WorkflowRun build = mock(WorkflowRun.class);
final TuleapSCMSource source = mock(TuleapSCMSource.class);
final PrintStream logger = mock(PrintStream.class);
final WorkflowJob workflowJob = mock(WorkflowJob.class);

when(build.getParent()).thenReturn(workflowJob);

this.sourceByItem.when(() -> SCMSource.SourceByItem.findSource(workflowJob)).thenReturn(source);
this.tuleapConnector.when(() -> TuleapConnector.lookupScanCredentials(
Mockito.any(),
Mockito.any(),
Mockito.any()
)).thenReturn(null);

verify(this.gitApi, never()).sendBuildStatus(
"5",
"aeiouy123456",
TuleapBuildStatus.success,
this.accessKey
);

this.notifier.sendBuildStatusToTuleap(build, logger, TuleapBuildStatus.success);
}

@Test(expected = RuntimeException.class)
public void testItThrowsAnExceptionWhenGitDataNotFound() {
final WorkflowRun build = mock(WorkflowRun.class);
final TuleapSCMSource source = mock(TuleapSCMSource.class);
final PrintStream logger = mock(PrintStream.class);
final WorkflowJob workflowJob = mock(WorkflowJob.class);
final TuleapAccessToken accessKey = mock(TuleapAccessToken.class);

when(build.getParent()).thenReturn(workflowJob);
when(build.getAction(BuildData.class)).thenReturn(null);

this.sourceByItem.when(() -> SCMSource.SourceByItem.findSource(workflowJob)).thenReturn(source);
this.tuleapConnector.when(() -> TuleapConnector.lookupScanCredentials(
Mockito.any(),
Mockito.any(),
Mockito.any()
)).thenReturn(accessKey);

verify(this.gitApi, never()).sendBuildStatus(
"5",
"aeiouy123456",
TuleapBuildStatus.success,
this.accessKey
);

this.notifier.sendBuildStatusToTuleap(build, logger, TuleapBuildStatus.success);
}

@Test
public void testItNotifiesTuleap() {
final WorkflowRun build = mock(WorkflowRun.class);
final TuleapSCMSource source = mock(TuleapSCMSource.class);
final PrintStream logger = mock(PrintStream.class);
final WorkflowJob workflowJob = mock(WorkflowJob.class);
final TuleapAccessToken accessKey = mock(TuleapAccessToken.class);
final BuildData gitData = mock(BuildData.class);
final ObjectId sha1 = mock(ObjectId.class);
final TuleapGitRepository repository = new TuleapGitRepository();
final Build lastBuild = mock(Build.class);

repository.setId(5);
gitData.lastBuild = lastBuild;

when(build.getParent()).thenReturn(workflowJob);
when(build.getAction(BuildData.class)).thenReturn(gitData);
when(lastBuild.getSHA1()).thenReturn(sha1);
when(sha1.name()).thenReturn("aeiouy123465");
when(source.getTuleapGitRepository()).thenReturn(repository);

this.sourceByItem.when(() -> SCMSource.SourceByItem.findSource(workflowJob)).thenReturn(source);
this.tuleapConnector.when(() -> TuleapConnector.lookupScanCredentials(
Mockito.any(),
Mockito.any(),
Mockito.any()
)).thenReturn(accessKey);

verify(this.gitApi, atMostOnce()).sendBuildStatus(
"5",
"aeiouy123456",
TuleapBuildStatus.success,
accessKey
);

this.notifier.sendBuildStatusToTuleap(build, logger, TuleapBuildStatus.success);
}
}