Skip to content

Commit

Permalink
Introduce a digest candidate and make it the actual digest upon succe…
Browse files Browse the repository at this point in the history
…ssful build completion

Print added, modified and removed files
Refactor the code to make it readable and maintainable
  • Loading branch information
rafalsatl committed Sep 20, 2024
1 parent ea51b4f commit 9dc05d8
Showing 1 changed file with 150 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@

import static com.github.eirslett.maven.plugins.frontend.mojo.YarnUtils.isYarnrcYamlFilePresent;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
Expand All @@ -13,7 +18,9 @@
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.maven.execution.MavenSession;
Expand Down Expand Up @@ -87,6 +94,8 @@ public synchronized void execute(FrontendPluginFactory factory) throws TaskRunne
boolean isYarnBerry = isYarnrcYamlFilePresent(this.session, this.workingDirectory);
factory.getYarnRunner(proxyConfig, getRegistryUrl(), isYarnBerry).execute(this.arguments,
this.environmentVariables);

acceptIncrementalBuildDigest();
} else {
getLog().info("Skipping yarn install as package.json unchanged");
}
Expand All @@ -95,62 +104,40 @@ public synchronized void execute(FrontendPluginFactory factory) throws TaskRunne
}
}

private void acceptIncrementalBuildDigest() {
getLog().info("Accepting yarn incremental build digest...");
if (getDigestFile().exists()) {
if (!getDigestFile().delete()) {
getLog().warn("Failed to delete the previous incremental build digest");
}
}

if (!getDigestCandidateFile().renameTo(getDigestFile())) {
getLog().warn("Failed to accept the incremental build digest");
}
}

private boolean shouldExecute() {
if (this.arguments.equals("build")) {
try {
ArrayList<File> triggerfiles = new ArrayList<>();

Files.walkFileTree(workingDirectory.toPath(), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path file, BasicFileAttributes attrs)
{
if (file.endsWith("target")) {
return FileVisitResult.SKIP_SUBTREE;
} else {
return FileVisitResult.CONTINUE;
}
}
ArrayList<File> triggerfiles = getDigestFiles();
String currDigest = createDigest(triggerfiles);

@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
String filename = file.getFileName().toString();
if (filename.endsWith(".js")) {
triggerfiles.add(file.toFile());
}
try {
String prevDigest = readPreviousDigest();

return FileVisitResult.CONTINUE;
}
});

String completeDigest = triggerfiles.parallelStream().map(file -> {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");

try (FileInputStream fis = new FileInputStream(file)) {
byte[] byteArray = new byte[1024];
while (fis.read(byteArray) != -1) {
digest.update(byteArray);
}
} catch (IOException e) {
throw new RuntimeException(e);
}

byte[] bytes = digest.digest();
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}

return file + " : " + sb;
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
if (currDigest.equals(prevDigest)) {
getLog().info("Atlassian Fork FTW - No changes detected! - Skipping yarn build");
// For now, we'll just assume all the target files are still there ;)
return false; // TADAM! Build is not needed
}
}).sorted().collect(Collectors.joining("\n"));

// TODO save to target and compare
// if the file in target will not exist than we know clean was performed
reportDigestDifferences(prevDigest, currDigest);
} catch (FileNotFoundException e) {
// This is fine... we'll save the digest this time
} // Let any other IOException's get handled below

return true;
saveDigestCandidate(currDigest);
} catch (IOException e) {
getLog().error("Failed to determine if an incremental build is needed: " + e);
}
Expand All @@ -159,6 +146,121 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
return true;
}

private ArrayList<File> getDigestFiles() throws IOException {
ArrayList<File> files = new ArrayList<>();

Files.walkFileTree(workingDirectory.toPath(), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path file, BasicFileAttributes attrs)
{
if (file.endsWith("target")) {
return FileVisitResult.SKIP_SUBTREE;
} else {
return FileVisitResult.CONTINUE;
}
}

@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
String filename = file.getFileName().toString();
if (filename.endsWith(".js")) {
files.add(file.toFile());
}

return FileVisitResult.CONTINUE;
}
});

return files;
}

private static String createDigest(ArrayList<File> triggerfiles) {
return triggerfiles.parallelStream().map(file -> {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");

try (FileInputStream fis = new FileInputStream(file)) {
byte[] byteArray = new byte[1024];
while (fis.read(byteArray) != -1) {
digest.update(byteArray);
}
} catch (IOException e) {
throw new RuntimeException(e);
}

byte[] bytes = digest.digest();
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}

return file + " : " + sb + "\n";
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}).sorted().collect(Collectors.joining(""));
}

private void saveDigestCandidate(String currDigest) throws IOException {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(getDigestCandidateFile()))) {
writer.write(currDigest);
}
}

private String readPreviousDigest() throws IOException {
try (BufferedReader reader = new BufferedReader(new FileReader(getDigestFile()))) {
StringBuilder previousDigest = new StringBuilder();

String line;
while ((line = reader.readLine()) != null) {
previousDigest.append(line).append("\n");
}

return previousDigest.toString();
}
}

private void reportDigestDifferences(String prevDigest, String currDigest) {
Map<String, String> prevDigestContents = getDigestContentMap(prevDigest);
Map<String, String> currDigestContents = getDigestContentMap(currDigest);

for (Map.Entry<String, String> entry : prevDigestContents.entrySet()) {
String prevFile = entry.getKey();
String prevHash = entry.getValue();
String currHash = currDigestContents.get(prevFile);
if (currHash == null) {
getLog().info("File removed: " + prevFile);
} else if (!prevHash.equals(currHash)) {
getLog().info("File changed: " + prevFile);
}
}

for (Map.Entry<String, String> entry : currDigestContents.entrySet()) {
String currFile = entry.getKey();
String prevHash = prevDigestContents.get(currFile);
if (prevHash == null) {
getLog().info("File added: " + currFile);
}
}
}

private Map<String, String> getDigestContentMap(String digest) {
return Arrays.stream(digest.split("\n")).map(line -> line.split(" : "))
.collect(Collectors.toMap(parts -> parts[0], parts -> parts[1]));
}

private File getDigestCandidateFile() {
return new File(getTargetDir(), "yarn-incremental-build-digest.candidate.txt");
}

private File getDigestFile() {
return new File(getTargetDir(), "yarn-incremental-build-digest.txt");
}

private File getTargetDir() {
return new File(workingDirectory, "target");
}

private ProxyConfig getProxyConfig() {
if (this.yarnInheritsProxyConfigFromMaven) {
return MojoUtils.getProxyConfig(this.session, this.decrypter);
Expand Down

0 comments on commit 9dc05d8

Please sign in to comment.