Skip to content

Commit

Permalink
CR
Browse files Browse the repository at this point in the history
  • Loading branch information
yahavi committed Apr 29, 2023
1 parent 85511a5 commit d3ea274
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 17 deletions.
8 changes: 7 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,13 @@
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.1.1</version>
<version>5.3.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>5.3.1</version>
<scope>test</scope>
</dependency>
</dependencies>
Expand Down
29 changes: 21 additions & 8 deletions src/main/java/io/jenkins/plugins/jfrog/JfStep.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import hudson.tasks.Builder;
import hudson.util.ArgumentListBuilder;
import io.jenkins.cli.shaded.org.apache.commons.io.FilenameUtils;
import io.jenkins.plugins.jfrog.actions.ArtifactoryBuildInfo;
import io.jenkins.plugins.jfrog.actions.BuildInfoBuildBadgeAction;
import io.jenkins.plugins.jfrog.actions.JFrogCliConfigEncryption;
import io.jenkins.plugins.jfrog.configuration.Credentials;
import io.jenkins.plugins.jfrog.configuration.JFrogPlatformBuilder;
Expand All @@ -26,6 +26,7 @@
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.jenkinsci.Symbol;
import org.jenkinsci.plugins.plaincredentials.StringCredentials;
import org.jfrog.build.api.util.Log;
import org.kohsuke.stapler.DataBoundConstructor;

import javax.annotation.Nonnull;
Expand Down Expand Up @@ -94,7 +95,7 @@ public void perform(@NonNull Run<?, ?> run, @NonNull FilePath workspace, @NonNul
if (exitValue != 0) {
throw new RuntimeException("Running 'jf' command failed with exit code " + exitValue);
}
addBuildInfoActionIfNeeded(run, taskOutputStream);
addBuildInfoActionIfNeeded(new JenkinsBuildInfoLog(listener), run, taskOutputStream);
} catch (Exception e) {
String errorMessage = "Couldn't execute 'jf' command. " + ExceptionUtils.getRootCauseMessage(e);
throw new RuntimeException(errorMessage, e);
Expand Down Expand Up @@ -235,11 +236,11 @@ private void addConfigArguments(ArgumentListBuilder builder, JFrogPlatformInstan
/**
* Add build-info Action if the command is 'jf rt bp' or 'jf rt build-publish'.
*
* @param log - Task logger
* @param run - The Jenkins project
* @param taskOutputStream - Task's output stream
* @throws JsonProcessingException in case of any unexpected error during the JSON parsing of the build info URL.
*/
private void addBuildInfoActionIfNeeded(Run<?, ?> run, ByteArrayOutputStream taskOutputStream) throws JsonProcessingException {
void addBuildInfoActionIfNeeded(Log log, Run<?, ?> run, ByteArrayOutputStream taskOutputStream) {
if (args.length < 2 ||
!args[0].equals("rt") ||
!equalsAny(args[1], "bp", "build-publish")) {
Expand All @@ -250,22 +251,34 @@ private void addBuildInfoActionIfNeeded(Run<?, ?> run, ByteArrayOutputStream tas
String taskOutput = taskOutputStream.toString(StandardCharsets.UTF_8);
taskOutput = substringBetween(taskOutput, "{", "}");
if (taskOutput == null) {
logIllegalBuildPublishOutput(log, taskOutputStream);
return;
}

// Parse the output into BuildInfoOutputModel to extract the build-info URL
BuildInfoOutputModel buildInfoOutputModel = mapper.readValue("{" + taskOutput + "}", BuildInfoOutputModel.class);
if (buildInfoOutputModel == null) {
BuildInfoOutputModel buildInfoOutputModel;
try {
buildInfoOutputModel = mapper.readValue("{" + taskOutput + "}", BuildInfoOutputModel.class);
if (buildInfoOutputModel == null) {
logIllegalBuildPublishOutput(log, taskOutputStream);
return;
}
} catch (JsonProcessingException e) {
logIllegalBuildPublishOutput(log, taskOutputStream);
return;
}
String buildInfoUrl = buildInfoOutputModel.getBuildInfoUiUrl();

// Add the ArtifactoryBuildInfo action into the job to show the build-info button
// Add the BuildInfoBuildBadgeAction action into the job to show the build-info button
if (isNotBlank(buildInfoUrl)) {
run.addAction(new ArtifactoryBuildInfo(buildInfoUrl));
run.addAction(new BuildInfoBuildBadgeAction(buildInfoUrl));
}
}

private void logIllegalBuildPublishOutput(Log log, ByteArrayOutputStream taskOutputStream) {
log.info("Illegal build-publish output: " + taskOutputStream.toString(StandardCharsets.UTF_8));
}

@Symbol("jf")
@Extension
public static final class DescriptorImpl extends BuildStepDescriptor<Builder> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
/**
* Represents the build-info URL Action with the Artifactory icon.
*/
public class ArtifactoryBuildInfo implements BuildBadgeAction {
public class BuildInfoBuildBadgeAction implements BuildBadgeAction {
private final String url;

public ArtifactoryBuildInfo(String url) {
public BuildInfoBuildBadgeAction(String url) {
this.url = url;
}

Expand Down
89 changes: 89 additions & 0 deletions src/test/java/io/jenkins/plugins/jfrog/AddBuildInfoActionTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package io.jenkins.plugins.jfrog;

import hudson.model.Action;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
import org.jfrog.build.api.util.NullLog;
import org.junit.Rule;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.mockito.junit.jupiter.MockitoExtension;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.stream.Stream;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.*;

/**
* @author yahavi
**/
@ExtendWith(MockitoExtension.class)
public class AddBuildInfoActionTest {
private static final String RT_BP_OUTPUT = ("13:14:38 [\uD83D\uDD35Info] Deploying build info...\n" +
"13:14:40 [\uD83D\uDD35Info] Build info successfully deployed.\n" +
"{\n" +
" \"buildInfoUiUrl\": \"http://127.0.0.1:8081/ui/builds/test/1/1682417678409/published?buildRepo=artifactory-build-info\"\n" +
"}");
private static final String EXPECTED_BUILD_INFO_URL = "http://127.0.0.1:8081/ui/builds/test/1/1682417678409/published?buildRepo=artifactory-build-info";

@Rule
public MockitoRule rule = MockitoJUnit.rule().silent();

@Captor
ArgumentCaptor<Action> valueCapture;

@Mock
WorkflowRun run;

private static Stream<Arguments> positiveDataProvider() {
return Stream.of(
Arguments.of("rt bp", RT_BP_OUTPUT),
Arguments.of("rt build-publish", RT_BP_OUTPUT)
);
}

@ParameterizedTest
@MethodSource("positiveDataProvider")
public void addBuildInfoActionPositiveTest(String command, String output) throws IOException {
doNothing().when(run).addAction(valueCapture.capture());
runCliCommand(command, output);

Mockito.verify(run, times(1)).addAction(isA(Action.class));
assertEquals(1, valueCapture.getAllValues().size());
assertEquals(valueCapture.getValue().getUrlName(), EXPECTED_BUILD_INFO_URL);
}

private static Stream<Arguments> negativeDataProvider() {
return Stream.of(
Arguments.of("rt u a b", RT_BP_OUTPUT),
Arguments.of("rt bp", "{ \"a\": \"b\" }"),
Arguments.of("rt bp", "Illegal output"),
Arguments.of("rt bp", "{ Illegal JSON }")
);
}

@ParameterizedTest
@MethodSource("negativeDataProvider")
public void addBuildInfoActionNegativeTest(String command, String output) throws IOException {
runCliCommand(command, output);
Mockito.verify(run, never()).addAction(isA(Action.class));
}

private void runCliCommand(String command, String output) throws IOException {
try (ByteArrayOutputStream taskOutputStream = new ByteArrayOutputStream()) {
taskOutputStream.writeBytes(output.getBytes(StandardCharsets.UTF_8));
new JfStep(command).addBuildInfoActionIfNeeded(new NullLog(), run, taskOutputStream);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package io.jenkins.plugins.jfrog.integration;

import io.jenkins.plugins.casc.ConfigurationAsCode;
import io.jenkins.plugins.jfrog.actions.ArtifactoryBuildInfo;
import io.jenkins.plugins.jfrog.actions.BuildInfoBuildBadgeAction;
import joptsimple.internal.Strings;
import org.apache.commons.lang3.StringUtils;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
Expand Down Expand Up @@ -183,11 +183,11 @@ public void testBuildInfoAction(JenkinsRule jenkins) throws Exception {
// Assert build info published
assertTrue(job.getLog().contains("Build info successfully deployed"));
// Check build-info Action
ArtifactoryBuildInfo buildInfoAction = job.getAction(ArtifactoryBuildInfo.class);
assertNotNull(buildInfoAction);
assertTrue(StringUtils.startsWith(buildInfoAction.getUrlName(), PLATFORM_URL));
assertTrue(StringUtils.isNotBlank(buildInfoAction.getIconFileName()));
assertEquals("Artifactory Build Info", buildInfoAction.getDisplayName());
BuildInfoBuildBadgeAction buildInfoBuildBadgeAction = job.getAction(BuildInfoBuildBadgeAction.class);
assertNotNull(buildInfoBuildBadgeAction);
assertTrue(StringUtils.startsWith(buildInfoBuildBadgeAction.getUrlName(), PLATFORM_URL));
assertTrue(StringUtils.isNotBlank(buildInfoBuildBadgeAction.getIconFileName()));
assertEquals("Artifactory Build Info", buildInfoBuildBadgeAction.getDisplayName());
}

/**
Expand Down

0 comments on commit d3ea274

Please sign in to comment.