Skip to content

Commit

Permalink
added repoinit support: checklist, plan, and init stage.
Browse files Browse the repository at this point in the history
  • Loading branch information
adamcin committed May 6, 2020
1 parent 469d5a7 commit b9eb7b3
Show file tree
Hide file tree
Showing 54 changed files with 1,462 additions and 88 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ target/
*.iml
.mvn/wrapper/maven-wrapper.jar
.oakpal-cache/
pom.xml.versionsBackup
dependency-reduced-pom.xml
2 changes: 1 addition & 1 deletion api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<parent>
<groupId>net.adamcin.oakpal</groupId>
<artifactId>oakpal</artifactId>
<version>2.0.1-SNAPSHOT</version>
<version>2.1.0-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion cli/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<parent>
<groupId>net.adamcin.oakpal</groupId>
<artifactId>oakpal</artifactId>
<version>2.0.1-SNAPSHOT</version>
<version>2.1.0-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>

Expand Down
8 changes: 8 additions & 0 deletions cli/src/main/java/net/adamcin/oakpal/cli/Command.java
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,14 @@ Optional<String> flipOpt(final @NotNull String wholeOpt) {
builder.addPreInstallFile(console.getCwd().toPath().resolve(args[++i]).toFile());
}
break;
case "-ri":
case "--repoinit-file":
if (isNoOpt) {
builder.setRepoInitFiles(Collections.emptyList());
} else {
builder.addRepoInitFile(console.getCwd().toPath().resolve(args[++i]).toFile());
}
break;
case "-xp":
case "--extend-classpath":
if (isNoOpt) {
Expand Down
83 changes: 81 additions & 2 deletions cli/src/main/java/net/adamcin/oakpal/cli/Options.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package net.adamcin.oakpal.cli;

import net.adamcin.oakpal.api.Fun;
import net.adamcin.oakpal.api.Nothing;
import net.adamcin.oakpal.api.Result;
import net.adamcin.oakpal.api.Severity;
Expand All @@ -12,11 +13,16 @@
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
Expand All @@ -25,6 +31,7 @@
import java.util.stream.Collectors;

import static net.adamcin.oakpal.api.Fun.compose1;
import static net.adamcin.oakpal.api.Fun.constantly1;
import static net.adamcin.oakpal.api.Fun.result1;

final class Options {
Expand All @@ -42,6 +49,7 @@ final class Options {
private final File planFile;
private final File planFileBaseDir;
private final List<File> preInstallFiles;
private final List<File> repoInitFiles;
private final List<File> extendedClassPathFiles;
private final boolean noHooks;
private final List<File> scanFiles;
Expand All @@ -54,6 +62,7 @@ final class Options {
new File(System.getProperty("java.io.tmpdir")),
null, null, null, null,
Collections.emptyList(),
Collections.emptyList(),
Collections.emptyList(), false,
Collections.emptyList(),
EMPTY_PRINTER,
Expand All @@ -71,6 +80,7 @@ final class Options {
final @Nullable File planFile,
final @Nullable File planFileBaseDir,
final @NotNull List<File> preInstallFiles,
final @NotNull List<File> repoInitFiles,
final @NotNull List<File> extendedClassPathFiles,
final boolean noHooks,
final @NotNull List<File> scanFiles,
Expand All @@ -87,6 +97,7 @@ final class Options {
this.planFile = planFile;
this.planFileBaseDir = planFileBaseDir;
this.preInstallFiles = preInstallFiles;
this.repoInitFiles = repoInitFiles;
this.extendedClassPathFiles = extendedClassPathFiles;
this.noHooks = noHooks;
this.scanFiles = scanFiles;
Expand Down Expand Up @@ -142,6 +153,10 @@ public File getCacheDir() {
return preInstallFiles;
}

public @NotNull List<File> getRepoInitFiles() {
return repoInitFiles;
}

public @NotNull List<File> getExtendedClassPathFiles() {
return extendedClassPathFiles;
}
Expand All @@ -159,7 +174,7 @@ public Severity getFailOnSeverity() {
}

boolean hasOverrides() {
return noHooks || !getPreInstallFiles().isEmpty();
return noHooks || !getPreInstallFiles().isEmpty() || !getRepoInitFiles().isEmpty();
}

public OakpalPlan applyOverrides(final @NotNull OakpalPlan basePlan) {
Expand All @@ -172,6 +187,18 @@ public OakpalPlan applyOverrides(final @NotNull OakpalPlan basePlan) {
.collect(Result.tryCollect(Collectors.toList())).forEach(allUrls::addAll);
overridePlan.withPreInstallUrls(allUrls);
}
if (!getRepoInitFiles().isEmpty()) {
List<URL> allUrls = new ArrayList<>(basePlan.getRepoInitUrls());
if (!basePlan.getRepoInits().isEmpty()) {
cacheRepoInits(getCacheDir(), basePlan.getRepoInits()).forEach(repoInitUrl -> {
allUrls.add(repoInitUrl);
overridePlan.withRepoInits(Collections.emptyList());
});
}
getRepoInitFiles().stream().map(compose1(File::toURI, result1(URI::toURL)))
.collect(Result.tryCollect(Collectors.toList())).forEach(allUrls::addAll);
overridePlan.withRepoInitUrls(allUrls);
}
if (isNoHooks()) {
overridePlan.withInstallHookPolicy(InstallHookPolicy.SKIP);
overridePlan.withEnablePreInstallHooks(false);
Expand All @@ -182,6 +209,47 @@ public OakpalPlan applyOverrides(final @NotNull OakpalPlan basePlan) {
}
}

static byte[] getRepoInitBytes(final List<String> repoInits) {
return String.join("\n", repoInits).getBytes(StandardCharsets.UTF_8);
}

static Result<String> fileNameForRepoInitBytes(final byte[] repoInitBytes) {
return result1((Fun.ThrowingFunction<? super String, MessageDigest>) MessageDigest::getInstance)
.apply("SHA-1")
.map(digest -> digest.digest(repoInitBytes))
.map(Base64.getUrlEncoder()::encodeToString);
}

static Result<Nothing> writeToStream(final @NotNull byte[] bytesToWrite,
final @NotNull Fun.ThrowingSupplier<OutputStream> streamSupplier) {
try (OutputStream out = streamSupplier.tryGet()) {
out.write(bytesToWrite);
return Result.success(Nothing.instance);
} catch (Exception e) {
return Result.failure(e);
}
}

static Result<URL> cacheRepoInits(final @NotNull File cacheDir, final @NotNull List<String> repoInits) {
if (repoInits.isEmpty()) {
return Result.failure("refusing to cache empty repoinit script");
}
final byte[] repoInitBytes = getRepoInitBytes(repoInits);
final File parentDir = cacheDir.toPath().resolve("repoinits").toFile();
parentDir.mkdirs();
return fileNameForRepoInitBytes(repoInitBytes)
.map(fileName -> new File(parentDir, fileName))
.flatMap(cacheFile -> {
final Result<URL> cacheFileUrl = compose1(File::toURI, result1(URI::toURL)).apply(cacheFile);
if (cacheFile.exists()) {
return cacheFileUrl;
} else {
return writeToStream(repoInitBytes, () -> new FileOutputStream(cacheFile))
.flatMap(constantly1(() -> cacheFileUrl));
}
});
}

static final class Builder {
private boolean justHelp;
private boolean justVersion;
Expand All @@ -193,6 +261,7 @@ static final class Builder {
private File planFile;
private File planFileBaseDir;
private List<File> preInstallFiles = new ArrayList<>();
private List<File> repoInitFiles = new ArrayList<>();
private List<File> extendedClassPathFiles = new ArrayList<>();
private File outFile;
private File cacheDir;
Expand Down Expand Up @@ -255,6 +324,16 @@ public Builder addPreInstallFile(final @NotNull File preInstallFile) {
return this;
}

public Builder setRepoInitFiles(final @NotNull List<File> repoInitFiles) {
this.repoInitFiles = repoInitFiles;
return this;
}

public Builder addRepoInitFile(final @NotNull File repoInitFile) {
this.repoInitFiles.add(repoInitFile);
return this;
}

public Builder setExtendedClassPathFiles(final @NotNull List<File> extendedClassPathFiles) {
this.extendedClassPathFiles = new ArrayList<>(extendedClassPathFiles);
return this;
Expand Down Expand Up @@ -353,7 +432,7 @@ Result<Options> build(final @NotNull Console console) {
.flatMap(classLoader -> messageWriter(console, outputJson, outFile).map(writer ->
new Options(justHelp, justVersion, storeBlobs, planUrl,
classLoader, realCacheDir, opearFile, planName, planFile,
planFileBaseDir, preInstallFiles, extendedClassPathFiles,
planFileBaseDir, preInstallFiles, repoInitFiles, extendedClassPathFiles,
noHooks, scanFiles, writer, Optional.ofNullable(failOnSeverity)
.orElse(DEFAULT_OPTIONS.failOnSeverity))))));
}
Expand Down
3 changes: 2 additions & 1 deletion cli/src/main/resources/net/adamcin/oakpal/cli/help.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ oakpal [ <options> ] <scanFile> ...
or if no opear is specified, the basic oakpal plan will be used.
+p | --no-plan : Use no plan for the scan. Overrides the default behavior, which otherwise
uses the oakpal core "basic-plan.json".
-pi | --pre-install-file <file> : Add a preinstall package to the list specified in the plan, if any.
-pf | --plan-file <file> : Override the opear plan with the specified json file. The base directory for the
plan will default to the parent directory of the file, unless a different
directory is specified using --plan-file-base.
Expand All @@ -29,6 +28,8 @@ oakpal [ <options> ] <scanFile> ...
like preInstallUrls.
--no-hooks : Disable preinstall and scan install hooks for all packages, otherwise, rely on
install hook policies configured in the selected plan.
-pi | --pre-install-file <file> : Add a preinstall package to the list specified in the plan, if any.
-ri | --repoinit-file <file> : Add a repoinit file to the list specified in the plan, if any.
-xp | --extend-classpath <file> : Extend the opear classpath with the specified jar file or directory.
-s | --severity-fail <severity> : Exit with a non-zero code if any violations are
reported with a severity level equal to or higher
Expand Down
9 changes: 9 additions & 0 deletions cli/src/test/java/net/adamcin/oakpal/cli/CommandTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,8 @@ public void testParseArgs_adhocOpear() throws Exception {

final File planFromFile = new File("src/test/resources/opears/adhocPlan/plan.json");

final File repoInitFile2 = new File("src/test/resources/opears/adhocPlan/repoinit2.txt");

final Console console = getMockConsole();
final OptionsValidator validator = new OptionsValidator(console);

Expand All @@ -342,6 +344,7 @@ public void testParseArgs_adhocOpear() throws Exception {
"--plan-file", planFromFile.getAbsolutePath(),
"--plan-file-base", planFromFile.getParentFile().getAbsolutePath(),
"--pre-install-file", contentPackageJar.getAbsolutePath(),
"--repoinit-file", repoInitFile2.getAbsolutePath(),
"--extend-classpath", testModuleJar.getAbsolutePath()),
options -> {
assertEquals("expect plan file",
Expand All @@ -350,6 +353,8 @@ public void testParseArgs_adhocOpear() throws Exception {
planFromFile.getParentFile().getAbsolutePath(), options.getPlanFileBaseDir().getAbsolutePath());
assertTrue("expect pre-install file", options.getPreInstallFiles().stream()
.anyMatch(file -> file.getAbsolutePath().equals(contentPackageJar.getAbsolutePath())));
assertTrue("expect repoinit file", options.getRepoInitFiles().stream()
.anyMatch(file -> file.getAbsolutePath().equals(repoInitFile2.getAbsolutePath())));
assertTrue("expect classpath", options.getExtendedClassPathFiles().stream()
.anyMatch(file -> file.getAbsolutePath().equals(testModuleJar.getAbsolutePath())));
assertNotNull("expect test_module-handler.js", options.getScanClassLoader()
Expand All @@ -364,6 +369,8 @@ public void testParseArgs_adhocOpear() throws Exception {
"--no-plan-file-base",
"--pre-install-file", contentPackageJar.getAbsolutePath(),
"--no-pre-install-file",
"--repoinit-file", repoInitFile2.getAbsolutePath(),
"--no-repoinit-file",
"--extend-classpath", testModuleJar.getAbsolutePath(),
"--no-extend-classpath"),

Expand All @@ -372,6 +379,8 @@ public void testParseArgs_adhocOpear() throws Exception {
assertNull("expect null plan file base dir", options.getPlanFileBaseDir());
assertFalse("expect no pre-install file", options.getPreInstallFiles().stream()
.anyMatch(file -> file.getAbsolutePath().equals(contentPackageJar.getAbsolutePath())));
assertFalse("expect no repoinit file", options.getRepoInitFiles().stream()
.anyMatch(file -> file.getAbsolutePath().equals(repoInitFile2.getAbsolutePath())));
assertFalse("expect no classpath", options.getExtendedClassPathFiles().stream()
.anyMatch(file -> file.getAbsolutePath().equals(testModuleJar.getAbsolutePath())));
assertNull("expect no test_module-handler.js", options.getScanClassLoader()
Expand Down
Loading

0 comments on commit b9eb7b3

Please sign in to comment.