/******************************************************************************* * Copyright (c) 2017 Synopsys, Inc * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Synopsys, Inc - initial implementation and documentation *******************************************************************************/ package jenkins.plugins.coverity.ws; import java.io.IOException; import java.io.PrintStream; import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; import com.coverity.ws.v9.CovRemoteServiceException_Exception; import com.coverity.ws.v9.DefectService; import com.coverity.ws.v9.MergedDefectDataObj; import com.coverity.ws.v9.MergedDefectFilterSpecDataObj; import com.coverity.ws.v9.MergedDefectsPageDataObj; import com.coverity.ws.v9.PageSpecDataObj; import com.coverity.ws.v9.SnapshotScopeSpecDataObj; import com.coverity.ws.v9.StreamIdDataObj; import hudson.Launcher; import hudson.Util; import hudson.model.AbstractBuild; import hudson.model.BuildListener; import hudson.model.Hudson; import hudson.model.Result; import hudson.model.TaskListener; import jenkins.model.Jenkins; import jenkins.plugins.coverity.CIMInstance; import jenkins.plugins.coverity.CIMStream; import jenkins.plugins.coverity.CoverityBuildAction; import jenkins.plugins.coverity.CoverityDefect; import jenkins.plugins.coverity.CoverityPublisher; import jenkins.plugins.coverity.DefectFilters; import org.apache.commons.lang.StringUtils; /** * Class responsible for reading defects from Coverity after the commit process has been completed. The defects * will be added as a {@link CoverityBuildAction} to the build. */ public class DefectReader { private AbstractBuild<?, ?> build; private BuildListener listener; private CoverityPublisher publisher; public DefectReader(AbstractBuild<?, ?> build, BuildListener listener, CoverityPublisher publisher) { this.build = build; this.listener = listener; this.publisher = publisher; } public void getLatestDefectsForBuild() { if (publisher.isSkipFetchingDefects()) { // never get any defects when configured to skip fetching defects return; } CIMStream cimStream = publisher.getCimStream(); CIMInstance cimInstance = publisher.getDescriptor().getInstance(cimStream.getInstance()); if (StringUtils.isEmpty(cimStream.getStream())) { listener.getLogger().println("[Coverity] Stream has not been configured. Skipping fetching defects."); return; } listener.getLogger().println(MessageFormat.format("[Coverity] Fetching defects for stream \"{0}\"", cimStream.getStream())); List<MergedDefectDataObj> defects = null; try { defects = getDefectsForSnapshot(cimInstance, cimStream, listener.getLogger()); List<CoverityDefect> matchingDefects = new ArrayList<>(); // Loop through all defects create defect objects for(MergedDefectDataObj defect : defects) { matchingDefects.add(new CoverityDefect(defect.getCid(), defect.getCheckerName(), defect.getFunctionDisplayName(), defect.getFilePathname())); } if(!matchingDefects.isEmpty()) { listener.getLogger().println(MessageFormat.format("[Coverity] Found {0} defects matching all filters", matchingDefects.size())); if(publisher.isFailBuild()) { if(build.getResult().isBetterThan(Result.FAILURE)) { build.setResult(Result.FAILURE); } } // if the user wants to mark the build as unstable when defects are found, then we // notify the publisher to do so. if(publisher.isUnstable()){ publisher.setUnstableBuild(true); } } else { listener.getLogger().println("[Coverity] No defects matched all filters."); } CoverityBuildAction action = new CoverityBuildAction(build, cimStream.getProject(), cimStream.getStream(), cimStream.getInstance(), matchingDefects); build.addAction(action); String rootUrl = Jenkins.getInstance().getRootUrl(); if(rootUrl != null) { listener.getLogger().println("Coverity details: " + rootUrl + build.getUrl() + action.getUrlName()); } } catch (IOException e) { e.printStackTrace(listener.error("[Coverity] An error occurred while fetching defects")); build.setResult(Result.FAILURE); } catch (CovRemoteServiceException_Exception e) { e.printStackTrace(listener.error("[Coverity] An error occurred while fetching defects")); build.setResult(Result.FAILURE); } } private List<MergedDefectDataObj> getDefectsForSnapshot(CIMInstance cim, CIMStream cimStream, PrintStream logger) throws IOException, CovRemoteServiceException_Exception { List<MergedDefectDataObj> mergeList = new ArrayList<MergedDefectDataObj>(); DefectService ds = cim.getDefectService(); StreamIdDataObj streamId = new StreamIdDataObj(); streamId.setName(cimStream.getStream()); List<StreamIdDataObj> streamIds = new ArrayList<StreamIdDataObj>(); streamIds.add(streamId); DefectFilters defectFilters = cimStream.getDefectFilters(); MergedDefectFilterSpecDataObj filter = defectFilters != null ? defectFilters.ToFilterSpecDataObj() : new MergedDefectFilterSpecDataObj(); PageSpecDataObj pageSpec = new PageSpecDataObj(); SnapshotScopeSpecDataObj snapshotScope = new SnapshotScopeSpecDataObj(); snapshotScope.setShowSelector("last()"); // The loop will pull up to the maximum amount of defect, doing per page size int pageSize = 1000; // Size of page to be pulled int defectSize = 3000; // Maximum amount of defect to pull for(int pageStart = 0; pageStart < defectSize; pageStart += pageSize){ if (pageStart >= pageSize) logger.println(MessageFormat.format("[Coverity] Fetching defects for stream \"{0}\" (fetched {1} of {2})", cimStream.getStream(), pageStart, defectSize)); pageSpec.setPageSize(pageSize); pageSpec.setStartIndex(pageStart); pageSpec.setSortAscending(true); MergedDefectsPageDataObj mergedDefectsForStreams = ds.getMergedDefectsForStreams(streamIds, filter, pageSpec, snapshotScope); defectSize = mergedDefectsForStreams.getTotalNumberOfRecords(); mergeList.addAll(mergedDefectsForStreams.getMergedDefects()); } return mergeList; } }