diff --git a/appveyor.yml b/appveyor.yml index 901c738..f67e697 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,10 +14,11 @@ install: - ps: | Add-Type -AssemblyName System.IO.Compression.FileSystem if (!(Test-Path -Path "C:\maven" )) { - (new-object System.Net.WebClient).DownloadFile('http://www.us.apache.org/dist/maven/maven-3/3.2.5/binaries/apache-maven-3.2.5-bin.zip', 'C:\maven-bin.zip') + (new-object System.Net.WebClient).DownloadFile('https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip', 'C:\maven-bin.zip') [System.IO.Compression.ZipFile]::ExtractToDirectory("C:\maven-bin.zip", "C:\maven") } - - cmd: SET M2_HOME=C:\maven\apache-maven-3.2.5 + - cmd: SET M2_HOME=C:\maven\apache-maven-3.8.1 + - cmd: SET ATLAS_MVN=%M2_HOME%\bin\mvn - cmd: SET PATH=%M2_HOME%\bin;%JAVA_HOME%\bin;%PATH:C:\Ruby193\bin;=% - cmd: SET JAVA_OPTS=-XX:MaxPermSize=2g -Xmx4g - cmd: mvn --version diff --git a/pom.xml b/pom.xml index abda532..0cd425e 100644 --- a/pom.xml +++ b/pom.xml @@ -1,11 +1,12 @@ + 4.0.0 io.qameta.allure allure-bamboo - 1.13-SNAPSHOT + 2.00-SNAPSHOT Allure for Bamboo Allure reports right in deployment plans in Bamboo atlassian-plugin @@ -15,12 +16,15 @@ 8.0.3-89c970d65 - 6.9.1 + 7.1.4 ${bamboo.version} - 1.2.3 - 1.2.13 + 1.8 + 1.8 + + 2.0.2 + 2.1.7 ${project.groupId}.${project.artifactId} - 1.7.29 + 1.7.36 1.2.17 1.8 @@ -33,16 +37,80 @@ + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + 2.14.0 + + + com.fasterxml.jackson.core + jackson-databind + 2.14.0 + org.buildobjects jproc - 2.2.1 + 2.8.2 com.atlassian.bamboo atlassian-bamboo-web ${bamboo.version} provided + + + commons-beanutils + commons-beanutils + + + org.hamcrest + hamcrest-core + + + com.fasterxml.jackson.core + jackson-databind + + + junit + junit + + + log4j + log4j + + + org.slf4j + slf4j-api + + + net.bytebuddy + byte-buddy + + + org.objenesis + objenesis + + + commons-collections + commons-collections + + + commons-lang + commons-lang + + + commons-logging + commons-logging + + + com.fasterxml.jackson.core + jackson-core + + + org.yaml + snakeyaml + + com.atlassian.templaterenderer @@ -53,7 +121,7 @@ net.lingala.zip4j zip4j - 1.3.3 + 2.11.2 org.slf4j @@ -94,13 +162,19 @@ junit junit - 4.12 + 4.13.2 test + + + org.hamcrest + hamcrest-core + + org.mockito mockito-core - 3.2.4 + 4.11.0 test @@ -115,6 +189,16 @@ atlassian-plugins-osgi-testrunner ${plugin.testrunner.version} test + + + commons-beanutils + commons-beanutils + + + commons-io + commons-io + + javax.ws.rs @@ -127,16 +211,25 @@ commons-beanutils 1.9.4 - - commons-beanutils - commons-beanutils - 1.9.4 - net.sf.json-lib json-lib 2.4 jdk15 + + + commons-beanutils + commons-beanutils + + + commons-collections + commons-collections + + + commons-logging + commons-logging + + diff --git a/src/main/java/io/qameta/allure/bamboo/AllureArtifactsManager.java b/src/main/java/io/qameta/allure/bamboo/AllureArtifactsManager.java index bbe49f6..bd8fc76 100644 --- a/src/main/java/io/qameta/allure/bamboo/AllureArtifactsManager.java +++ b/src/main/java/io/qameta/allure/bamboo/AllureArtifactsManager.java @@ -1,5 +1,6 @@ package io.qameta.allure.bamboo; +import com.atlassian.bamboo.ResultKey; import com.atlassian.bamboo.artifact.MutableArtifact; import com.atlassian.bamboo.artifact.MutableArtifactImpl; import com.atlassian.bamboo.build.BuildDefinition; @@ -19,16 +20,16 @@ import com.atlassian.bamboo.build.artifact.TrampolineArtifactFileData; import com.atlassian.bamboo.build.artifact.TrampolineUrlArtifactLinkDataProvider; import com.atlassian.bamboo.build.artifact.handlers.ArtifactHandlersService; -import com.atlassian.bamboo.chains.Chain; import com.atlassian.bamboo.chains.ChainResultsSummary; import com.atlassian.bamboo.chains.ChainStageResult; import com.atlassian.bamboo.plan.PlanResultKey; import com.atlassian.bamboo.plan.artifact.ArtifactDefinitionContextImpl; +import com.atlassian.bamboo.plan.cache.ImmutableChain; import com.atlassian.bamboo.plugin.BambooPluginUtils; -import com.atlassian.bamboo.plugin.descriptor.predicate.ConjunctionModuleDescriptorPredicate; import com.atlassian.bamboo.resultsummary.BuildResultsSummary; import com.atlassian.bamboo.resultsummary.ResultsSummaryManager; import com.atlassian.bamboo.security.SecureToken; +import com.atlassian.plugin.ModuleDescriptor; import com.atlassian.plugin.PluginAccessor; import com.atlassian.plugin.predicate.EnabledModulePredicate; import com.atlassian.plugin.predicate.ModuleOfClassPredicate; @@ -57,6 +58,7 @@ import java.util.Map; import java.util.Optional; import java.util.UUID; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Predicate; import java.util.stream.Collector; @@ -86,7 +88,6 @@ public class AllureArtifactsManager { private static final Logger LOGGER = LoggerFactory.getLogger(AllureArtifactsManager.class); private static final String REPORTS_SUBDIR = "allure-reports"; - private final PluginAccessor pluginAccessor; private final ArtifactHandlersService artifactHandlersService; private final BuildDefinitionManager buildDefinitionManager; @@ -114,30 +115,29 @@ public AllureArtifactsManager(PluginAccessor pluginAccessor, ArtifactHandlersSer * @param planKeyString key for plan * @param buildNumber build number * @param filePath path of the artifact - * @return empty if cannot get artifact, url if possible + * @return empty if you cannot get artifact, url if possible */ Optional getArtifactUrl(final String planKeyString, final String buildNumber, final String filePath) { final BuildDefinition buildDefinition = buildDefinitionManager.getBuildDefinition(getPlanKey(planKeyString)); final Map artifactConfig = getArtifactHandlersConfig(buildDefinition); final PlanResultKey planResultKey = getPlanResultKey(planKeyString, parseInt(buildNumber)); - return ofNullable(resultsSummaryManager.getResultsSummary(planResultKey)).map(resultsSummary -> - getArtifactHandlerByClassName(fromCustomData(resultsSummary.getCustomBuildData()).getArtifactHandlerClass()) - .map(artifactHandler -> { - if (isAgentArtifactHandler(artifactHandler)) { - return getLocalStorageURL(planKeyString, buildNumber, filePath); - } - final ArtifactDefinitionContextImpl artifactDef = getAllureArtifactDef(); - return ofNullable(artifactHandler.getArtifactLinkDataProvider( - mutableArtifact(planResultKey, artifactDef.getName()), configProvider(artifactConfig) - )).map(linkProvider -> { - if (linkProvider instanceof TrampolineUrlArtifactLinkDataProvider) { - final TrampolineUrlArtifactLinkDataProvider urlLinkProvider = (TrampolineUrlArtifactLinkDataProvider) linkProvider; - urlLinkProvider.setPlanResultKey(planResultKey); - urlLinkProvider.setArtifactName(artifactDef.getName()); - } - return getArtifactFile(filePath, linkProvider); - }).orElse(null); - }).orElse(null)); + return ofNullable(resultsSummaryManager.getResultsSummary(planResultKey)).flatMap(resultsSummary -> getArtifactHandlerByClassName(fromCustomData(resultsSummary.getCustomBuildData()).getArtifactHandlerClass()) + .map(artifactHandler -> { + if (isAgentArtifactHandler(artifactHandler)) { + return getLocalStorageURL(planKeyString, buildNumber, filePath); + } + final ArtifactDefinitionContextImpl artifactDef = getAllureArtifactDef(); + return ofNullable(artifactHandler.getArtifactLinkDataProvider( + mutableArtifact(planResultKey, artifactDef.getName()), configProvider(artifactConfig) + )).map(linkProvider -> { + if (linkProvider instanceof TrampolineUrlArtifactLinkDataProvider) { + final TrampolineUrlArtifactLinkDataProvider urlLinkProvider = (TrampolineUrlArtifactLinkDataProvider) linkProvider; + urlLinkProvider.setPlanResultKey(planResultKey); + urlLinkProvider.setArtifactName(artifactDef.getName()); + } + return getArtifactFile(filePath, linkProvider); + }).orElse(null); + })); } @Nullable @@ -190,14 +190,14 @@ Collection downloadAllArtifactsTo(@NotNull ChainResultsSummary chainResult } /** - * Copy all of the build's artifacts for this build across to the builds artifact directory + * Copy all the build's artifacts for this build across to the builds artifact directory * * @param chain chain * @param summary results summary * @param reportDir directory of a report * @return empty if not applicable, result otherwise */ - Optional uploadReportArtifacts(@NotNull Chain chain, @NotNull ChainResultsSummary summary, File reportDir) { + Optional uploadReportArtifacts(@NotNull ImmutableChain chain, @NotNull ChainResultsSummary summary, File reportDir) { try { final ArtifactDefinitionContextImpl artifact = getAllureArtifactDef(); artifact.setLocation(""); @@ -230,7 +230,7 @@ Optional uploadReportArtifacts(@NotNull Chain chain, @NotNull @Override public ArtifactHandlerPublishingResult call() { try { - return artifactHandler.publish(summary.getPlanResultKey(), artifact, artifactPublishingConfig); + return artifactHandler.publish((ResultKey) summary.getPlanResultKey(), artifact, artifactPublishingConfig); } catch (final Exception e) { LOGGER.error("Failed to publish Allure Report using handler " + artifactHandler.getClass().getName(), e); return ArtifactHandlerPublishingResultImpl.failure(); @@ -264,7 +264,7 @@ private void downloadAllArtifactsTo(ArtifactLinkDataProvider dataProvider, File data = trampolineData.getDelegate(); if (data.getFileType().equals(ArtifactFileData.FileType.REGULAR_FILE)) { final String fileName = Paths.get(data.getName()).toFile().getName(); - copyURLToFile(new URL(data.getUrl()), Paths.get(tempDir.getPath(), fileName).toFile()); + copyURLToFile(new URL(requireNonNull(data.getUrl())), Paths.get(tempDir.getPath(), fileName).toFile()); } else { downloadAllArtifactsTo(dataProvider, tempDir, trampolineData.getTag()); } @@ -280,7 +280,8 @@ private void logAndThrow(Exception e, String message) { throw new RuntimeException(message, e); } - private void downloadAllArtifactsTo(FileSystemArtifactLinkDataProvider dataProvider, File tempDir) { + @SuppressWarnings("UnstableApiUsage") + private void downloadAllArtifactsTo(@NotNull FileSystemArtifactLinkDataProvider dataProvider, File tempDir) { ofNullable(dataProvider.getFile().listFiles()).map(Arrays::asList).ifPresent(list -> list.forEach(file -> { try { if (file.isFile()) { @@ -297,12 +298,13 @@ private void downloadAllArtifactsTo(FileSystemArtifactLinkDataProvider dataProvi @Nullable private String getArtifactFile(String filePath, ArtifactLinkDataProvider linkProvider) { + String fixedFilePath = filePath.replaceFirst("^/", ""); if (linkProvider instanceof FileSystemArtifactLinkDataProvider) { - return linkProvider.getRootUrl() + return requireNonNull(linkProvider.getRootUrl()) .replaceFirst("BASE_URL", getBaseUrl().build().toString()) - .replace("index.html", isEmpty(filePath) ? "index.html" : filePath); + .replace("index.html", isEmpty(fixedFilePath) ? "index.html" : fixedFilePath); } else { - final Iterable datas = linkProvider.listObjects(filePath); + final Iterable datas = linkProvider.listObjects(fixedFilePath); if (size(datas) == 1) { ArtifactFileData data = datas.iterator().next(); if (data instanceof TrampolineArtifactFileData) { @@ -343,7 +345,6 @@ private Map getArtifactHandlersConfig(BuildDefinition buildDefin final Map config = artifactHandlersService.getRuntimeConfiguration(); final Map planCustomConfiguration = buildDefinition.getCustomConfiguration(); if (ArtifactHandlingUtils.isCustomArtifactHandlingConfigured(planCustomConfiguration)) { - // This hacky way it's compatible with both Bamboo 5.x and Bamboo 6.x final Collector, ?, Map> toMap = toMap(Map.Entry::getKey, Map.Entry::getValue); final Predicate> isArtifactHandler = e -> e.getKey().startsWith(ARTIFACT_HANDLERS_CONFIG_PREFIX); final Predicate> isNotHandlerSwitch = e -> SHARED_NON_SHARED_ONOFF_OPTION_NAME.values().stream().noneMatch(o -> e.getKey().endsWith(o)); @@ -358,12 +359,9 @@ private MutableArtifact mutableArtifact(PlanResultKey planResultKey, String name return new MutableArtifactImpl(name, planResultKey, null, false, 0L); } - private List getArtifactHandlers() { - final ConjunctionModuleDescriptorPredicate predicate = new ConjunctionModuleDescriptorPredicate<>(); - - predicate.append(new ModuleOfClassPredicate<>(ArtifactHandler.class)); - predicate.append(new EnabledModulePredicate<>()); + private List getArtifactHandlers() { + Predicate> predicate = new ModuleOfClassPredicate<>(ArtifactHandler.class).and(new EnabledModulePredicate()); return ImmutableList.copyOf(pluginAccessor.getModules(predicate)); } @@ -374,15 +372,14 @@ private boolean isAgentArtifactHandler(ArtifactHandler artifactHandler) { @SuppressWarnings("unchecked") private Optional getArtifactHandlerByClassName(String className) { - final ConjunctionModuleDescriptorPredicate predicate = new ConjunctionModuleDescriptorPredicate<>(); + AtomicReference>> predicate = new AtomicReference<>(); return ofNullable(className).map(clazz -> { final Class aClass; try { aClass = (Class) Class.forName(clazz); - predicate.append(new ModuleOfClassPredicate<>(aClass)); - predicate.append(new EnabledModulePredicate<>()); + predicate.set(new ModuleOfClassPredicate<>(aClass).and(new EnabledModulePredicate())); - return pluginAccessor.getModules(predicate).stream().findAny().orElse(null); + return pluginAccessor.getModules(predicate.get()).stream().findAny().orElse(null); } catch (ClassNotFoundException e) { LOGGER.error("Failed to find artifact handler for class name " + className, e); } diff --git a/src/main/java/io/qameta/allure/bamboo/AllureBuildCompleteAction.java b/src/main/java/io/qameta/allure/bamboo/AllureBuildCompleteAction.java index 2a0ecf4..632297f 100644 --- a/src/main/java/io/qameta/allure/bamboo/AllureBuildCompleteAction.java +++ b/src/main/java/io/qameta/allure/bamboo/AllureBuildCompleteAction.java @@ -1,41 +1,54 @@ package io.qameta.allure.bamboo; import com.atlassian.bamboo.build.BuildDefinition; -import com.atlassian.bamboo.chains.Chain; import com.atlassian.bamboo.chains.ChainExecution; import com.atlassian.bamboo.chains.ChainResultsSummary; import com.atlassian.bamboo.chains.plugins.PostChainAction; import com.atlassian.bamboo.configuration.AdministrationConfiguration; +import com.atlassian.bamboo.plan.cache.ImmutableChain; import com.atlassian.bamboo.resultsummary.ResultsSummary; import com.atlassian.bamboo.resultsummary.ResultsSummaryManager; import com.atlassian.bamboo.v2.build.BaseConfigurablePlugin; import com.atlassian.spring.container.ContainerManager; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; +import com.google.gson.JsonParser; import io.qameta.allure.bamboo.info.AddExecutorInfo; +import io.qameta.allure.bamboo.info.allurewidgets.summary.Summary; +import io.qameta.allure.bamboo.util.Downloader; +import io.qameta.allure.bamboo.util.FileStringReplacer; +import io.qameta.allure.bamboo.util.ZipUtil; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; -import java.net.HttpURLConnection; +import java.io.Writer; import java.net.URL; import java.net.URLConnection; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.regex.Pattern; + import static com.google.common.io.Files.createTempDir; import static io.qameta.allure.bamboo.AllureBuildResult.allureBuildResult; import static io.qameta.allure.bamboo.util.ExceptionUtil.stackTraceToString; import static java.lang.String.format; -import static java.util.Arrays.*; +import static java.nio.file.Files.createTempFile; +import static java.util.Arrays.asList; import static java.util.stream.Collectors.toList; import static org.apache.commons.io.FileUtils.deleteQuietly; import static org.codehaus.plexus.util.FileUtils.copyDirectory; @@ -44,8 +57,7 @@ public class AllureBuildCompleteAction extends BaseConfigurablePlugin implements PostChainAction { private static final Logger LOGGER = LoggerFactory.getLogger(AllureBuildCompleteAction.class); - private static final List HISTORY_FILES = asList("history.json", "history-trend.json"); - + private static final List HISTORY_FILES = asList("history.json", "history-trend.json", "categories-trend.json", "duration-trend.json"); private final AllureExecutableProvider allureExecutable; private final AllureSettingsManager settingsManager; private final AllureArtifactsManager artifactsManager; @@ -67,7 +79,8 @@ public AllureBuildCompleteAction(AllureExecutableProvider allureExecutable, } @Override - public void execute(@NotNull Chain chain, @NotNull ChainResultsSummary chainResultsSummary, @NotNull ChainExecution chainExecution) throws Exception { + @SuppressWarnings("UnstableApiUsage") + public void execute(@NotNull ImmutableChain chain, @NotNull ChainResultsSummary chainResultsSummary, @NotNull ChainExecution chainExecution) { final BuildDefinition buildDef = chain.getBuildDefinition(); final AllureGlobalConfig globalConfig = settingsManager.getSettings(); final AllureBuildConfig buildConfig = AllureBuildConfig.fromContext(buildDef.getCustomConfiguration()); @@ -101,11 +114,21 @@ public void execute(@NotNull Chain chain, @NotNull ChainResultsSummary chainResu } else { LOGGER.info("Starting allure generate into {} for {}", allureReportDir.getPath(), chain.getName()); prepareResults(artifactsPaths.stream().map(Path::toFile).collect(toList()), chain, chainExecution); + + // Setting the new logo in the allure libraries before generate the report. + if (globalConfig.isCustomLogoEnabled()) { + allure.setCustomLogo(buildConfig.getCustomLogoUrl()); + } allure.generate(artifactsPaths, allureReportDir.toPath()); + // Setting report name + this.finalize(allureReportDir, chainExecution.getPlanResultKey().getBuildNumber(), chain.getBuildName()); + + // Create an exportable zip with the report + ZipUtil.zipFolder(allureReportDir.toPath(), allureReportDir.toPath().resolve("report.zip")); + LOGGER.info("Allure has been generated successfully for {}", chain.getName()); artifactsManager.uploadReportArtifacts(chain, chainResultsSummary, allureReportDir) .ifPresent(result -> result.dumpToCustomData(customBuildData)); - } } catch (Exception e) { LOGGER.error("Failed to build allure report for {}", chain.getName(), e); @@ -116,7 +139,31 @@ public void execute(@NotNull Chain chain, @NotNull ChainResultsSummary chainResu } } - private void prepareResults(List artifactsTempDirs, Chain chain, ChainExecution chainExecution) throws IOException, InterruptedException { + private void finalize(@NotNull File allureReportDir, int buildNumber, String buildName) throws IOException { + //////////////////// + // Update Report Name (It is the way now) + Path widgetsJsonPath = Paths.get(allureReportDir.getAbsolutePath()).resolve("widgets").resolve("summary.json"); + ObjectMapper mapper = new ObjectMapper(); + Summary summary = mapper.readValue(widgetsJsonPath.toFile(), Summary.class); + summary.setReportName(String.format("Build %s - %s", buildNumber, buildName)); + mapper.writeValue(widgetsJsonPath.toFile(), summary); + //////////////////// + // Deleting title from Logo + Path appJsPath = Paths.get(allureReportDir.getAbsolutePath()).resolve("app.js"); + FileStringReplacer.replaceInFile(appJsPath, + Pattern.compile(">Allure", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.COMMENTS), + "> " + ); + //////////////////// + // Changing page title + Path indexHtmlPath = Paths.get(allureReportDir.getAbsolutePath()).resolve("index.html"); + FileStringReplacer.replaceInFile(indexHtmlPath, + Pattern.compile(".*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.COMMENTS), + String.format(" Build %s - %s ", buildNumber, buildName) + ); + } + + private void prepareResults(List artifactsTempDirs, @NotNull ImmutableChain chain, @NotNull ChainExecution chainExecution) { copyHistory(artifactsTempDirs, chain.getPlanKey().getKey(), chainExecution.getPlanResultKey().getBuildNumber()); addExecutorInfo(artifactsTempDirs, chain, chainExecution.getPlanResultKey().getBuildNumber()); } @@ -124,7 +171,8 @@ private void prepareResults(List artifactsTempDirs, Chain chain, ChainExec /** * Write the history file to results directory. */ - private void copyHistory(List artifactsTempDirs, String planKey, int buildNumber) { + @SuppressWarnings("UnstableApiUsage") + private void copyHistory(@NotNull List artifactsTempDirs, String planKey, int buildNumber) { final Path tmpDirToDownloadHistory = createTempDir().toPath(); getLastBuildNumberWithHistory(planKey, buildNumber) .ifPresent(buildId -> copyHistoryFiles(planKey, tmpDirToDownloadHistory, buildId)); @@ -158,13 +206,15 @@ private Optional getLastBuildNumberWithHistory(String planKey, int buil private boolean historyArtifactExists(String planKey, int buildId) { String artifactUrl = getHistoryArtifactUrl("history.json", planKey, buildId); + ObjectMapper mapper = new ObjectMapper(); + JsonParser parser = new JsonParser(); try { - HttpURLConnection.setFollowRedirects(false); - HttpURLConnection con = (HttpURLConnection) new URL(artifactUrl).openConnection(); - con.setRequestMethod("HEAD"); - return con.getResponseCode() == HttpURLConnection.HTTP_OK; + final Path historyTmpFile = createTempFile("history", ".json"); + Downloader.download(new URL(artifactUrl), historyTmpFile); + mapper.readValue(historyTmpFile.toFile(), Object.class); + return true; } catch (Exception e) { - LOGGER.info("Cannot connect to artifact {}.", artifactUrl, e); + LOGGER.info("Cannot connect to artifact or the artifact is not valid {}.", artifactUrl, e); return false; } } @@ -190,16 +240,17 @@ private String getHistoryArtifactUrl(String fileName, String planKey, int buildI getBambooBaseUrl(), planKey, buildId, fileName); } - private void addExecutorInfo(List artifactsTempDirs, Chain chain, int buildNumber) throws IOException, InterruptedException { + private void addExecutorInfo(@NotNull List artifactsTempDirs, @NotNull ImmutableChain chain, int buildNumber) { final String rootUrl = getBambooBaseUrl(); final String buildName = chain.getBuildName(); final String buildUrl = String.format("%s/browse/%s-%s", rootUrl, chain.getPlanKey().getKey(), buildNumber); - final String reportUrl = String.format("%s/plugins/servlet/allure/report/%s/%s/", rootUrl, + final String reportUrl = String.format("%s/plugins/servlet/allure/report/%s/%s", rootUrl, chain.getPlanKey().getKey(), buildNumber); final AddExecutorInfo executorInfo = new AddExecutorInfo(rootUrl, Integer.toString(buildNumber), buildName, buildUrl, reportUrl); artifactsTempDirs.forEach(executorInfo::invoke); } + /** * Returns the base url of bamboo server. */ diff --git a/src/main/java/io/qameta/allure/bamboo/AllureBuildConfig.java b/src/main/java/io/qameta/allure/bamboo/AllureBuildConfig.java index 7395c4c..197efa6 100644 --- a/src/main/java/io/qameta/allure/bamboo/AllureBuildConfig.java +++ b/src/main/java/io/qameta/allure/bamboo/AllureBuildConfig.java @@ -1,5 +1,8 @@ package io.qameta.allure.bamboo; +import org.apache.commons.validator.routines.UrlValidator; + +import javax.annotation.Nullable; import java.io.Serializable; import java.util.Map; @@ -7,8 +10,10 @@ import static io.qameta.allure.bamboo.AllureConstants.ALLURE_CONFIG_ENABLED; import static io.qameta.allure.bamboo.AllureConstants.ALLURE_CONFIG_EXECUTABLE; import static io.qameta.allure.bamboo.AllureConstants.ALLURE_CONFIG_FAILED_ONLY; +import static io.qameta.allure.bamboo.AllureConstants.ALLURE_CUSTOM_LOGO_PATH; import static java.lang.Boolean.FALSE; import static java.lang.Boolean.TRUE; +import static java.util.Optional.ofNullable; import static org.apache.commons.lang.StringUtils.isEmpty; public class AllureBuildConfig implements Serializable { @@ -16,22 +21,34 @@ public class AllureBuildConfig implements Serializable { private final String executable; private final boolean enabled; private final String artifactName; + private final String logoUrl; + private final static String DEFAULT_ARTIFACT_NAME = "allure-results"; + public final static String DEFAULT_CUSTOM_LOGO_URL = "https://qameta.io/allure-report/img/reportlogo.svg"; - private AllureBuildConfig(String executable, String enabled, String onlyForFailed, String artifactName) { + private AllureBuildConfig(String executable, String enabled, String onlyForFailed, String artifactName, String logoUrl) { this.onlyForFailed = isEmpty(onlyForFailed) ? TRUE : Boolean.parseBoolean(onlyForFailed); this.enabled = isEmpty(enabled) ? FALSE : Boolean.parseBoolean(enabled); this.executable = executable; this.artifactName = artifactName; + // If the URL is not a valid URL it will be omitted + UrlValidator urlValidator = new UrlValidator(); + this.logoUrl = urlValidator.isValid(logoUrl) ? logoUrl : AllureBuildConfig.DEFAULT_CUSTOM_LOGO_URL; } static AllureBuildConfig fromContext(Map context) { - final String failedOnlyString = context.get(ALLURE_CONFIG_FAILED_ONLY); - final String enableAllureString = context.get(ALLURE_CONFIG_ENABLED); return new AllureBuildConfig( - context.get(ALLURE_CONFIG_EXECUTABLE), - enableAllureString, - failedOnlyString, - context.get(ALLURE_CONFIG_ARTIFACT_NAME)); + getSingleValue(context, ALLURE_CONFIG_EXECUTABLE, null), + getSingleValue(context, ALLURE_CONFIG_ENABLED, FALSE.toString()), + getSingleValue(context, ALLURE_CONFIG_FAILED_ONLY, FALSE.toString()), + getSingleValue(context, ALLURE_CONFIG_ARTIFACT_NAME, AllureBuildConfig.DEFAULT_ARTIFACT_NAME), + getSingleValue(context, ALLURE_CUSTOM_LOGO_PATH, AllureBuildConfig.DEFAULT_CUSTOM_LOGO_URL)); + } + + @Nullable + private static String getSingleValue(Map context, String key, String defaultVal) { + return ofNullable(context.get(key)) + .map(value -> value instanceof String[] ? ((String[]) value)[0] : (String) value) + .orElse(defaultVal); } boolean isOnlyForFailed() { @@ -53,4 +70,9 @@ boolean isEnabled() { public String getArtifactName() { return artifactName; } + + public String getCustomLogoUrl() { + return this.logoUrl; + } + } diff --git a/src/main/java/io/qameta/allure/bamboo/AllureBuildConfigurator.java b/src/main/java/io/qameta/allure/bamboo/AllureBuildConfigurator.java index 48f44ec..ed798d3 100644 --- a/src/main/java/io/qameta/allure/bamboo/AllureBuildConfigurator.java +++ b/src/main/java/io/qameta/allure/bamboo/AllureBuildConfigurator.java @@ -5,13 +5,12 @@ import com.atlassian.bamboo.plan.configuration.MiscellaneousPlanConfigurationPlugin; import com.atlassian.bamboo.utils.error.ErrorCollection; import com.atlassian.bamboo.v2.build.BaseConfigurablePlugin; -import com.atlassian.bamboo.v2.build.configuration.MiscellaneousBuildConfigurationPlugin; import com.atlassian.bamboo.ww2.actions.build.admin.create.BuildConfiguration; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - import static com.atlassian.bamboo.plan.PlanClassHelper.isChain; +import static com.atlassian.bamboo.plan.PlanClassHelper.isChain; import static io.qameta.allure.bamboo.AllureConstants.ALLURE_CONFIG_ENABLED; import static io.qameta.allure.bamboo.AllureConstants.ALLURE_CONFIG_EXECUTABLE; import static io.qameta.allure.bamboo.AllureConstants.ALLURE_CONFIG_FAILED_ONLY; @@ -19,7 +18,6 @@ import static java.util.Optional.ofNullable; import static org.apache.commons.lang.StringUtils.isEmpty; -@SuppressWarnings("unchecked") public class AllureBuildConfigurator extends BaseConfigurablePlugin implements MiscellaneousPlanConfigurationPlugin { @@ -62,7 +60,7 @@ public void prepareConfigObject(@NotNull BuildConfiguration buildConfiguration) buildConfiguration.setProperty(ALLURE_CONFIG_FAILED_ONLY, TRUE); } if (buildConfiguration.getProperty(ALLURE_CONFIG_EXECUTABLE) == null) { - ofNullable(executablesManager).map(manager -> manager.getDefaultAllureExecutable().orElse(null)) + ofNullable(executablesManager).flatMap(BambooExecutablesManager::getDefaultAllureExecutable) .ifPresent(executable -> buildConfiguration.setProperty(ALLURE_CONFIG_EXECUTABLE, executable)); } } diff --git a/src/main/java/io/qameta/allure/bamboo/AllureCommandLineSupport.java b/src/main/java/io/qameta/allure/bamboo/AllureCommandLineSupport.java index d939bbb..c07313a 100644 --- a/src/main/java/io/qameta/allure/bamboo/AllureCommandLineSupport.java +++ b/src/main/java/io/qameta/allure/bamboo/AllureCommandLineSupport.java @@ -3,6 +3,7 @@ import org.buildobjects.process.ProcBuilder; import org.jetbrains.annotations.NotNull; +import java.io.File; import java.nio.file.Paths; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -14,7 +15,7 @@ public class AllureCommandLineSupport { private static final Pattern RESULT_TC_COUNT_REGEX = Pattern.compile(".+Found (\\d+) test cases.+", Pattern.DOTALL); - private static final int GENERATE_TIMEOUT_MS = (int) MINUTES.toMillis(5); + private static final int GENERATE_TIMEOUT_MS = (int) MINUTES.toMillis(10); String runCommand(String cmd, String... args) { return new ProcBuilder(cmd) @@ -41,6 +42,8 @@ boolean isWindows() { } boolean hasCommand(String command) { - return Paths.get(command).toFile().exists(); + File cmdFile = Paths.get(command).toFile(); + // It needs to be sure that the command is a file + return cmdFile.exists() && cmdFile.isFile(); } } diff --git a/src/main/java/io/qameta/allure/bamboo/AllureConstants.java b/src/main/java/io/qameta/allure/bamboo/AllureConstants.java index 21fa404..331e5df 100644 --- a/src/main/java/io/qameta/allure/bamboo/AllureConstants.java +++ b/src/main/java/io/qameta/allure/bamboo/AllureConstants.java @@ -36,8 +36,13 @@ final class AllureConstants { static String ALLURE_CONFIG_DOWNLOAD_ENABLED = "custom.allure.config.download.enabled"; static String ALLURE_CONFIG_ENABLED_BY_DEFAULT = "custom.allure.config.enabled.default"; static String ALLURE_CONFIG_DOWNLOAD_URL = "custom.allure.config.download.url"; + static String ALLURE_CONFIG_DOWNLOAD_CLI_URL = "custom.allure.config.download.cli.url"; static String ALLURE_CONFIG_LOCAL_STORAGE = "custom.allure.config.local.storage"; + // ALLURE CUSTOM LOGO + static String ALLURE_CUSTOM_LOGO_ENABLED = "custom.allure.config.logo.enabled"; + static String ALLURE_CUSTOM_LOGO_PATH = "custom.allure.logo.url"; + private AllureConstants() { } } diff --git a/src/main/java/io/qameta/allure/bamboo/AllureDownloader.java b/src/main/java/io/qameta/allure/bamboo/AllureDownloader.java index 0d28673..b4de59a 100644 --- a/src/main/java/io/qameta/allure/bamboo/AllureDownloader.java +++ b/src/main/java/io/qameta/allure/bamboo/AllureDownloader.java @@ -1,33 +1,26 @@ package io.qameta.allure.bamboo; -import net.lingala.zip4j.core.ZipFile; -import net.lingala.zip4j.exception.ZipException; +import io.qameta.allure.bamboo.util.Downloader; +import io.qameta.allure.bamboo.util.ZipUtil; +import org.apache.commons.compress.archivers.ArchiveException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; -import java.io.InputStream; -import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; -import java.net.URLConnection; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Optional; -import static java.lang.Integer.getInteger; import static java.nio.file.Files.createTempFile; -import static java.util.concurrent.TimeUnit.SECONDS; import static javax.ws.rs.core.UriBuilder.fromPath; -import static org.apache.commons.io.FileUtils.copyInputStreamToFile; import static org.apache.commons.io.FileUtils.deleteQuietly; import static org.apache.commons.io.FileUtils.moveDirectory; class AllureDownloader { private static final Logger LOGGER = LoggerFactory.getLogger(AllureDownloader.class); - private static final int CONN_TIMEOUT_MS = (int) SECONDS.toMillis(getInteger("allure.download.conn.timeout.sec", 10)); - private static final int DOWNLOAD_TIMEOUT_MS = (int) SECONDS.toMillis(getInteger("allure.download.timeout.sec", 60)); private final AllureSettingsManager settingsManager; @@ -41,15 +34,16 @@ Optional downloadAndExtractAllureTo(String allureHomeDir, String version) LOGGER.info("Extracting file {} to {}...", zipFilePath, allureHomeDir); final String extractedDirName = "allure-" + version; final File homeDir = new File(allureHomeDir); - final Path extracteDir = zipFilePath.getParent(); - new ZipFile(zipFilePath.toFile()).extractAll(extracteDir.toString()); + final Path extractDir = zipFilePath.getParent(); + ZipUtil.unzip(zipFilePath, extractDir.toString()); + if (homeDir.exists()) { LOGGER.info("Directory " + homeDir + " already exists, removing it.."); deleteQuietly(homeDir); } - moveDirectory(extracteDir.resolve(extractedDirName).toFile(), homeDir); + moveDirectory(extractDir.resolve(extractedDirName).toFile(), homeDir); return Paths.get(allureHomeDir); - } catch (ZipException | IOException e) { + } catch (ArchiveException | IOException e) { LOGGER.error("Failed to download and extract Allure of version {} to dir {}", version, allureHomeDir, e); return null; } finally { @@ -65,21 +59,11 @@ private Optional downloadAllure(String version) { try { final Path downloadToFile = createTempFile("allure", ".zip"); LOGGER.info("Downloading allure.zip from {} to {}", url, downloadToFile); - final URLConnection connection = url.openConnection(); - connection.setConnectTimeout(CONN_TIMEOUT_MS); - connection.setReadTimeout(DOWNLOAD_TIMEOUT_MS); - connection.setRequestProperty("Connection", "close"); - connection.setRequestProperty("Pragma", "no-cache"); - ((HttpURLConnection) connection).setInstanceFollowRedirects(true); - connection.connect(); - try (InputStream input = connection.getInputStream()) { - copyInputStreamToFile(input, downloadToFile.toFile()); - return Optional.of(downloadToFile); - } + return Downloader.download(url, downloadToFile); } catch (Exception e) { LOGGER - .warn("Failed to download from {}. Root cause : {}. Trying with next url.", - url, e.getMessage()); + .warn("Failed to download from {}. Root cause : {}.", + url, e.toString()); } } } catch (Exception e) { @@ -89,13 +73,13 @@ private Optional downloadAllure(String version) { } private URL[] buildAllureDownloadUrls(String version) throws MalformedURLException { - URL oldUrl = fromPath(settingsManager.getSettings().getDownloadBaseUrl()) + URL gitUrl = fromPath(settingsManager.getSettings().getDownloadBaseUrl()) .path(version + "/" + "allure-" + version + ".zip") .build().toURL(); String binaryName = "allure-commandline"; - URL newUrl = fromPath(settingsManager.getSettings().getDownloadBaseUrl()) + URL mavenUrl = fromPath(settingsManager.getSettings().getDownloadCliBaseUrl()) .path(binaryName + "/" + version + "/" + binaryName + "-" + version + ".zip") .build().toURL(); - return new URL[]{oldUrl, newUrl}; + return new URL[]{gitUrl, mavenUrl}; } } diff --git a/src/main/java/io/qameta/allure/bamboo/AllureExecutable.java b/src/main/java/io/qameta/allure/bamboo/AllureExecutable.java index a052e31..22ac4b6 100644 --- a/src/main/java/io/qameta/allure/bamboo/AllureExecutable.java +++ b/src/main/java/io/qameta/allure/bamboo/AllureExecutable.java @@ -1,15 +1,24 @@ package io.qameta.allure.bamboo; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLMapper; +import io.qameta.allure.bamboo.info.AllurePlugins; +import io.qameta.allure.bamboo.util.FileStringReplacer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; +import java.io.File; +import java.io.IOException; +import java.net.URL; import java.nio.file.Path; import java.util.Collection; import java.util.LinkedList; +import java.util.regex.Pattern; import static java.util.Arrays.asList; import static java.util.stream.Collectors.toList; +import static javax.ws.rs.core.UriBuilder.fromPath; class AllureExecutable { private static final Logger LOGGER = LoggerFactory.getLogger(AllureExecutable.class); @@ -41,6 +50,48 @@ AllureGenerateResult generate(Collection sourceDirs, Path targetDir) { } } + public void setCustomLogo(String logoUrl) { + final String pluginName = "custom-logo-plugin"; + final String allureConfigFileName = "allure.yml"; + final String cssFileName = "styles.css"; + + Path rootPath = this.cmdPath.getParent().getParent(); + Path configFolder = rootPath.resolve("config"); + Path logoPluginFolder = rootPath.resolve("plugins").resolve(pluginName).resolve("static"); + + /// Editing Yaml to add plugin + ObjectMapper objectMapper = new YAMLMapper(); + try { + File configFile = configFolder.resolve(allureConfigFileName).toFile(); + AllurePlugins ap = objectMapper.readValue(configFile, AllurePlugins.class); + //Saving the file only if it necessary + if (ap.registerPlugin(pluginName)) { + objectMapper.writeValue(configFile, ap); + } + //Setting new Logo + URL srcLogoUrl = fromPath(logoUrl).build().toURL(); + FileStringReplacer.replaceInFile(logoPluginFolder.resolve(cssFileName), + Pattern.compile("url\\('.+'\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.COMMENTS), + "url(" + srcLogoUrl.toString() + ")" + ); + + // aligning logo to center + FileStringReplacer.replaceInFile(logoPluginFolder.resolve(cssFileName), + Pattern.compile("(?<=\\s )left",Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.COMMENTS), + "center" + ); + // removing margin + FileStringReplacer.replaceInFile(logoPluginFolder.resolve(cssFileName), + Pattern.compile("10px",Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.COMMENTS), + "0px" + ); + + } catch (IOException e) { + LOGGER.error(e.toString()); + throw new RuntimeException(e); + } + } + Path getCmdPath() { return cmdPath; } diff --git a/src/main/java/io/qameta/allure/bamboo/AllureExecutableProvider.java b/src/main/java/io/qameta/allure/bamboo/AllureExecutableProvider.java index 3d15f1d..fa7e21b 100644 --- a/src/main/java/io/qameta/allure/bamboo/AllureExecutableProvider.java +++ b/src/main/java/io/qameta/allure/bamboo/AllureExecutableProvider.java @@ -15,8 +15,8 @@ public class AllureExecutableProvider { private static final Logger LOGGER = LoggerFactory.getLogger(AllureExecutableProvider.class); - static final String DEFAULT_VERSION = "2.17.2"; - static final String DEFAULT_PATH = "/tmp/allure/2.17.2"; + static final String DEFAULT_VERSION = "2.19.0"; + static final String DEFAULT_PATH = "/tmp/allure/2.19.0"; private static final Pattern EXEC_NAME_PATTERN = compile("[^\\d]*(\\d[0-9\\.]{2,}[a-zA-Z0-9\\-]*)$"); private static final String BINARY_SUBDIR = "binary"; diff --git a/src/main/java/io/qameta/allure/bamboo/AllureGlobalConfig.java b/src/main/java/io/qameta/allure/bamboo/AllureGlobalConfig.java index 1773fd6..6de9875 100644 --- a/src/main/java/io/qameta/allure/bamboo/AllureGlobalConfig.java +++ b/src/main/java/io/qameta/allure/bamboo/AllureGlobalConfig.java @@ -1,14 +1,18 @@ package io.qameta.allure.bamboo; +import org.jetbrains.annotations.NotNull; + import javax.annotation.Nullable; import java.io.File; import java.io.Serializable; import java.util.Map; +import static io.qameta.allure.bamboo.AllureConstants.ALLURE_CONFIG_DOWNLOAD_CLI_URL; import static io.qameta.allure.bamboo.AllureConstants.ALLURE_CONFIG_DOWNLOAD_ENABLED; import static io.qameta.allure.bamboo.AllureConstants.ALLURE_CONFIG_DOWNLOAD_URL; import static io.qameta.allure.bamboo.AllureConstants.ALLURE_CONFIG_ENABLED_BY_DEFAULT; import static io.qameta.allure.bamboo.AllureConstants.ALLURE_CONFIG_LOCAL_STORAGE; +import static io.qameta.allure.bamboo.AllureConstants.ALLURE_CUSTOM_LOGO_ENABLED; import static java.lang.Boolean.FALSE; import static java.lang.Boolean.TRUE; import static java.lang.Boolean.parseBoolean; @@ -18,30 +22,38 @@ class AllureGlobalConfig implements Serializable { private static final String DEFAULT_DOWNLOAD_BASE_URL = "https://github.com/allure-framework/allure2/releases/download/"; + private static final String DEFAULT_CLI_BASE_URL = "https://repo.maven.apache.org/maven2/io/qameta/allure/"; public static final String DEFAULT_LOCAL_STORAGE_PATH = new File(getJavaIoTmpDir(), "allure-reports").getPath(); private final boolean downloadEnabled; + private final boolean customLogoEnabled; private final boolean enabledByDefault; private final String localStoragePath; private final String downloadBaseUrl; + private final String downloadCliBaseUrl; AllureGlobalConfig() { - this(TRUE.toString(), FALSE.toString(), DEFAULT_DOWNLOAD_BASE_URL, DEFAULT_LOCAL_STORAGE_PATH); + this(TRUE.toString(), FALSE.toString(), DEFAULT_DOWNLOAD_BASE_URL, DEFAULT_LOCAL_STORAGE_PATH, DEFAULT_CLI_BASE_URL, TRUE.toString()); } - AllureGlobalConfig(String downloadEnabled, String enabledByDefault, String downloadBaseUrl, String localStoragePath) { + AllureGlobalConfig(String downloadEnabled, String enabledByDefault, String downloadBaseUrl, String localStoragePath, String cmdLineUrl, String customLogoEnable) { this.downloadEnabled = isEmpty(downloadEnabled) ? TRUE : parseBoolean(downloadEnabled); this.enabledByDefault = isEmpty(enabledByDefault) ? FALSE : parseBoolean(enabledByDefault); this.downloadBaseUrl = isEmpty(downloadBaseUrl) ? DEFAULT_DOWNLOAD_BASE_URL : downloadBaseUrl; + this.downloadCliBaseUrl = isEmpty(cmdLineUrl) ? DEFAULT_CLI_BASE_URL : cmdLineUrl; this.localStoragePath = isEmpty(localStoragePath) ? DEFAULT_LOCAL_STORAGE_PATH : localStoragePath; + this.customLogoEnabled = isEmpty(customLogoEnable) ? TRUE : parseBoolean(customLogoEnable); } + @NotNull static AllureGlobalConfig fromContext(Map context) { return new AllureGlobalConfig( - getSingleValue(context, ALLURE_CONFIG_DOWNLOAD_ENABLED, TRUE.toString()), + getSingleValue(context, ALLURE_CONFIG_DOWNLOAD_ENABLED, FALSE.toString()), getSingleValue(context, ALLURE_CONFIG_ENABLED_BY_DEFAULT, FALSE.toString()), - getSingleValue(context, ALLURE_CONFIG_DOWNLOAD_URL, null), - getSingleValue(context, ALLURE_CONFIG_LOCAL_STORAGE, null) + getSingleValue(context, ALLURE_CONFIG_DOWNLOAD_URL, DEFAULT_DOWNLOAD_BASE_URL), + getSingleValue(context, ALLURE_CONFIG_LOCAL_STORAGE, DEFAULT_LOCAL_STORAGE_PATH), + getSingleValue(context, ALLURE_CONFIG_DOWNLOAD_CLI_URL, DEFAULT_CLI_BASE_URL), + getSingleValue(context, ALLURE_CUSTOM_LOGO_ENABLED, FALSE.toString()) ); } @@ -52,6 +64,15 @@ private static String getSingleValue(Map context, String key, String defaultVal) .orElse(defaultVal); } + void toContext(@NotNull Map context) { + context.put(ALLURE_CONFIG_DOWNLOAD_ENABLED, isDownloadEnabled()); + context.put(ALLURE_CONFIG_ENABLED_BY_DEFAULT, isEnabledByDefault()); + context.put(ALLURE_CONFIG_DOWNLOAD_URL, getDownloadBaseUrl()); + context.put(ALLURE_CONFIG_DOWNLOAD_CLI_URL, getDownloadCliBaseUrl()); + context.put(ALLURE_CONFIG_LOCAL_STORAGE, getLocalStoragePath()); + context.put(ALLURE_CUSTOM_LOGO_ENABLED, isCustomLogoEnabled()); + } + boolean isDownloadEnabled() { return downloadEnabled; } @@ -60,17 +81,18 @@ boolean isEnabledByDefault() { return enabledByDefault; } - void toContext(Map context) { - context.put(ALLURE_CONFIG_DOWNLOAD_ENABLED, isDownloadEnabled()); - context.put(ALLURE_CONFIG_ENABLED_BY_DEFAULT, isEnabledByDefault()); - context.put(ALLURE_CONFIG_DOWNLOAD_URL, getDownloadBaseUrl()); - context.put(ALLURE_CONFIG_LOCAL_STORAGE, getLocalStoragePath()); + boolean isCustomLogoEnabled() { + return customLogoEnabled; } String getDownloadBaseUrl() { return downloadBaseUrl; } + String getDownloadCliBaseUrl() { + return downloadCliBaseUrl; + } + public String getLocalStoragePath() { return localStoragePath; } diff --git a/src/main/java/io/qameta/allure/bamboo/AllureReportServlet.java b/src/main/java/io/qameta/allure/bamboo/AllureReportServlet.java index 3d987a4..f5bfdbc 100644 --- a/src/main/java/io/qameta/allure/bamboo/AllureReportServlet.java +++ b/src/main/java/io/qameta/allure/bamboo/AllureReportServlet.java @@ -14,7 +14,6 @@ import static org.sonatype.aether.util.StringUtils.isEmpty; import javax.inject.Inject; -import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -47,7 +46,7 @@ public static Pattern getUrlPattern() { } @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + protected void doGet(HttpServletRequest request, HttpServletResponse response) { getArtifactUrl(request, response).ifPresent(file -> { try (InputStream inputStream = new URL(file).openStream()) { setResponseHeaders(response, file); @@ -59,7 +58,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t } @Override - protected void doHead(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + protected void doHead(HttpServletRequest request, HttpServletResponse response) { getArtifactUrl(request, response).ifPresent(file -> { try (InputStream inputStream = new URL(file).openStream()) { setResponseHeaders(response, file); diff --git a/src/main/java/io/qameta/allure/bamboo/AllureReportTask.java b/src/main/java/io/qameta/allure/bamboo/AllureReportTask.java index 46022d0..38058e0 100644 --- a/src/main/java/io/qameta/allure/bamboo/AllureReportTask.java +++ b/src/main/java/io/qameta/allure/bamboo/AllureReportTask.java @@ -20,8 +20,7 @@ public AllureReportTask() { @Override public TaskResult execute(@NotNull TaskContext taskContext) { final BuildLogger buildLogger = taskContext.getBuildLogger(); - buildLogger.addBuildLogHeader("Allure Report", true); - buildLogger.addBuildLogEntry("This allure report task is now a sham. It does nothing, so please use" + + buildLogger.addBuildLogEntry("Allure Report Task: This allure report task is now a sham. It does nothing, so please use" + " the suggested way of configuration as listed in the Allure docs! "); return TaskResultBuilder.newBuilder(taskContext).success().build(); } diff --git a/src/main/java/io/qameta/allure/bamboo/AllureSettingsManager.java b/src/main/java/io/qameta/allure/bamboo/AllureSettingsManager.java index 6ef125c..cd6d223 100644 --- a/src/main/java/io/qameta/allure/bamboo/AllureSettingsManager.java +++ b/src/main/java/io/qameta/allure/bamboo/AllureSettingsManager.java @@ -3,10 +3,12 @@ import com.atlassian.sal.api.pluginsettings.PluginSettings; import com.atlassian.sal.api.pluginsettings.PluginSettingsFactory; -import static io.qameta.allure.bamboo.AllureConstants.ALLURE_CONFIG_DOWNLOAD_URL; +import static io.qameta.allure.bamboo.AllureConstants.ALLURE_CONFIG_DOWNLOAD_CLI_URL; import static io.qameta.allure.bamboo.AllureConstants.ALLURE_CONFIG_DOWNLOAD_ENABLED; +import static io.qameta.allure.bamboo.AllureConstants.ALLURE_CONFIG_DOWNLOAD_URL; import static io.qameta.allure.bamboo.AllureConstants.ALLURE_CONFIG_ENABLED_BY_DEFAULT; import static io.qameta.allure.bamboo.AllureConstants.ALLURE_CONFIG_LOCAL_STORAGE; +import static io.qameta.allure.bamboo.AllureConstants.ALLURE_CUSTOM_LOGO_ENABLED; public class AllureSettingsManager { private final PluginSettings settings; @@ -17,16 +19,20 @@ public AllureSettingsManager(PluginSettingsFactory settingsFactory) { AllureGlobalConfig getSettings() { final String downloadEnabled = (String) settings.get(ALLURE_CONFIG_DOWNLOAD_ENABLED); + final String customLogoEnabled = (String) settings.get(ALLURE_CUSTOM_LOGO_ENABLED); final String enableByDefault = (String) settings.get(ALLURE_CONFIG_ENABLED_BY_DEFAULT); final String downloadBaseUrl = (String) settings.get(ALLURE_CONFIG_DOWNLOAD_URL); + final String downloadCliBaseUrl = (String) settings.get(ALLURE_CONFIG_DOWNLOAD_CLI_URL); final String localStorage = (String) settings.get(ALLURE_CONFIG_LOCAL_STORAGE); - return new AllureGlobalConfig( downloadEnabled, enableByDefault, downloadBaseUrl, localStorage); + return new AllureGlobalConfig( downloadEnabled, enableByDefault, downloadBaseUrl, localStorage, downloadCliBaseUrl, customLogoEnabled); } void saveSettings(AllureGlobalConfig config) { settings.put(ALLURE_CONFIG_DOWNLOAD_ENABLED, String.valueOf(config.isDownloadEnabled())); - settings.put(ALLURE_CONFIG_ENABLED_BY_DEFAULT, String.valueOf(config.isEnabledByDefault())); + settings.put(ALLURE_CUSTOM_LOGO_ENABLED, String.valueOf(config.isCustomLogoEnabled())); settings.put(ALLURE_CONFIG_DOWNLOAD_URL, String.valueOf(config.getDownloadBaseUrl())); settings.put(ALLURE_CONFIG_LOCAL_STORAGE, String.valueOf(config.getLocalStoragePath())); + settings.put(ALLURE_CONFIG_ENABLED_BY_DEFAULT, String.valueOf(config.isEnabledByDefault())); + } } diff --git a/src/main/java/io/qameta/allure/bamboo/BambooExecutablesManager.java b/src/main/java/io/qameta/allure/bamboo/BambooExecutablesManager.java index 1c851b1..ef1c256 100644 --- a/src/main/java/io/qameta/allure/bamboo/BambooExecutablesManager.java +++ b/src/main/java/io/qameta/allure/bamboo/BambooExecutablesManager.java @@ -11,6 +11,7 @@ import java.util.Collection; import java.util.List; +import java.util.Objects; import java.util.Optional; import static com.atlassian.bamboo.v2.build.agent.capability.CapabilitySetManagerUtils.getSharedCapabilitySet; @@ -76,6 +77,6 @@ private Collection getCapabilityKeys() { } private Optional getCapability(String capabilityKey) { - return ofNullable(capabilitySetManager.getSharedLocalCapabilitySet().getCapability(capabilityKey)); + return ofNullable(Objects.requireNonNull(capabilitySetManager.getSharedLocalCapabilitySet()).getCapability(capabilityKey)); } } diff --git a/src/main/java/io/qameta/allure/bamboo/info/AllurePlugins.java b/src/main/java/io/qameta/allure/bamboo/info/AllurePlugins.java new file mode 100644 index 0000000..2bd7d56 --- /dev/null +++ b/src/main/java/io/qameta/allure/bamboo/info/AllurePlugins.java @@ -0,0 +1,35 @@ +package io.qameta.allure.bamboo.info; + +import java.util.List; + +public class AllurePlugins { + private List plugins; + + public AllurePlugins(List plugins) { + this.plugins = plugins; + } + + public AllurePlugins() { + } + + public boolean isRegistered(String pluginName) { + return plugins.contains(pluginName); + } + + public List getPlugins() { + return plugins; + } + + public boolean registerPlugin(String pluginName) { + if (!this.isRegistered(pluginName)) { + this.plugins.add(pluginName); + return true; + } + return false; + } + + @Override + public String toString() { + return "\nplugins: " + this.plugins; + } +} diff --git a/src/main/java/io/qameta/allure/bamboo/info/allurewidgets/summary/Statistic.java b/src/main/java/io/qameta/allure/bamboo/info/allurewidgets/summary/Statistic.java new file mode 100644 index 0000000..241856b --- /dev/null +++ b/src/main/java/io/qameta/allure/bamboo/info/allurewidgets/summary/Statistic.java @@ -0,0 +1,168 @@ + +package io.qameta.allure.bamboo.info.allurewidgets.summary; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +public class Statistic { + + @SerializedName("failed") + @Expose + private Integer failed; + @SerializedName("broken") + @Expose + private Integer broken; + @SerializedName("skipped") + @Expose + private Integer skipped; + @SerializedName("passed") + @Expose + private Integer passed; + @SerializedName("unknown") + @Expose + private Integer unknown; + @SerializedName("total") + @Expose + private Integer total; + + /** + * No args constructor for use in serialization + * + */ + public Statistic() { + } + + /** + * + * @param broken + * @param total + * @param failed + * @param passed + * @param skipped + * @param unknown + */ + public Statistic(Integer failed, Integer broken, Integer skipped, Integer passed, Integer unknown, Integer total) { + super(); + this.failed = failed; + this.broken = broken; + this.skipped = skipped; + this.passed = passed; + this.unknown = unknown; + this.total = total; + } + + public Integer getFailed() { + return failed; + } + + public void setFailed(Integer failed) { + this.failed = failed; + } + + public Statistic withFailed(Integer failed) { + this.failed = failed; + return this; + } + + public Integer getBroken() { + return broken; + } + + public void setBroken(Integer broken) { + this.broken = broken; + } + + public Statistic withBroken(Integer broken) { + this.broken = broken; + return this; + } + + public Integer getSkipped() { + return skipped; + } + + public void setSkipped(Integer skipped) { + this.skipped = skipped; + } + + public Statistic withSkipped(Integer skipped) { + this.skipped = skipped; + return this; + } + + public Integer getPassed() { + return passed; + } + + public void setPassed(Integer passed) { + this.passed = passed; + } + + public Statistic withPassed(Integer passed) { + this.passed = passed; + return this; + } + + public Integer getUnknown() { + return unknown; + } + + public void setUnknown(Integer unknown) { + this.unknown = unknown; + } + + public Statistic withUnknown(Integer unknown) { + this.unknown = unknown; + return this; + } + + public Integer getTotal() { + return total; + } + + public void setTotal(Integer total) { + this.total = total; + } + + public Statistic withTotal(Integer total) { + this.total = total; + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(Statistic.class.getName()).append('@').append(Integer.toHexString(System.identityHashCode(this))).append('['); + sb.append("failed"); + sb.append('='); + sb.append(((this.failed == null)?"":this.failed)); + sb.append(','); + sb.append("broken"); + sb.append('='); + sb.append(((this.broken == null)?"":this.broken)); + sb.append(','); + sb.append("skipped"); + sb.append('='); + sb.append(((this.skipped == null)?"":this.skipped)); + sb.append(','); + sb.append("passed"); + sb.append('='); + sb.append(((this.passed == null)?"":this.passed)); + sb.append(','); + sb.append("unknown"); + sb.append('='); + sb.append(((this.unknown == null)?"":this.unknown)); + sb.append(','); + sb.append("total"); + sb.append('='); + sb.append(((this.total == null)?"":this.total)); + sb.append(','); + if (sb.charAt((sb.length()- 1)) == ',') { + sb.setCharAt((sb.length()- 1), ']'); + } else { + sb.append(']'); + } + return sb.toString(); + } + +} diff --git a/src/main/java/io/qameta/allure/bamboo/info/allurewidgets/summary/Summary.java b/src/main/java/io/qameta/allure/bamboo/info/allurewidgets/summary/Summary.java new file mode 100644 index 0000000..cfc0ff7 --- /dev/null +++ b/src/main/java/io/qameta/allure/bamboo/info/allurewidgets/summary/Summary.java @@ -0,0 +1,124 @@ + +package io.qameta.allure.bamboo.info.allurewidgets.summary; + +import java.util.List; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +public class Summary { + + @SerializedName("reportName") + @Expose + private String reportName; + @SerializedName("testRuns") + @Expose + private List testRuns = null; + @SerializedName("statistic") + @Expose + private Statistic statistic; + @SerializedName("time") + @Expose + private Time time; + + /** + * No args constructor for use in serialization + */ + public Summary() { + } + + /** + * @param statistic + * @param reportName + * @param time + * @param testRuns + */ + public Summary(String reportName, List testRuns, Statistic statistic, Time time) { + super(); + this.reportName = reportName; + this.testRuns = testRuns; + this.statistic = statistic; + this.time = time; + } + + public String getReportName() { + return reportName; + } + + public void setReportName(String reportName) { + this.reportName = reportName; + } + + public Summary withReportName(String reportName) { + this.reportName = reportName; + return this; + } + + public List getTestRuns() { + return testRuns; + } + + public void setTestRuns(List testRuns) { + this.testRuns = testRuns; + } + + public Summary withTestRuns(List testRuns) { + this.testRuns = testRuns; + return this; + } + + public Statistic getStatistic() { + return statistic; + } + + public void setStatistic(Statistic statistic) { + this.statistic = statistic; + } + + public Summary withStatistic(Statistic statistic) { + this.statistic = statistic; + return this; + } + + public Time getTime() { + return time; + } + + public void setTime(Time time) { + this.time = time; + } + + public Summary withTime(Time time) { + this.time = time; + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(Summary.class.getName()).append('@').append(Integer.toHexString(System.identityHashCode(this))).append('['); + sb.append("reportName"); + sb.append('='); + sb.append(((this.reportName == null) ? "" : this.reportName)); + sb.append(','); + sb.append("testRuns"); + sb.append('='); + sb.append(((this.testRuns == null) ? "" : this.testRuns)); + sb.append(','); + sb.append("statistic"); + sb.append('='); + sb.append(((this.statistic == null) ? "" : this.statistic)); + sb.append(','); + sb.append("time"); + sb.append('='); + sb.append(((this.time == null) ? "" : this.time)); + sb.append(','); + if (sb.charAt((sb.length() - 1)) == ',') { + sb.setCharAt((sb.length() - 1), ']'); + } else { + sb.append(']'); + } + return sb.toString(); + } + +} diff --git a/src/main/java/io/qameta/allure/bamboo/info/allurewidgets/summary/Time.java b/src/main/java/io/qameta/allure/bamboo/info/allurewidgets/summary/Time.java new file mode 100644 index 0000000..c557096 --- /dev/null +++ b/src/main/java/io/qameta/allure/bamboo/info/allurewidgets/summary/Time.java @@ -0,0 +1,166 @@ + +package io.qameta.allure.bamboo.info.allurewidgets.summary; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +public class Time { + + @SerializedName("start") + @Expose + private Long start; + @SerializedName("stop") + @Expose + private Long stop; + @SerializedName("duration") + @Expose + private Integer duration; + @SerializedName("minDuration") + @Expose + private Integer minDuration; + @SerializedName("maxDuration") + @Expose + private Integer maxDuration; + @SerializedName("sumDuration") + @Expose + private Integer sumDuration; + + /** + * No args constructor for use in serialization + */ + public Time() { + } + + /** + * @param duration + * @param sumDuration + * @param minDuration + * @param stop + * @param start + * @param maxDuration + */ + public Time(Long start, Long stop, Integer duration, Integer minDuration, Integer maxDuration, Integer sumDuration) { + super(); + this.start = start; + this.stop = stop; + this.duration = duration; + this.minDuration = minDuration; + this.maxDuration = maxDuration; + this.sumDuration = sumDuration; + } + + public Long getStart() { + return start; + } + + public void setStart(Long start) { + this.start = start; + } + + public Time withStart(Long start) { + this.start = start; + return this; + } + + public Long getStop() { + return stop; + } + + public void setStop(Long stop) { + this.stop = stop; + } + + public Time withStop(Long stop) { + this.stop = stop; + return this; + } + + public Integer getDuration() { + return duration; + } + + public void setDuration(Integer duration) { + this.duration = duration; + } + + public Time withDuration(Integer duration) { + this.duration = duration; + return this; + } + + public Integer getMinDuration() { + return minDuration; + } + + public void setMinDuration(Integer minDuration) { + this.minDuration = minDuration; + } + + public Time withMinDuration(Integer minDuration) { + this.minDuration = minDuration; + return this; + } + + public Integer getMaxDuration() { + return maxDuration; + } + + public void setMaxDuration(Integer maxDuration) { + this.maxDuration = maxDuration; + } + + public Time withMaxDuration(Integer maxDuration) { + this.maxDuration = maxDuration; + return this; + } + + public Integer getSumDuration() { + return sumDuration; + } + + public void setSumDuration(Integer sumDuration) { + this.sumDuration = sumDuration; + } + + public Time withSumDuration(Integer sumDuration) { + this.sumDuration = sumDuration; + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(Time.class.getName()).append('@').append(Integer.toHexString(System.identityHashCode(this))).append('['); + sb.append("start"); + sb.append('='); + sb.append(((this.start == null) ? "" : this.start)); + sb.append(','); + sb.append("stop"); + sb.append('='); + sb.append(((this.stop == null) ? "" : this.stop)); + sb.append(','); + sb.append("duration"); + sb.append('='); + sb.append(((this.duration == null) ? "" : this.duration)); + sb.append(','); + sb.append("minDuration"); + sb.append('='); + sb.append(((this.minDuration == null) ? "" : this.minDuration)); + sb.append(','); + sb.append("maxDuration"); + sb.append('='); + sb.append(((this.maxDuration == null) ? "" : this.maxDuration)); + sb.append(','); + sb.append("sumDuration"); + sb.append('='); + sb.append(((this.sumDuration == null) ? "" : this.sumDuration)); + sb.append(','); + if (sb.charAt((sb.length() - 1)) == ',') { + sb.setCharAt((sb.length() - 1), ']'); + } else { + sb.append(']'); + } + return sb.toString(); + } + +} diff --git a/src/main/java/io/qameta/allure/bamboo/util/Downloader.java b/src/main/java/io/qameta/allure/bamboo/util/Downloader.java new file mode 100644 index 0000000..646189f --- /dev/null +++ b/src/main/java/io/qameta/allure/bamboo/util/Downloader.java @@ -0,0 +1,33 @@ +package io.qameta.allure.bamboo.util; + +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.Optional; + +import static java.lang.Integer.getInteger; +import static java.util.concurrent.TimeUnit.SECONDS; + +public class Downloader { + private static final int CONN_TIMEOUT_MS = (int) SECONDS.toMillis(getInteger("allure.download.conn.timeout.sec", 20)); + private static final int DOWNLOAD_TIMEOUT_MS = (int) SECONDS.toMillis(getInteger("allure.download.timeout.sec", 120)); + + public static Optional download(URL url, Path target) throws IOException { + final URLConnection connection = url.openConnection(); + connection.setConnectTimeout(CONN_TIMEOUT_MS); + connection.setReadTimeout(DOWNLOAD_TIMEOUT_MS); + connection.setRequestProperty("Connection", "close"); + connection.setRequestProperty("Pragma", "no-cache"); + ((HttpURLConnection) connection).setInstanceFollowRedirects(true); + connection.connect(); + try (InputStream input = connection.getInputStream()) { + Files.copy(input, target, StandardCopyOption.REPLACE_EXISTING); + return Optional.of(target); + } + } +} diff --git a/src/main/java/io/qameta/allure/bamboo/util/FileStringReplacer.java b/src/main/java/io/qameta/allure/bamboo/util/FileStringReplacer.java new file mode 100644 index 0000000..24fb631 --- /dev/null +++ b/src/main/java/io/qameta/allure/bamboo/util/FileStringReplacer.java @@ -0,0 +1,25 @@ +package io.qameta.allure.bamboo.util; + +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.regex.Pattern; + +public class FileStringReplacer { + + public static void replaceInFile(Path filePath, String oldString, String newString) throws IOException { + String content = new String(Files.readAllBytes(filePath), StandardCharsets.UTF_8); + content = content.replaceAll(oldString, newString); + Files.write(filePath, content.getBytes(StandardCharsets.UTF_8)); + } + + public static void replaceInFile(Path filePath, @NotNull Pattern pattern, String newString) throws IOException { + String content = new String(Files.readAllBytes(filePath), StandardCharsets.UTF_8); + content = pattern.matcher(content).replaceAll(newString); + Files.write(filePath, content.getBytes(StandardCharsets.UTF_8)); + } + +} diff --git a/src/main/java/io/qameta/allure/bamboo/util/ZipUtil.java b/src/main/java/io/qameta/allure/bamboo/util/ZipUtil.java new file mode 100644 index 0000000..9f36e58 --- /dev/null +++ b/src/main/java/io/qameta/allure/bamboo/util/ZipUtil.java @@ -0,0 +1,57 @@ +package io.qameta.allure.bamboo.util; + +import net.lingala.zip4j.ZipFile; +import org.apache.commons.compress.archivers.ArchiveEntry; +import org.apache.commons.compress.archivers.ArchiveException; +import org.apache.commons.compress.archivers.ArchiveInputStream; +import org.apache.commons.compress.archivers.ArchiveStreamFactory; +import org.apache.commons.compress.utils.IOUtils; +import org.jetbrains.annotations.NotNull; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import static java.nio.file.Files.createTempDirectory; +import static java.nio.file.Files.move; + +public class ZipUtil { + + public static void unzip(@NotNull Path zipFilePath, String outputDir) throws IOException, ArchiveException { + + final ArchiveStreamFactory asf = new ArchiveStreamFactory(); + + InputStream zipStream = Files.newInputStream(zipFilePath); + ArchiveInputStream ais = asf.createArchiveInputStream(ArchiveStreamFactory.ZIP, zipStream); + + for (ArchiveEntry entry; (entry = ais.getNextEntry()) != null; ) { + Path entryPath = Paths.get(outputDir, entry.getName()); + File entryFile = entryPath.toFile(); + + if (!entry.isDirectory()) { + File parentEntryFile = entryFile.getParentFile(); + if (parentEntryFile.isDirectory() && !(parentEntryFile.mkdirs() || parentEntryFile.exists())) { + throw new IOException("The directory: " + parentEntryFile.getPath() + " couldn't be created successfully"); + } + + try (OutputStream outputStream = Files.newOutputStream(entryPath)) { + IOUtils.copy(ais, outputStream); + } + } else if (!(entryFile.mkdirs() || entryFile.exists())) { + throw new IOException("The directory: " + entryFile.getPath() + " couldn't be created successfully"); + } + } + } + + public static void zipFolder(@NotNull Path srcFolder, @NotNull Path targetDir) throws IOException { + Path zipReportTmpDir = createTempDirectory("tmp_allure_report"); + Path zipReport = zipReportTmpDir.resolve("report.zip"); + try (ZipFile zp = new ZipFile(zipReport.toFile())) { + zp.addFolder(srcFolder.toFile()); + } + move(zipReport, targetDir, StandardCopyOption.REPLACE_EXISTING); + } +} diff --git a/src/main/resources/allure-bamboo.properties b/src/main/resources/allure-bamboo.properties index 1b48585..ef3355b 100644 --- a/src/main/resources/allure-bamboo.properties +++ b/src/main/resources/allure-bamboo.properties @@ -23,7 +23,7 @@ allure.task.help.title = Go to Allure website! error.property.empty = The property value should not be empty error.path.absolute = The path should be relative -error.address.placeholder = The pattern should contains exactly one placeholder <%s> +error.address.placeholder = The pattern should contain exactly one placeholder <%s> webitems.system.admin.build.allureReport=Allure Report admin.allureReportConfig.edit.title=Configure Allure Reporting @@ -41,4 +41,7 @@ allure.config.download.url.error.required=Allure binary base url is required allure.config.local.storage.label=Allure local storage allure.config.local.storage.label.required=Allure local storage is required build.allure.title=Allure Report -buildResult.allure.title=Allure Report \ No newline at end of file +buildResult.allure.title=Allure Report + +allure.config.logo.enabled.label=Enable report custom logo +custom.allure.logo.url.label=Custom logo URL (svg) \ No newline at end of file diff --git a/src/main/resources/atlassian-plugin.xml b/src/main/resources/atlassian-plugin.xml index ec1a924..be1303c 100644 --- a/src/main/resources/atlassian-plugin.xml +++ b/src/main/resources/atlassian-plugin.xml @@ -3,7 +3,7 @@ ${project.description} ${project.version} - images/icon.png + images/logo.png images/logo.png 1 true @@ -17,8 +17,11 @@ - WARNING: this is deprecated way of building Allure Report, please refer the official guide to get more info - + + If you enable the allure report generation in the "Other" tab, this task is automatically executed without + adding it to your pipeline. However, you can still add it to your pipeline if you want to see it. + + diff --git a/src/main/resources/templates/editAllureBambooConfiguration.ftl b/src/main/resources/templates/editAllureBambooConfiguration.ftl index 6d094c3..a4bbe48 100644 --- a/src/main/resources/templates/editAllureBambooConfiguration.ftl +++ b/src/main/resources/templates/editAllureBambooConfiguration.ftl @@ -9,4 +9,6 @@ [@ww.textfield labelKey="custom.allure.artifact.name.label" name="custom.allure.artifact.name" required="false"/] + [@ww.textfield labelKey="custom.allure.logo.url.label" name="custom.allure.logo.url" required="false"/] + [/@ui.bambooSection] \ No newline at end of file diff --git a/src/main/resources/templates/editAllureReportConfig.ftl b/src/main/resources/templates/editAllureReportConfig.ftl index 8091c44..f93e505 100644 --- a/src/main/resources/templates/editAllureReportConfig.ftl +++ b/src/main/resources/templates/editAllureReportConfig.ftl @@ -20,6 +20,8 @@ ] [@ww.checkbox labelKey='allure.config.download.enabled.label' name='custom.allure.config.download.enabled' toggle='true' /] + [@ww.checkbox labelKey='allure.config.logo.enabled.label' name='custom.allure.config.logo.enabled' toggle='true' /] + [@ww.checkbox labelKey='allure.config.enabled.default.label' name='custom.allure.config.enabled.default' toggle='true' /] [@ww.textfield labelKey="allure.config.download.url.label" name="custom.allure.config.download.url" required="true"/] diff --git a/src/main/resources/templates/viewAllureReport.ftl b/src/main/resources/templates/viewAllureReport.ftl index 37d10c7..281b6ea 100644 --- a/src/main/resources/templates/viewAllureReport.ftl +++ b/src/main/resources/templates/viewAllureReport.ftl @@ -2,6 +2,7 @@ [#assign reportUrl = "${baseUrl}/plugins/servlet/allure/report/${planKey}/${buildNumber}/"] +[#assign reportZipUrl = "${baseUrl}/plugins/servlet/allure/report/${planKey}/${buildNumber}/report.zip"] [@ui.header pageKey='buildResult.changes.title' object='${immutablePlan.name} ${resultsSummary.buildNumber}' title=true/] -Expand +Expand  +Download  \ No newline at end of file diff --git a/src/test/java/io/qameta/allure/bamboo/AllureCommandLineSupportTest.java b/src/test/java/io/qameta/allure/bamboo/AllureCommandLineSupportTest.java index 8a90b49..f9128d5 100644 --- a/src/test/java/io/qameta/allure/bamboo/AllureCommandLineSupportTest.java +++ b/src/test/java/io/qameta/allure/bamboo/AllureCommandLineSupportTest.java @@ -2,12 +2,13 @@ import org.junit.Test; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; -import static org.junit.Assert.assertThat; + public class AllureCommandLineSupportTest { - private AllureCommandLineSupport support = new AllureCommandLineSupport(); + private final AllureCommandLineSupport support = new AllureCommandLineSupport(); @Test public void itShouldReturnNotContainingTestcasesResult() throws Exception { diff --git a/src/test/java/io/qameta/allure/bamboo/AllureDownloaderTest.java b/src/test/java/io/qameta/allure/bamboo/AllureDownloaderTest.java index c2e1a17..c8a76ec 100644 --- a/src/test/java/io/qameta/allure/bamboo/AllureDownloaderTest.java +++ b/src/test/java/io/qameta/allure/bamboo/AllureDownloaderTest.java @@ -8,11 +8,14 @@ import org.mockito.Mock; import org.mockito.junit.MockitoRule; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Paths; import static org.apache.commons.io.FileUtils.deleteQuietly; -import static org.apache.commons.io.FileUtils.getTempDirectoryPath; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.Mockito.when; import static org.mockito.junit.MockitoJUnit.rule; @@ -29,7 +32,7 @@ public class AllureDownloaderTest { @Before public void setUp() { - homeDir = Paths.get(getTempDirectoryPath(), "allure-home").toString(); + homeDir = Paths.get(System.getProperty("java.io.tmpdir"), "allure-home").toString(); settings = new AllureGlobalConfig(); when(settingsManager.getSettings()).thenReturn(settings); } @@ -37,7 +40,8 @@ public void setUp() { @Test public void itShouldDownloadAndExtractAllureRelease() { downloader.downloadAndExtractAllureTo(homeDir, "2.17.2"); - assertTrue(Paths.get(homeDir, "bin", "allure").toFile().exists()); + File f = Paths.get(homeDir, "bin", "allure").toFile(); + assertTrue(f.exists()); } @After diff --git a/src/test/java/io/qameta/allure/bamboo/AllureExecutableProviderTest.java b/src/test/java/io/qameta/allure/bamboo/AllureExecutableProviderTest.java index a05fca8..87891fe 100644 --- a/src/test/java/io/qameta/allure/bamboo/AllureExecutableProviderTest.java +++ b/src/test/java/io/qameta/allure/bamboo/AllureExecutableProviderTest.java @@ -14,13 +14,12 @@ import static io.qameta.allure.bamboo.AllureExecutableProvider.DEFAULT_VERSION; import static io.qameta.allure.bamboo.AllureExecutableProvider.getAllureSubDir; import static org.hamcrest.CoreMatchers.equalTo; -import static org.junit.Assert.assertThat; -import static org.mockito.Matchers.anyString; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.junit.MockitoJUnit.rule; -@SuppressWarnings("OptionalGetWithoutIsPresent") public class AllureExecutableProviderTest { private final String homeDir = "/home/allure"; diff --git a/src/test/java/io/qameta/allure/bamboo/AllureExecutableTest.java b/src/test/java/io/qameta/allure/bamboo/AllureExecutableTest.java index 263749b..df0b684 100644 --- a/src/test/java/io/qameta/allure/bamboo/AllureExecutableTest.java +++ b/src/test/java/io/qameta/allure/bamboo/AllureExecutableTest.java @@ -11,7 +11,7 @@ import static com.google.common.io.Files.createTempDir; import static java.util.Collections.singleton; -import static org.mockito.Matchers.anyString; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.junit.MockitoJUnit.rule; @@ -20,7 +20,7 @@ public class AllureExecutableTest { @Rule public MockitoRule mockitoRule = rule(); - private Path path = Paths.get("/tmp/where-allure/installed"); + private final Path path = Paths.get("/tmp/where-allure/installed"); @Mock private AllureCommandLineSupport cmdLine; @@ -30,6 +30,7 @@ public class AllureExecutableTest { private Path toDir; @Before + @SuppressWarnings("UnstableApiUsage") public void setUp() throws Exception { executable = new AllureExecutable(path, cmdLine); fromDir = createTempDir().toPath(); diff --git a/src/test/java/io/qameta/allure/bamboo/util/ExceptionUtilTest.java b/src/test/java/io/qameta/allure/bamboo/util/ExceptionUtilTest.java index 903101b..ff81e25 100644 --- a/src/test/java/io/qameta/allure/bamboo/util/ExceptionUtilTest.java +++ b/src/test/java/io/qameta/allure/bamboo/util/ExceptionUtilTest.java @@ -3,13 +3,13 @@ import org.junit.Test; import static io.qameta.allure.bamboo.util.ExceptionUtil.stackTraceToString; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.assertThat; public class ExceptionUtilTest { @Test - public void itShouldPrintStackTraceIntoString() throws Exception { + public void itShouldPrintStackTraceIntoString() { try { throw new RuntimeException("Print me please"); } catch (RuntimeException e) {