first commit

This commit is contained in:
Geert Bevin 2023-04-07 21:22:26 -04:00
commit 95412fbe7e
23 changed files with 946 additions and 0 deletions

View file

@ -0,0 +1,64 @@
package rife.bld.extension;
import rife.bld.Project;
import rife.bld.publish.PublishDeveloper;
import rife.bld.publish.PublishLicense;
import rife.bld.publish.PublishScm;
import java.util.List;
import static rife.bld.dependencies.Repository.*;
import static rife.bld.dependencies.Scope.*;
import static rife.bld.operations.JavadocOptions.DocLinkOption.NO_MISSING;
public class ArchiveBuild extends Project {
public ArchiveBuild() {
pkg = "rife.bld.extension";
name = "Archive";
version = version(0,1,0,"SNAPSHOT");
archiveBaseName = "bld-archive";
javaRelease = 17;
downloadSources = true;
autoDownloadPurge = true;
repositories = List.of(MAVEN_CENTRAL, RIFE2_SNAPSHOTS, RIFE2_RELEASES);
scope(compile)
.include(dependency("com.uwyn.rife2", "rife2", version(1,5,18,"SNAPSHOT")))
.include(dependency("org.apache.commons", "commons-compress", version(1,23,0)));
scope(test)
.include(dependency("org.junit.jupiter", "junit-jupiter", version(5,9,2)))
.include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1,9,2)));
javadocOperation()
.javadocOptions()
.docLint(NO_MISSING)
.link("https://rife2.github.io/rife2/");
publishOperation()
.repository(version.isSnapshot() ? repository("rife2-snapshots") : repository("rife2-releases"))
.info()
.groupId("com.uwyn.rife2")
.artifactId("bld-archive")
.description("bld extension for creating archives")
.url("https://github.com/rife2/bld-archive")
.developer(new PublishDeveloper()
.id("gbevin")
.name("Geert Bevin")
.email("gbevin@uwyn.com")
.url("https://github.com/gbevin"))
.license(new PublishLicense()
.name("The Apache License, Version 2.0")
.url("https://www.apache.org/licenses/LICENSE-2.0.txt"))
.scm(new PublishScm()
.connection("scm:git:https://github.com/rife2/bld-archive.git")
.developerConnection("scm:git:git@github.com:rife2/bld-archive.git")
.url("https://github.com/rife2/bld-archive"))
.signKey(property("sign.key"))
.signPassphrase(property("sign.passphrase"));
}
public static void main(String[] args) {
new ArchiveBuild().start(args);
}
}

View file

@ -0,0 +1,374 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.extension;
import org.apache.commons.compress.archivers.zip.UnixStat;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.utils.IOUtils;
import rife.bld.NamedFile;
import rife.bld.operations.AbstractOperation;
import rife.tools.FileUtils;
import rife.tools.StringUtils;
import java.io.*;
import java.nio.file.Files;
import java.util.*;
import java.util.regex.Pattern;
/**
* Creates a zip archive of the provided sources and directories.
*
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @since 1.0
*/
public class ZipOperation extends AbstractOperation<ZipOperation> {
private final List<File> sourceDirectories_ = new ArrayList<>();
private final List<NamedFile> sourceFiles_ = new ArrayList<>();
private File destinationDirectory_;
private String destinationFileName_;
private final List<Pattern> included_ = new ArrayList<>();
private final List<Pattern> excluded_ = new ArrayList<>();
/**
* Performs the zip operation.
*
* @throws IOException when an exception occurred during the zip creation process
* @since 1.0
*/
public void execute()
throws IOException {
executeCreateDestinationDirectory();
executeCreateZipArchive();
if (!silent()) {
System.out.println("The zip archive was created at '" + destinationFile() + "'");
}
}
/**
* Part of the {@link #execute} operation, create the destination directory.
*
* @since 1.0
*/
protected void executeCreateDestinationDirectory() {
destinationDirectory().mkdirs();
}
/**
* Part of the {@link #execute} operation, create the zip archive.
*
* @since 1.0
*/
protected void executeCreateZipArchive()
throws IOException {
var out_file = new File(destinationDirectory(), destinationFileName());
try (ZipArchiveOutputStream zip = new ZipArchiveOutputStream(out_file)) {
for (var source_dir : sourceDirectories()) {
for (var file_name : FileUtils.getFileList(source_dir)) {
var file = new File(source_dir, file_name);
if (StringUtils.filter(file.getAbsolutePath(), included(), excluded(), false)) {
executeAddFileToZip(zip, new NamedFile(file_name, file));
}
}
}
for (var source_file : sourceFiles()) {
if (StringUtils.filter(source_file.file().getAbsolutePath(), included(), excluded(), false)) {
executeAddFileToZip(zip, source_file);
}
}
zip.finish();
}
}
/**
* Part of the {@link #execute} operation, add a single file to the zip archive.
*
* @since 1.0
*/
protected void executeAddFileToZip(ZipArchiveOutputStream zip, NamedFile file)
throws IOException {
var entry = new ZipArchiveEntry(file.file(), file.name().replace('\\', '/'));
entry.setTime(file.file().lastModified());
int mode = 0;
if (file.file().isFile()) {
mode = UnixStat.FILE_FLAG;
} else if (file.file().isDirectory()) {
mode = UnixStat.DIR_FLAG;
}
if (mode != 0) {
var permissions = Files.getPosixFilePermissions(file.file().toPath());
var owner = 0;
var group = 0;
var others = 0;
for (var p : permissions) {
switch (p) {
case OWNER_READ -> owner += 4;
case OWNER_WRITE -> owner += 2;
case OWNER_EXECUTE -> owner += 1;
case GROUP_READ -> group += 4;
case GROUP_WRITE -> group += 2;
case GROUP_EXECUTE -> group += 1;
case OTHERS_READ -> others += 4;
case OTHERS_WRITE -> others += 2;
case OTHERS_EXECUTE -> others += 1;
}
}
mode = owner * 64 + group * 8 + others;
entry.setUnixMode(mode);
}
zip.putArchiveEntry(entry);
if (file.file().isFile()) {
try (InputStream i = Files.newInputStream(file.file().toPath())) {
IOUtils.copy(i, zip);
}
}
zip.closeArchiveEntry();
}
/**
* Provides source directories that will be used for the zip archive creation.
*
* @param directories source directories
* @return this operation instance
* @since 1.0
*/
public ZipOperation sourceDirectories(File... directories) {
sourceDirectories_.addAll(List.of(directories));
return this;
}
/**
* Provides a list of source directories that will be used for the zip archive creation.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param directories a list of source directories
* @return this operation instance
* @since 1.0
*/
public ZipOperation sourceDirectories(List<File> directories) {
sourceDirectories_.addAll(directories);
return this;
}
/**
* Provides source files that will be used for the zip archive creation.
*
* @param files source files
* @return this operation instance
* @since 1.0
*/
public ZipOperation sourceFiles(NamedFile... files) {
sourceFiles_.addAll(List.of(files));
return this;
}
/**
* Provides a list of source files that will be used for the zip archive creation.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param files a list of source files
* @return this operation instance
* @since 1.0
*/
public ZipOperation sourceFiles(List<NamedFile> files) {
sourceFiles_.addAll(files);
return this;
}
/**
* Provides the destination directory in which the zip archive will be created.
*
* @param directory the zip destination directory
* @return this operation instance
* @since 1.0
*/
public ZipOperation destinationDirectory(File directory) {
destinationDirectory_ = directory;
return this;
}
/**
* Provides the destination file name that will be used for the zip archive creation.
*
* @param name the zip archive destination file name
* @return this operation instance
* @since 1.0
*/
public ZipOperation destinationFileName(String name) {
destinationFileName_ = name;
return this;
}
/**
* Provides regex patterns that will be found to determine which files
* will be included in the javadoc generation.
*
* @param included inclusion patterns
* @return this operation instance
* @since 1.0
*/
public ZipOperation included(String... included) {
included_.addAll(Arrays.stream(included).map(Pattern::compile).toList());
return this;
}
/**
* Provides patterns that will be found to determine which files
* will be included in the zip archive.
*
* @param included inclusion patterns
* @return this operation instance
* @since 1.0
*/
public ZipOperation included(Pattern... included) {
included_.addAll(List.of(included));
return this;
}
/**
* Provides a list of patterns that will be found to determine which files
* will be included in the zip archive.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param included a list of inclusion patterns
* @return this operation instance
* @since 1.0
*/
public ZipOperation included(List<Pattern> included) {
included_.addAll(included);
return this;
}
/**
* Provides regex patterns that will be found to determine which files
* will be excluded from the javadoc generation.
*
* @param excluded exclusion patterns
* @return this operation instance
* @since 1.0
*/
public ZipOperation excluded(String... excluded) {
excluded_.addAll(Arrays.stream(excluded).map(Pattern::compile).toList());
return this;
}
/**
* Provides patterns that will be found to determine which files
* will be excluded from the zip archive.
*
* @param excluded exclusion patterns
* @return this operation instance
* @since 1.0
*/
public ZipOperation excluded(Pattern... excluded) {
excluded_.addAll(List.of(excluded));
return this;
}
/**
* Provides a list of patterns that will be found to determine which files
* will be excluded from the zip archive.
* <p>
* A copy will be created to allow this list to be independently modifiable.
*
* @param excluded a list of exclusion patterns
* @return this operation instance
* @since 1.0
*/
public ZipOperation excluded(List<Pattern> excluded) {
excluded_.addAll(excluded);
return this;
}
/**
* Retrieves the list of source directories that will be used for the
* zip archive creation.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the zip archive's source directories
* @since 1.0
*/
public List<File> sourceDirectories() {
return sourceDirectories_;
}
/**
* Retrieves the list of source files that will be used for the
* zip archive creation.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the zip archive's source files
* @since 1.0
*/
public List<NamedFile> sourceFiles() {
return sourceFiles_;
}
/**
* Retrieves the destination directory in which the zip archive will
* be created.
*
* @return the zip archive's destination directory
* @since 1.0
*/
public File destinationDirectory() {
return destinationDirectory_;
}
/**
* Retrieves the destination file name that will be used for the zip
* archive creation.
*
* @return the zip archive's destination file name
* @since 1.0
*/
public String destinationFileName() {
return destinationFileName_;
}
/**
* Retrieves the destination file where the zip archive will be created.
*
* @return the zip archive's destination file
* @since 1.0
*/
public File destinationFile() {
return new File(destinationDirectory(), destinationFileName());
}
/**
* Retrieves the list of patterns that will be evaluated to determine which files
* will be included in the zip archive.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the zip's archive's inclusion patterns
* @since 1.0
*/
public List<Pattern> included() {
return included_;
}
/**
* Retrieves the list of patterns that will be evaluated to determine which files
* will be excluded the zip archive.
* <p>
* This is a modifiable list that can be retrieved and changed.
*
* @return the zip's archive's exclusion patterns
* @since 1.0
*/
public List<Pattern> excluded() {
return excluded_;
}
}

View file

@ -0,0 +1,172 @@
/*
* Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com)
* Licensed under the Apache License, Version 2.0 (the "License")
*/
package rife.bld.extension;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.junit.jupiter.api.Test;
import rife.bld.NamedFile;
import rife.tools.FileUtils;
import java.io.File;
import java.nio.file.Files;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
import static java.nio.file.attribute.PosixFilePermission.*;
import static org.junit.jupiter.api.Assertions.*;
public class TestZipOperation {
@Test
void testInstantiation() {
var operation = new ZipOperation();
assertTrue(operation.sourceDirectories().isEmpty());
assertTrue(operation.sourceFiles().isEmpty());
assertNull(operation.destinationDirectory());
assertNull(operation.destinationFileName());
assertTrue(operation.included().isEmpty());
assertTrue(operation.excluded().isEmpty());
}
@Test
void testPopulation() {
var source_directory1 = new File("sourceDirectory1");
var source_directory2 = new File("sourceDirectory2");
var source_file1 = new NamedFile("sourceFile1", new File("sourceFile1"));
var source_file2 = new NamedFile("sourceFile2", new File("sourceFile2"));
var destination_directory = new File("destinationDirectory");
var destination_fileName = "destinationFileName";
var included1 = Pattern.compile("included1");
var included2 = Pattern.compile("included2");
var excluded1 = Pattern.compile("excluded1");
var excluded2 = Pattern.compile("excluded2");
var operation1 = new ZipOperation()
.sourceDirectories(List.of(source_directory1, source_directory2))
.sourceFiles(List.of(source_file1, source_file2))
.destinationDirectory(destination_directory)
.destinationFileName(destination_fileName)
.included(List.of(included1, included2))
.excluded(List.of(excluded1, excluded2));
assertTrue(operation1.sourceDirectories().contains(source_directory1));
assertTrue(operation1.sourceDirectories().contains(source_directory2));
assertTrue(operation1.sourceFiles().contains(source_file1));
assertTrue(operation1.sourceFiles().contains(source_file2));
assertEquals(destination_directory, operation1.destinationDirectory());
assertEquals(destination_fileName, operation1.destinationFileName());
assertEquals(new File(destination_directory, destination_fileName), operation1.destinationFile());
assertTrue(operation1.included().contains(included1));
assertTrue(operation1.included().contains(included2));
assertTrue(operation1.excluded().contains(excluded1));
assertTrue(operation1.excluded().contains(excluded2));
var operation2 = new ZipOperation()
.destinationDirectory(destination_directory)
.destinationFileName(destination_fileName);
operation2.sourceDirectories().add(source_directory1);
operation2.sourceDirectories().add(source_directory2);
operation2.sourceFiles().add(source_file1);
operation2.sourceFiles().add(source_file2);
operation2.included().add(included1);
operation2.included().add(included2);
operation2.excluded().add(excluded1);
operation2.excluded().add(excluded2);
assertTrue(operation2.sourceDirectories().contains(source_directory1));
assertTrue(operation2.sourceDirectories().contains(source_directory2));
assertTrue(operation2.sourceFiles().contains(source_file1));
assertTrue(operation2.sourceFiles().contains(source_file2));
assertEquals(destination_directory, operation2.destinationDirectory());
assertEquals(destination_fileName, operation2.destinationFileName());
assertTrue(operation2.included().contains(included1));
assertTrue(operation2.included().contains(included2));
assertTrue(operation2.excluded().contains(excluded1));
assertTrue(operation2.excluded().contains(excluded2));
var operation3 = new ZipOperation()
.sourceDirectories(source_directory1, source_directory2)
.sourceFiles(source_file1, source_file2)
.destinationDirectory(destination_directory)
.destinationFileName(destination_fileName)
.included(included1, included2)
.excluded(excluded1, excluded2);
assertTrue(operation3.sourceDirectories().contains(source_directory1));
assertTrue(operation3.sourceDirectories().contains(source_directory2));
assertTrue(operation3.sourceFiles().contains(source_file1));
assertTrue(operation3.sourceFiles().contains(source_file2));
assertEquals(destination_directory, operation3.destinationDirectory());
assertEquals(destination_fileName, operation3.destinationFileName());
assertTrue(operation3.included().contains(included1));
assertTrue(operation3.included().contains(included2));
assertTrue(operation3.excluded().contains(excluded1));
assertTrue(operation3.excluded().contains(excluded2));
}
@Test
void testExecute()
throws Exception {
var tmp = Files.createTempDirectory("test").toFile();
try {
var source_dir = new File(tmp, "source");
var destination_dir = new File(tmp, "destination");
var destination_name = "archive.zip";
source_dir.mkdirs();
var source1 = new File(source_dir, "source1.text");
var source2 = new File(source_dir, "source2.text");
var source3 = new File(source_dir, "source3.text");
var source4 = new File(source_dir, "source4.txt");
var source5 = new File(tmp, "source5.text");
var source6 = new File(tmp, "source6.text");
FileUtils.writeString("source1", source1);
FileUtils.writeString("source2", source2);
FileUtils.writeString("source3", source3);
FileUtils.writeString("source4", source4);
FileUtils.writeString("source5", source5);
FileUtils.writeString("source6", source6);
Files.setPosixFilePermissions(source1.toPath(), Set.of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE));
Files.setPosixFilePermissions(source2.toPath(), Set.of(OWNER_READ, GROUP_READ, GROUP_WRITE, GROUP_EXECUTE));
Files.setPosixFilePermissions(source3.toPath(), Set.of(OWNER_READ, OTHERS_READ, OTHERS_WRITE, OTHERS_EXECUTE));
new ZipOperation()
.sourceDirectories(List.of(source_dir))
.sourceFiles(List.of(
new NamedFile("src5.txt", source5),
new NamedFile("src6.txt", source6)))
.destinationDirectory(destination_dir)
.destinationFileName(destination_name)
.included("source.*\\.text")
.excluded("source5.*")
.execute();
var zip_archive = new File(destination_dir, destination_name);
assertTrue(zip_archive.exists());
var content = new StringBuilder();
try (var zip = new ZipFile(zip_archive)) {
var e = zip.getEntries();
while (e.hasMoreElements()) {
var zip_entry = e.nextElement();
content.append(zip_entry.getName());
content.append(" ");
content.append(zip_entry.getUnixMode());
content.append("\n");
}
}
assertEquals("""
source1.text 448
source2.text 312
source3.text 263
src6.txt 420
""", content.toString());
} finally {
FileUtils.deleteDirectory(tmp);
}
}
}