All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.marvelution.bamboo.plugins.sonar.Sonar Maven / Gradle / Ivy

There is a newer version: 4.0.0
Show newest version
/*
 * Licensed to Marvelution under one or more contributor license 
 * agreements.  See the NOTICE file distributed with this work 
 * for additional information regarding copyright ownership.
 * Marvelution licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package com.marvelution.bamboo.plugins.sonar;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Writer;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.apache.maven.model.Build;
import org.apache.maven.model.Model;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.PluginExecution;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.WriterFactory;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
import org.jetbrains.annotations.NotNull;

import com.atlassian.bamboo.build.Artifact;
import com.atlassian.bamboo.build.DefaultArtifact;
import com.atlassian.bamboo.builder.Maven2Builder;
import com.atlassian.bamboo.repository.RepositoryException;
import com.atlassian.bamboo.resultsummary.ExtendedBuildResultsSummary;
import com.atlassian.bamboo.v2.build.BuildContext;
import com.atlassian.bamboo.v2.build.BuildPlanDefinition;

/**
 * Helper class for Sonar specific requirements, like:
 * 
    *
  • generating a sonar pom file
  • *
  • generating sonar {@link Maven2Builder} command
  • *
* * @author Mark Rekveld */ public final class Sonar { private static final Logger LOGGER = Logger.getLogger(Sonar.class); private static final Pattern ALTERNATE_POM_PATTERN = Pattern.compile("\\s*(-f|--file)\\s+"); public static final String SONAR_PROJECT_KEY = "custom.sonar.project.key"; public static final String SONAR_BUILD_STATE_KEY = "custom.sonar.build.state"; public static final String SONAR_ARTIFACT_LABEL = "Sonar Analysis Log"; public static final String SONAR_ARTIFACT_COPY_PATTERN = "sonar-log.log"; public static final String SONAR_ARTIFACT_SRC_DIRECTORY = ""; public static final Artifact SONAR_LOG_ARTIFACT = new DefaultArtifact(SONAR_ARTIFACT_LABEL, SONAR_ARTIFACT_COPY_PATTERN, SONAR_ARTIFACT_SRC_DIRECTORY); public static final String SONAR_SERVER = "custom.sonar.server"; public static final String BUILD_PLAN_SPECIFIC = "buildPlanSpecific"; public static final String SONAR_RUN = "custom.sonar.run"; public static final String SONAR_HOST_URL = "custom.sonar.host.url"; public static final String SONAR_HOST_USERNAME = "custom.sonar.host.username"; public static final String SONAR_HOST_PASSWORD = "custom.sonar.host.password"; public static final String SONAR_JDBC_URL = "custom.sonar.jdbc.url"; public static final String SONAR_JDBC_DRIVER = "custom.sonar.jdbc.driver"; public static final String SONAR_JDBC_USERNAME = "custom.sonar.jdbc.username"; public static final String SONAR_JDBC_PASSWORD = "custom.sonar.jdbc.password"; public static final Map SONAR_GOAL_PROPERTIES = new HashMap(); static { SONAR_GOAL_PROPERTIES.put(SONAR_HOST_URL, "http://localhost:9000/"); SONAR_GOAL_PROPERTIES.put(SONAR_JDBC_URL, ""); SONAR_GOAL_PROPERTIES.put(SONAR_JDBC_DRIVER, ""); SONAR_GOAL_PROPERTIES.put(SONAR_JDBC_USERNAME, "sonar"); SONAR_GOAL_PROPERTIES.put(SONAR_JDBC_PASSWORD, "sonar"); } public static final String SONAR_BUILD_JDK = "custom.sonar.build.jdk"; public static final String SONAR_BUILD_JDK_DEFAULT = "Reuse Build Defined JDK"; public static final String SONAR_ADD_ARGS = "custom.sonar.add.args"; public static final String SONAR_REUSE_REPORTS = "custom.sonar.reusereports"; public static final String SONAR_OVERRIDE_GLOBALS = "custom.sonar.override.globals"; public static final String SONAR_SKIP_ON_BUILD_FAILURE = "custom.sonar.skip.build.failure"; public static final String SONAR_SKIP_ON_MANUAL_BUILD = "custom.sonar.skip.manual.build"; public static final String SONAR_SKIP_ON_NO_CODE_CHANGES = "custom.sonar.skip.no.code.changes"; public static final String SONAR_FAILURE_BEHAVIOR = "custom.sonar.failure.behavior"; public static final String SONAR_IGNORE_BEHAVIOR = "ignore"; public static final String SONAR_FAIL_BEHAVIOR = "fail"; public static final String SONAR_LABEL_BEHAVIOR = "label"; public static final String[] SONAR_FAILURE_BEHAVIORS = { SONAR_IGNORE_BEHAVIOR, SONAR_FAIL_BEHAVIOR, SONAR_LABEL_BEHAVIOR }; public static final String SONAR_LIGHT = "custom.sonar.light.run"; public static final String SONAR_LIGHT_GROUPID = "custom.sonar.light.groupId"; public static final String SONAR_LIGHT_ARTIFACTID = "custom.sonar.light.artifactId"; public static final String SONAR_LIGHT_VERSION = "custom.sonar.light.version"; public static final String SONAR_LIGHT_NAME = "custom.sonar.light.name"; public static final String SONAR_LIGHT_DESCRIPTION = "custom.sonar.light.description"; public static final String SONAR_LIGHT_SOURCES = "custom.sonar.light.sources"; public static final String SONAR_LIGHT_TARGET = "custom.sonar.light.target"; public static final String SONAR_LIGHT_JDK = "custom.sonar.light.jdk"; public static final String SONAR_LIGHT_POM = "sonar-pom.xml"; public static final String BUILDER_HAS_TESTS = "testChecked"; public static final String BUILDER_TESTS_PATH = "testResultsDirectory"; public static final String CLOVER_EXISTS = "custom.clover.exists"; public static final String CLOVER_PATH = "custom.clover.path"; public static final String COBERTURA_EXISTS = "custom.cobertura.exists"; public static final String COBERTURA_PATH = "custom.cobertura.path"; /** * Generate the Sonar Light pom.xml and write it to the given repository directory * * @param repositoryDirectory the source repository {@link File} directory * @param customConfiguration the custom configuration of the Build * @throws IOException in case the generated sonar-pom.xml file cannot be written to the repository directory */ public void generateSonarLightPom(@NotNull File repositoryDirectory, @NotNull Map customConfiguration) throws IOException { final Model model = new Model(); model.setModelVersion("4.0.0"); model.setGroupId(customConfiguration.get(SONAR_LIGHT_GROUPID)); model.setArtifactId(customConfiguration.get(SONAR_LIGHT_ARTIFACTID)); if (StringUtils.isNotBlank(customConfiguration.get(SONAR_LIGHT_VERSION))) { model.setVersion(customConfiguration.get(SONAR_LIGHT_VERSION)); } else { model.setVersion("1.0"); } model.setName(customConfiguration.get(SONAR_LIGHT_NAME)); if (StringUtils.isNotBlank(customConfiguration.get(SONAR_LIGHT_DESCRIPTION))) { model.setDescription(customConfiguration.get(SONAR_LIGHT_DESCRIPTION)); } final Properties props = new Properties(); if (Boolean.parseBoolean((String) customConfiguration.get(SONAR_REUSE_REPORTS))) { props.setProperty("sonar.dynamicAnalysis", "reusereports"); } for (Entry entry : customConfiguration.entrySet()) { if (entry.getKey().endsWith(BUILDER_HAS_TESTS) && Boolean.parseBoolean(entry.getValue())) { final String pathKey = entry.getKey().replace(BUILDER_HAS_TESTS, BUILDER_TESTS_PATH); props.setProperty("sonar.surefire.reportPath", customConfiguration.get(pathKey)); break; } } if (Boolean.parseBoolean((String) customConfiguration.get(COBERTURA_EXISTS))) { props.setProperty("sonar.cobertura.reportPath", customConfiguration.get(COBERTURA_PATH)); } if (Boolean.parseBoolean((String) customConfiguration.get(CLOVER_EXISTS))) { props.setProperty("sonar.clover.reportPath", customConfiguration.get(CLOVER_PATH)); } model.setProperties(props); model.setBuild(new Build()); final List sourceDirectories = Arrays.asList(customConfiguration.get(SONAR_LIGHT_SOURCES).split(", ")); model.getBuild().setSourceDirectory(sourceDirectories.get(0)); model.getBuild().setOutputDirectory(customConfiguration.get(SONAR_LIGHT_TARGET)); if (sourceDirectories.size() > 1) { final Plugin sourcesPlugin = new Plugin(); sourcesPlugin.setGroupId("org.codehaus.mojo"); sourcesPlugin.setArtifactId("build-helper-maven-plugin"); sourcesPlugin.setVersion("1.1"); final PluginExecution execution = new PluginExecution(); execution.setId("add-source"); execution.setPhase("generate-sources"); execution.setGoals(Collections.singletonList("add-source")); final Xpp3Dom configuration = new Xpp3Dom("configuration"); final Xpp3Dom sources = new Xpp3Dom("sources"); for (int i = 1; i < sourceDirectories.size(); i++) { final Xpp3Dom source = new Xpp3Dom("source"); source.setValue(sourceDirectories.get(i)); sources.addChild(source); } configuration.addChild(sources); execution.setConfiguration(configuration); sourcesPlugin.getExecutions().add(execution); model.getBuild().getPlugins().add(sourcesPlugin); } String jdkVersion = "1.5"; if (StringUtils.isNotBlank(customConfiguration.get(SONAR_LIGHT_JDK))) { jdkVersion = customConfiguration.get(SONAR_LIGHT_JDK); } final Plugin compiler = new Plugin(); compiler.setArtifactId("maven-compiler-plugin"); final Xpp3Dom configuration = new Xpp3Dom("configuration"); final Xpp3Dom source = new Xpp3Dom("source"); source.setValue(jdkVersion); configuration.addChild(source); final Xpp3Dom target = new Xpp3Dom("target"); target.setValue(jdkVersion); configuration.addChild(target); compiler.setConfiguration(configuration); model.getBuild().getPlugins().add(compiler); final MavenXpp3Writer pomWriter = new MavenXpp3Writer(); Writer fileWriter = null; try { fileWriter = WriterFactory.newXmlWriter(new File(repositoryDirectory, SONAR_LIGHT_POM)); pomWriter.write(fileWriter, model); } finally { IOUtil.close(fileWriter); } } /** * Generate the Sonar goals and arguments for the {@link Maven2Builder} * * @param configuration the {@link Map} containing the custom configuration of the current Build Plan * @return the Sonar goals and arguments for the {@link Maven2Builder} */ @NotNull public String generateSonarGoals(@NotNull Map configuration) { final StringBuilder goalsBuilder = new StringBuilder(); goalsBuilder.append("-B"); if (Boolean.parseBoolean((String) configuration.get(SONAR_LIGHT))) { goalsBuilder.append(" --file " + SONAR_LIGHT_POM); } for (Entry property : SONAR_GOAL_PROPERTIES.entrySet()) { final String propertyName = property.getKey().substring("custom.".length()); if (StringUtils.isNotBlank(configuration.get(property.getKey()))) { goalsBuilder.append(" -D" + propertyName + "=" + configuration.get(property.getKey())); } else if (StringUtils.isNotBlank(property.getValue())) { goalsBuilder.append(" -D" + propertyName + "=" + property.getValue()); } } if (StringUtils.isNotBlank(configuration.get(SONAR_ADD_ARGS))) { goalsBuilder.append(" " + configuration.get(SONAR_ADD_ARGS)); } goalsBuilder.append(" sonar:sonar"); LOGGER.debug("Generated provided configuration to Sonar goals: " + goalsBuilder.toString()); return goalsBuilder.toString(); } /** * Copy the {@link BambooSonarServer} configuration to the given {@link BuildPlanDefinition} * * @param server the {@link BambooSonarServer} configuration to copy * @param buildDefinition the {@link BuildPlanDefinition} to copy the configuration to */ public void copySonarServerToBuildPlanDefinition(BambooSonarServer server, BuildPlanDefinition buildDefinition) { buildDefinition.getCustomConfiguration().put(SONAR_HOST_URL, server.getHost()); buildDefinition.getCustomConfiguration().put(SONAR_JDBC_URL, server.getDatabaseUrl()); buildDefinition.getCustomConfiguration().put(SONAR_JDBC_DRIVER, server.getDatabaseDriver()); buildDefinition.getCustomConfiguration().put(SONAR_JDBC_USERNAME, server.getDatabaseUsername()); buildDefinition.getCustomConfiguration().put(SONAR_JDBC_PASSWORD, server.getDatabasePassword()); String planAddArgs = buildDefinition.getCustomConfiguration().get(SONAR_ADD_ARGS); // Fix for Issue MARVBAMBOOSONAR-15: the server additional arguments should only be added if they are not in // the plan additional arguments if (StringUtils.isBlank(planAddArgs)) { planAddArgs = ""; } if (!planAddArgs.contains(server.getAdditionalArguments())) { buildDefinition.getCustomConfiguration().put(SONAR_ADD_ARGS, (planAddArgs + " " + server.getAdditionalArguments()).trim()); } } /** * Get the Sonar project key from the {@link BuildContext} * * @param buildContext the {@link BuildContext} to get the project key from * @return the Sonar project key */ public String getSonarProjectKeyFromBuildContext(BuildContext buildContext) { final MavenXpp3Reader reader = new MavenXpp3Reader(); String filename = null; if (buildContext.getBuildPlanDefinition().getBuilderV2() instanceof Maven2Builder) { final Maven2Builder builder = (Maven2Builder) buildContext.getBuildPlanDefinition().getBuilderV2(); filename = getPomFileNewFromGoals(builder.getGoal()); } else { filename = SONAR_LIGHT_POM; } FileInputStream fileInput = null; try { if (StringUtils.isBlank(filename)) { filename = "pom.xml"; } fileInput = new FileInputStream(new File(buildContext.getBuildPlanDefinition() .getRepositoryV2().getSourceCodeDirectory(buildContext.getPlanKey()), filename)); final Model model = reader.read(fileInput); return model.getGroupId() + ":" + model.getArtifactId(); } catch (FileNotFoundException e) { LOGGER.error("Could not find POM file: " + filename, e); } catch (IOException e) { LOGGER.error("Failed to read POM file: " + filename, e); } catch (XmlPullParserException e) { LOGGER.error("Failed to parse POM file: " + filename, e); } catch (RepositoryException e) { LOGGER.error("Failed to get the Source repository for the Build Plan", e); } finally { IOUtils.closeQuietly(fileInput); } return ""; } /** * Get the SonarProject key from the {@link com.atlassian.bamboo.build.Build} results data * * @param build the {@link com.atlassian.bamboo.build.Build} to get the Sonar Project key from * @return the Sonar Project key, may be blank */ @SuppressWarnings("unchecked") public String getSonarProjectFromBuildResultsData(com.atlassian.bamboo.build.Build build) { String sonarProject = ""; for (final ExtendedBuildResultsSummary result : (List) build.getBuildResultSummaries()) { if (StringUtils.isNotBlank(result.getCustomBuildData().get(Sonar.SONAR_PROJECT_KEY))) { sonarProject = result.getCustomBuildData().get(Sonar.SONAR_PROJECT_KEY); } } return sonarProject; } /** * Get the POM filename from the goals given * * @param goals the Maven 2 goals to get the POM filename from * @return the POM filename, may be null in which case the default pom.xml is used */ public String getPomFileNewFromGoals(String goals) { if (goals == null) { return null; } final Matcher matcher = ALTERNATE_POM_PATTERN.matcher(goals); if (matcher.find()) { final String[] options = goals.split("\\s"); for (int i = 0; i < options.length; i++) { if ("-f".equals(options[i]) || "--file".equals(options[i])) { return options[i + 1]; } } } return null; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy