Skip to content

Commit

Permalink
WINDUP-3721 Add '--exportZipReport' input option (#1629)
Browse files Browse the repository at this point in the history
  • Loading branch information
mrizzi committed Apr 4, 2023
1 parent d619a09 commit 85a33bf
Show file tree
Hide file tree
Showing 7 changed files with 324 additions and 0 deletions.
@@ -0,0 +1,49 @@
package org.jboss.windup.config;

import org.jboss.windup.graph.model.WindupConfigurationModel;
import org.jboss.windup.util.ThemeProvider;

/**
* Indicates output path will be compressed in a ZIP file
*
* @author Marco Rizzi
*/
public class ExportZipOutputOption extends AbstractConfigurationOption {
public static final String NAME = WindupConfigurationModel.EXPORT_ZIP_REPORT;

@Override
public String getDescription() {
return "If set, " + ThemeProvider.getInstance().getTheme().getBrandNameAcronym() + " will create a ZIP file containing the files from the output path.";
}

@Override
public String getName() {
return NAME;
}

@Override
public String getLabel() {
return "Should " + ThemeProvider.getInstance().getTheme().getBrandNameAcronym() + " create a ZIP file with the file in the output path?";
}

@Override
public Class<?> getType() {
return Boolean.class;
}

@Override
public InputType getUIType() {
return InputType.SINGLE;
}

@Override
public boolean isRequired() {
return false;
}

@Override
public ValidationResult validate(Object value) {
return ValidationResult.SUCCESS;
}

}
Expand Up @@ -11,6 +11,7 @@
import org.jboss.forge.furnace.util.Predicate;
import org.jboss.windup.config.AnalyzeKnownLibrariesOption;
import org.jboss.windup.config.ConfigurationOption;
import org.jboss.windup.config.ExportZipOutputOption;
import org.jboss.windup.config.RuleProvider;
import org.jboss.windup.config.furnace.FurnaceHolder;
import org.jboss.windup.exec.WindupProcessor;
Expand Down Expand Up @@ -475,4 +476,13 @@ public boolean isExportingSummary() {
return export == null ? false : export;
}

public WindupConfiguration setExportingZipReport(boolean export) {
setOptionValue(ExportZipOutputOption.NAME, export);
return this;
}
public boolean isExportingZipReport() {
Boolean export = getOptionValue(ExportZipOutputOption.NAME);
return export != null && export;
}

}
Expand Up @@ -12,6 +12,7 @@
import org.jboss.forge.furnace.util.Assert;
import org.jboss.forge.furnace.util.Predicate;
import org.jboss.windup.config.DefaultEvaluationContext;
import org.jboss.windup.config.ExportZipOutputOption;
import org.jboss.windup.config.GraphRewrite;
import org.jboss.windup.config.KeepWorkDirsOption;
import org.jboss.windup.config.LegacyReportsRenderingOption;
Expand Down Expand Up @@ -197,6 +198,7 @@ private WindupConfigurationModel setupWindupConfigurationModel(WindupConfigurati
configurationModel.setExportingCSV(configuration.isExportingCSV());
configurationModel.setExportingSummary(configuration.isExportingSummary());
configurationModel.setKeepWorkDirectories(configuration.getOptionValue(KeepWorkDirsOption.NAME));
configurationModel.setExportingZipReport(configuration.isExportingZipReport());

addUserRulesDirsToConfig(configuration, graphContext, configurationModel);
addUserLabelsDirsToConfig(configuration, graphContext, configurationModel);
Expand Down
Expand Up @@ -30,6 +30,7 @@ public interface WindupConfigurationModel extends WindupVertexFrame {
String SKIP_SOURCE_CODE_REPORTS_RENDERING = "skipSourceCodeReports";
String ANALYZE_KNOWN_LIBRARIES = "analyzeKnownLibraries";
String LEGACY_REPORTS = "legacyReports";
String EXPORT_ZIP_REPORT = "exportZipReport";

/**
* The input path to scan
Expand Down Expand Up @@ -202,4 +203,16 @@ public interface WindupConfigurationModel extends WindupVertexFrame {
*/
@Property(SUMMARY_MODE)
void setExportingSummary(boolean summary);

/**
* Indicates whether or not to export ZIP file
*/
@Property(EXPORT_ZIP_REPORT)
boolean isExportingZipReport();

/**
* Indicates whether or not to export ZIP file
*/
@Property(EXPORT_ZIP_REPORT)
void setExportingZipReport(boolean summary);
}
@@ -0,0 +1,61 @@
package org.jboss.windup.reporting.export;

import java.io.IOException;
import java.util.List;

import org.jboss.windup.config.AbstractRuleProvider;
import org.jboss.windup.config.GraphRewrite;
import org.jboss.windup.config.loader.RuleLoaderContext;
import org.jboss.windup.config.metadata.RuleMetadata;
import org.jboss.windup.config.operation.GraphOperation;
import org.jboss.windup.config.phase.PostFinalizePhase;
import org.jboss.windup.config.query.WindupConfigurationQuery;
import org.jboss.windup.graph.GraphContextFactory;
import org.jboss.windup.graph.model.WindupConfigurationModel;
import org.jboss.windup.graph.model.resource.FileModel;
import org.jboss.windup.graph.service.WindupConfigurationService;
import org.jboss.windup.util.ZipUtil;
import org.jboss.windup.util.exception.WindupException;
import org.ocpsoft.rewrite.config.Configuration;
import org.ocpsoft.rewrite.config.ConfigurationBuilder;
import org.ocpsoft.rewrite.context.EvaluationContext;

/**
* Creates a ZIP file containing all the files in the output path
*
* @author Marco Rizzi
*/
@RuleMetadata(
afterIDs = "DeleteWorkDirsAtTheEndRuleProvider",
description = "Creates a ZIP file containing all the files in the output path."
+ " Use --" + WindupConfigurationModel.EXPORT_ZIP_REPORT + " to enable it.",
phase = PostFinalizePhase.class
)
public class ExportZipReportRuleProvider extends AbstractRuleProvider {
public static final String ZIP_REPORTS_NAME = "reports.zip";
@Override
public Configuration getConfiguration(RuleLoaderContext ruleLoaderContext) {

return ConfigurationBuilder.begin()
.addRule()
.when(WindupConfigurationQuery.hasOption(WindupConfigurationModel.EXPORT_ZIP_REPORT, true).as("discard"))
.perform(
new GraphOperation() {
public void perform(GraphRewrite event, EvaluationContext context) {
final WindupConfigurationModel windupConfiguration = WindupConfigurationService.getConfigurationModel(event.getGraphContext());
try {
final FileModel outputFolderToZip = windupConfiguration.getOutputPath();
ZipUtil.zipFolder(outputFolderToZip.asFile().toPath(), outputFolderToZip.getFilePath(), ZIP_REPORTS_NAME, List.of(GraphContextFactory.DEFAULT_GRAPH_SUBDIRECTORY));
} catch (IOException e) {
throw new WindupException(e);
}
}

public String toString() {
return "Create a ZIP file to collect the reports";
}
}
);
}

}
@@ -0,0 +1,135 @@
package org.jboss.windup.reporting;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import javax.inject.Inject;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.forge.arquillian.AddonDependencies;
import org.jboss.forge.arquillian.AddonDependency;
import org.jboss.forge.arquillian.archive.AddonArchive;
import org.jboss.forge.furnace.util.Predicate;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.windup.config.LegacyReportsRenderingOption;
import org.jboss.windup.config.RuleProvider;
import org.jboss.windup.config.SkipReportsRenderingOption;
import org.jboss.windup.engine.predicates.RuleProviderWithDependenciesPredicate;
import org.jboss.windup.exec.WindupProcessor;
import org.jboss.windup.exec.configuration.WindupConfiguration;
import org.jboss.windup.graph.GraphContext;
import org.jboss.windup.graph.GraphContextFactory;
import org.jboss.windup.reporting.export.ExportZipReportRuleProvider;
import org.jboss.windup.rules.apps.java.config.ScanPackagesOption;
import org.jboss.windup.rules.apps.java.config.SourceModeOption;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(Arquillian.class)
public class ZipExportingTest {

@Deployment
@AddonDependencies({
@AddonDependency(name = "org.jboss.windup.config:windup-config"),
@AddonDependency(name = "org.jboss.windup.graph:windup-graph"),
@AddonDependency(name = "org.jboss.windup.reporting:windup-reporting"),
@AddonDependency(name = "org.jboss.windup.exec:windup-exec"),
@AddonDependency(name = "org.jboss.windup.rules.apps:windup-rules-java"),
@AddonDependency(name = "org.jboss.windup.utils:windup-utils"),
@AddonDependency(name = "org.jboss.forge.furnace.container:cdi")
})
public static AddonArchive getDeployment() {
return ShrinkWrap.create(AddonArchive.class).addBeansXML();
}

@Inject
private GraphContextFactory factory;

@Inject
private WindupProcessor processor;

@Test
public void testExportZipReport() throws Exception {
zipReportTest(true, false, false);
}

@Test
public void testExportZipReportLegacyReports() throws Exception {
zipReportTest(true, true, false);
}

@Test
public void testExportZipReportSkipReports() throws Exception {
zipReportTest(true, false, true);
}

@Test
public void testNotExportZipReport() throws Exception {
zipReportTest(false, false, false);
}

private void zipReportTest(boolean exportZipReports, boolean legacyReports, boolean skipReports) throws Exception {
final Path outputPath = Paths.get(FileUtils.getTempDirectory().toString(),
"windup_" + RandomStringUtils.randomAlphanumeric(6));

outputPath.toFile().mkdirs();
try (GraphContext context = factory.create(true)) {
String inputPath = "src/test/resources";
Predicate<RuleProvider> predicate = new RuleProviderWithDependenciesPredicate(ExportZipReportRuleProvider.class);
WindupConfiguration configuration = new WindupConfiguration()
.setGraphContext(context)
.setRuleProviderFilter(predicate)
.addInputPath(Paths.get(inputPath))
.setOutputDirectory(outputPath)
.setOptionValue(ScanPackagesOption.NAME, Collections.singletonList(""))
.setOptionValue(SourceModeOption.NAME, true);
if (exportZipReports) {
configuration.setExportingZipReport(true);
configuration.setOptionValue(LegacyReportsRenderingOption.NAME, legacyReports);
if (skipReports) configuration.setOptionValue(SkipReportsRenderingOption.NAME, true);
}
processor.execute(configuration);
final File reportsZipFile = new File(outputPath + "/reports.zip");
Assert.assertEquals(exportZipReports, reportsZipFile.exists());
if (exportZipReports) {
try (ZipFile reportsZip = new ZipFile(reportsZipFile);
Stream<Path> outputFiles = Files.walk(outputPath.toAbsolutePath())){
final Set<String> zipContent = reportsZip.stream()
.map(ZipEntry::getName)
.sorted()
.collect(Collectors.toCollection(LinkedHashSet::new));
Assert.assertTrue(
"Zip file content is different from the output folder content",
outputFiles
// reports.zip file itself and root folder are not part of the ZIP file
.filter(file -> !file.endsWith("reports.zip") && !outputPath.equals(file))
.map(path -> {
final Path relativePath = outputPath.relativize(path);
// in the ZipFile, the directories end with "/" but they don't in the Files.walk
if (Files.isDirectory(path)) {
return relativePath + File.separator;
}
else {
return relativePath.toString();
}
})
// check every file in the output path is contained in the ZIP file
.allMatch(zipContent::contains));
}
}
}
}
}
54 changes: 54 additions & 0 deletions utils/src/main/java/org/jboss/windup/util/ZipUtil.java
Expand Up @@ -5,12 +5,24 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.file.CopyOption;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
Expand All @@ -32,6 +44,12 @@ public class ZipUtil {

private static Set<String> supportedExtensions;

private static final CopyOption[] COPY_OPTIONS = {
StandardCopyOption.REPLACE_EXISTING,
StandardCopyOption.COPY_ATTRIBUTES,
LinkOption.NOFOLLOW_LINKS
};

/**
* Unzip a classpath resource using the given {@link Class} as the resource root path.
*/
Expand Down Expand Up @@ -166,4 +184,40 @@ private static ZipEntry getNextEntry(ZipInputStream zis) throws IOException {
}
}

public static void zipFolder(Path source, String zipOutputPath, String zipOutputName, List<String> pathPrefixesToExclude) throws IOException {

final URI uri = URI.create("jar:file:" + zipOutputPath + File.separator + zipOutputName);

Files.walkFileTree(source, new SimpleFileVisitor<>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attributes) {
final Path targetFile = source.relativize(file);
if (attributes.isSymbolicLink() || file.endsWith(zipOutputName) || pathPrefixesToExclude.stream().anyMatch(targetFile::startsWith)) {
return FileVisitResult.CONTINUE;
}

final Map<String, String> env = new HashMap<>();
env.put("create", "true");

try (final FileSystem zip = FileSystems.newFileSystem(uri, env)) {
final Path pathInZipfile = zip.getPath(targetFile.toString());
if (pathInZipfile.getParent() != null) {
Files.createDirectories(pathInZipfile.getParent());
}
Files.copy(file, pathInZipfile, COPY_OPTIONS);
} catch (IOException e) {
throw new WindupException(e);
}
return FileVisitResult.CONTINUE;
}

@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) {
final String message = String.format("Unable to zip : %s%n%s%n", file, exc);
log.warning(message);
System.err.println(message);
return FileVisitResult.CONTINUE;
}
});
}
}

0 comments on commit 85a33bf

Please sign in to comment.