blob: e32b2470ddfcd320779d581e95d5a49d86408203 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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 org.netbeans.nbbuild;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
* Build JUnit report from Apache Ant Report XXX
*
* @author skygo
*/
public class RatReportTask extends Task {
private File sourceFile;
private File root;
// not nice but to be last
private static final String OTHERS_AREA = "not cluster";
private static final String NOT_CLUSTER = "not under cluster";
private File reportFile;
/**
* source file Apache Rat xml report
*
* @param sourceFile
*/
public void setSource(File sourceFile) {
this.sourceFile = sourceFile;
}
/**
* Folder to put the rat reports in
*
* @param report
*/
public void setReport(File report) {
this.reportFile = report;
}
private boolean haltonfailure;
/** JUnit-format XML result file to generate, rather than halting the build. */
public void setHaltonfailure(boolean haltonfailure) {
this.haltonfailure = haltonfailure;
}
@Override
public void execute() throws BuildException {
root = sourceFile.getParentFile().getParentFile().getParentFile();
File[] modulesFolder = root.listFiles();
String repository = "";
// get repository information from git
//try {
List<String> commandAndArgs = new ArrayList<>();
commandAndArgs.add("git");
commandAndArgs.add("config");
commandAndArgs.add("--get");
commandAndArgs.add("remote.origin.url");
Stream<String> allFailures = Stream.empty();
try {
Process p = new ProcessBuilder(commandAndArgs).directory(root).start();
try (BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
String line;
while ((line = r.readLine()) != null) {
if (line.startsWith("http")) {
repository = line.replace(".git", "");
}
}
} catch (IOException ex) {
throw new BuildException("Cannot evaluate git information", ex);
}
} catch (IOException ex) {
throw new BuildException("git process issue", ex);
}
// map module and report
Map<String, ModuleInfo> moduleRATInfo = new TreeMap<>();
// build map to get cluster and module related from cluster.properties
Set<String> moduleDB = new HashSet<>();
Map<String, Set<String>> modulebycluster = new TreeMap<>();
for (File module : modulesFolder) {
if (module.isDirectory() && !module.isHidden()) {
moduleDB.add(module.getName());
moduleRATInfo.put(module.getName(), new ModuleInfo(module));
}
}
Set<String> clusterList = new TreeSet<>();
for (String key : getProject().getProperties().keySet()) {
if (key.startsWith("nb.cluster.")) {
String simplfiedKey = key.replaceAll("nb.cluster.", "");
simplfiedKey = simplfiedKey.replaceAll(".dir", "");
simplfiedKey = simplfiedKey.replaceAll(".depends", "");
clusterList.add(simplfiedKey);
modulebycluster.put(simplfiedKey, new HashSet<>());
}
}
for (String clusterName : clusterList) {
String property = getProject().getProperty("nb.cluster." + clusterName);
String[] split = property.split(",");
for (String amo : split) {
moduleDB.remove(amo);
modulebycluster.get(clusterName).add(amo);
}
}
modulebycluster.put(OTHERS_AREA, new HashSet<>());
for (String k : moduleDB) {
modulebycluster.get(OTHERS_AREA).add(k);
}
// remaining module sorted in others
modulebycluster.get(OTHERS_AREA).add(NOT_CLUSTER);
clusterList.add(OTHERS_AREA);
moduleRATInfo.put(NOT_CLUSTER, new ModuleInfo(root));
//read XML
try {
DocumentBuilder dBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
InputStream inputstream = new FileInputStream(sourceFile);
InputStreamReader reader = new InputStreamReader(inputstream);
InputSource inputSource = new InputSource(reader);
Document doc = dBuilder.parse(inputSource); // open xml source
XPathFactory xpf = XPathFactory.newInstance();
XPath path = xpf.newXPath();
Element rootElement = doc.getDocumentElement();
doPopulateUnapproved(moduleRATInfo, rootElement, path);
doPopulateApproved(moduleRATInfo, rootElement, path);
} catch (ParserConfigurationException | SAXException | IOException | XPathExpressionException ex) {
throw new BuildException("Cannot parse Rat report ", ex);
}
for (String clusterName : clusterList) {
// create a report file by cluster
File file = new File(reportFile, clusterName + ".xml");
if (!file.exists()) {
try {
reportFile.mkdirs();
file.createNewFile();
} catch (IOException ex) {
throw new BuildException("Impossible to create junit rat report for cluster " + clusterName, ex);
}
}
Map<String, String> pseudoTests = new LinkedHashMap<>();
for (String moduleName : modulebycluster.get(clusterName)) {
ModuleInfo amoduleInfo = moduleRATInfo.get(moduleName);
if (amoduleInfo != null) {
if (!amoduleInfo.getUnapproved().isEmpty()) {
pseudoTests.put(" module " + moduleName + " has " + amoduleInfo.getUnapproved().size() + " unapproved license(s)", "Unapproved license in " + amoduleInfo.getUnapproved().size() + " file(s) " + writeFiles(amoduleInfo.getUnapproved(), repository));
}
if (!amoduleInfo.getInvalidExternal().isEmpty()) {
pseudoTests.put(" module " + moduleName + " has " + amoduleInfo.getInvalidExternal().size() + " suspicious external binaries file(s)", "List of file " + amoduleInfo.getInvalidExternal().size() + " file(s) " + writeFiles(amoduleInfo.getInvalidExternal(), null));
}
} else {
// XXX if a moduleInfo is null folder is not present source base incomplete
}
}
JUnitReportWriter.writeReport(this, "Cluster: " + clusterName, file, pseudoTests);
allFailures = Stream.concat(allFailures, pseudoTests.values().stream().filter(err -> err != null));
}
if (haltonfailure) {
String failuresString = allFailures.collect(Collectors.joining("\n"));
if (!failuresString.isEmpty()) {
throw new BuildException("Failed Rat test(s):\n" + failuresString,
getLocation());
}
}
}
private static String writeFiles(Set<String> listFile, String repo) {
StringBuilder sb = new StringBuilder();
for (String fileentry : listFile) {
sb.append("\n");
if (repo != null) {
sb.append(repo).append("/tree/master/");
}
sb.append(fileentry.replaceAll(" ", "%20"));
}
return sb.toString();
}
private void doPopulateUnapproved(Map<String, ModuleInfo> moduleRATInfo, Element rootElement, XPath path) throws XPathExpressionException {
NodeList evaluate = (NodeList) path.evaluate("descendant::resource[license-approval/@name=\"false\"]", rootElement, XPathConstants.NODESET);
for (int i = 0; i < evaluate.getLength(); i++) {
String resources = relativize(evaluate.item(i).getAttributes().getNamedItem("name").getTextContent());
String moduleName = getModuleName(resources);
if (!moduleRATInfo.containsKey(moduleName)) {
moduleRATInfo.get(NOT_CLUSTER).addUnapproved(resources);
} else {
moduleRATInfo.get(moduleName).addUnapproved(resources);
}
}
}
private void doPopulateApproved(Map<String, ModuleInfo> moduleRATInfo, Element rootElement, XPath path) throws XPathExpressionException {
NodeList evaluate = (NodeList) path.evaluate("descendant::resource[license-approval/@name=\"true\"]", rootElement, XPathConstants.NODESET);
for (int i = 0; i < evaluate.getLength(); i++) {
String resources = relativize(evaluate.item(i).getAttributes().getNamedItem("name").getTextContent());
String moduleName = getModuleName(resources);
if (!moduleRATInfo.containsKey(moduleName)) {
moduleRATInfo.get(NOT_CLUSTER).addApproved(resources);
} else {
moduleRATInfo.get(moduleName).addApproved(resources);
}
}
}
private String relativize(String target) {
Path full = Paths.get(target);
Path rootPath = root.toPath();
return rootPath.relativize(full).toString();
}
private String getModuleName(String resource) {
String moduleName;
if (!resource.contains(File.separator)) {
moduleName = NOT_CLUSTER;
} else {
moduleName = resource.substring(0, resource.indexOf(File.separator));
}
return moduleName;
}
class ModuleInfo {
// approved resource list
// very simple string for now.
private final Set<String> approved = new HashSet<>();
// unapproved resource list
private final Set<String> unapproved = new HashSet<>();
private final Set<String> external = new HashSet<>();
private final File folder;
private ModuleInfo(File moduleFolder) {
this.folder = moduleFolder;
}
private File getFolder() {
return folder;
}
private void addApproved(String resources) {
approved.add(resources);
}
private void addInvalidExternal(String resources) {
external.add(resources);
}
private void addUnapproved(String resources) {
unapproved.add(resources);
}
private Set<String> getUnapproved() {
return unapproved;
}
private Set<String> getInvalidExternal() {
return external;
}
}
}