- Replace Git dependency in `Site` with `Path` for config files - Refactor `Server` initialization and add browser-opening functionality - Introduce `BlogBuilder` for blog building logic separation - Simplify `Main` by delegating build and server logic to `BlogBuilder` and `Server` - Add CLI enhancements with new options and improved readability Signed-off-by: Minecon724 <git@m724.eu>
This commit is contained in:
parent
e0878c8fbe
commit
6d58c7d232
4 changed files with 231 additions and 155 deletions
162
src/main/java/eu/m724/blog/BlogBuilder.java
Normal file
162
src/main/java/eu/m724/blog/BlogBuilder.java
Normal file
|
@ -0,0 +1,162 @@
|
|||
package eu.m724.blog;
|
||||
|
||||
import eu.m724.blog.data.Feed;
|
||||
import eu.m724.blog.data.Post;
|
||||
import eu.m724.blog.data.Site;
|
||||
import org.apache.commons.io.file.PathUtils;
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.lib.RepositoryBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileAlreadyExistsException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class BlogBuilder {
|
||||
private final Git git;
|
||||
private final Path workingDirectory;
|
||||
|
||||
private Site site;
|
||||
private TemplateRenderer template;
|
||||
|
||||
private Path templateDirectory;
|
||||
private Path outputDirectory;
|
||||
private boolean renderDrafts = false;
|
||||
|
||||
public BlogBuilder(Git git) {
|
||||
this.git = git;
|
||||
|
||||
this.workingDirectory = git.getRepository().getDirectory().toPath().getParent();
|
||||
this.templateDirectory = workingDirectory.resolve("template");
|
||||
this.outputDirectory = workingDirectory.resolve("generated_out");
|
||||
}
|
||||
|
||||
public static BlogBuilder fromPath(Path workingDirectory) throws IOException {
|
||||
var repository = new RepositoryBuilder()
|
||||
.setGitDir(workingDirectory.resolve(".git").toFile())
|
||||
.build();
|
||||
var git = new Git(repository);
|
||||
|
||||
//
|
||||
|
||||
return new BlogBuilder(git);
|
||||
}
|
||||
|
||||
public BlogBuilder templateDirectory(Path templateDirectory) {
|
||||
this.templateDirectory = templateDirectory;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlogBuilder outputDirectory(Path outputDirectory) {
|
||||
this.outputDirectory = outputDirectory;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlogBuilder renderDrafts(boolean renderDrafts) {
|
||||
this.renderDrafts = renderDrafts;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void mkdirs(boolean force) throws IOException {
|
||||
if (outputDirectory.toFile().exists()) {
|
||||
if (force) {
|
||||
PathUtils.deleteDirectory(outputDirectory);
|
||||
} else {
|
||||
throw new FileAlreadyExistsException(outputDirectory.toString(), null, "Output directory already exists. --force?");
|
||||
}
|
||||
}
|
||||
|
||||
Files.createDirectory(outputDirectory);
|
||||
}
|
||||
|
||||
public void build() throws IOException {
|
||||
if (site == null)
|
||||
this.site = Site.fromConfig(workingDirectory.resolve("site-config.json"));
|
||||
|
||||
if (template == null)
|
||||
this.template = new TemplateRenderer(templateDirectory);
|
||||
|
||||
copyIfExists(workingDirectory.resolve("assets"), outputDirectory.resolve("assets"));
|
||||
copyIfExists(templateDirectory.resolve("static"), outputDirectory.resolve("static"));
|
||||
|
||||
Files.createDirectory(outputDirectory.resolve("post"));
|
||||
var postDirectory = workingDirectory.resolve("posts");
|
||||
var posts = new ArrayList<Post>();
|
||||
|
||||
try (var stream = Files.walk(postDirectory)) {
|
||||
for (var path : stream.collect(Collectors.toSet())) {
|
||||
if (!Files.isRegularFile(path))
|
||||
continue; // directory is created below
|
||||
|
||||
if (!path.toString().endsWith(".html")) {
|
||||
System.out.println("Post " + path.getFileName() + ": unsupported file type");
|
||||
continue;
|
||||
}
|
||||
|
||||
path = postDirectory.relativize(path);
|
||||
var post = Post.fromFile(git, path);
|
||||
|
||||
if (post.draft() && !renderDrafts) {
|
||||
System.out.println("Post " + path.getFileName() + ": draft, ignoring");
|
||||
continue;
|
||||
}
|
||||
|
||||
var render = template.renderPost(site, post);
|
||||
var outFile = outputDirectory.resolve("post").resolve(path);
|
||||
|
||||
try {
|
||||
Files.createDirectory(outFile.getParent());
|
||||
} catch (FileAlreadyExistsException ignored) { }
|
||||
|
||||
Files.writeString(outFile, render);
|
||||
posts.add(post);
|
||||
}
|
||||
}
|
||||
|
||||
posts.sort(Comparator.comparing(Post::createdAt).reversed());
|
||||
Files.writeString(outputDirectory.resolve("index.html"), template.renderIndex(site, posts));
|
||||
|
||||
Files.writeString(outputDirectory.resolve("posts.rss"), Feed.generateRss(site, posts));
|
||||
}
|
||||
|
||||
|
||||
/* Internal functions */
|
||||
|
||||
private boolean copyTree(Path srcDir, Path destDir) throws IOException {
|
||||
if (!Files.isDirectory(srcDir)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try (var walk = Files.walk(srcDir)) {
|
||||
for (var src : walk.collect(Collectors.toSet())) {
|
||||
var rel = srcDir.relativize(src);
|
||||
var dest = destDir.resolve(rel);
|
||||
|
||||
if (Files.isRegularFile(src)) {
|
||||
var parent = dest.getParent();
|
||||
|
||||
if (!Files.isDirectory(parent)) {
|
||||
Files.createDirectories(parent);
|
||||
}
|
||||
|
||||
Files.copy(src, dest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void copyIfExists(Path srcDir, Path destDir) throws IOException {
|
||||
System.out.print(srcDir);
|
||||
|
||||
if (copyTree(srcDir, destDir)) {
|
||||
System.out.println(" copied");
|
||||
} else {
|
||||
System.out.println(" doesn't exist, not copying");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,25 +1,59 @@
|
|||
package eu.m724.blog;
|
||||
|
||||
import eu.m724.blog.data.Feed;
|
||||
import eu.m724.blog.data.Post;
|
||||
import eu.m724.blog.data.Site;
|
||||
import org.apache.commons.cli.*;
|
||||
import org.apache.commons.io.file.PathUtils;
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.lib.RepositoryBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileAlreadyExistsException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) throws IOException {
|
||||
System.out.println("Hello world!");
|
||||
|
||||
var commandLine = getCommandLine(args);
|
||||
|
||||
if (commandLine == null)
|
||||
System.exit(2);
|
||||
|
||||
args = commandLine.getArgs();
|
||||
|
||||
var workingDirectory = Path.of(args[0]);
|
||||
var templateDirectory = Path.of(commandLine.getOptionValue("output-dir", workingDirectory.resolve("template").toString()));
|
||||
var outputDirectory = Path.of(commandLine.getOptionValue("output-dir", workingDirectory.resolve("generated_out").toString()));
|
||||
var force = commandLine.hasOption("force");
|
||||
|
||||
var startServer = commandLine.hasOption("server");
|
||||
var openBrowser = !commandLine.hasOption("no-browser");
|
||||
var renderDrafts = commandLine.hasOption("draft") || startServer;
|
||||
|
||||
|
||||
/* Build process */
|
||||
|
||||
var start = System.nanoTime();
|
||||
|
||||
var builder = BlogBuilder.fromPath(workingDirectory)
|
||||
.templateDirectory(templateDirectory)
|
||||
.outputDirectory(outputDirectory)
|
||||
.renderDrafts(renderDrafts);
|
||||
|
||||
builder.mkdirs(force);
|
||||
builder.build();
|
||||
|
||||
var end = System.nanoTime();
|
||||
System.out.printf("Exported to %s (%.2f ms)\n", outputDirectory, (end - start) / 1000000.0);
|
||||
|
||||
/* Server process */
|
||||
|
||||
if (startServer) {
|
||||
var server = new Server(outputDirectory);
|
||||
server.start();
|
||||
|
||||
if (openBrowser) {
|
||||
server.openBrowser();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static CommandLine getCommandLine(String[] args) {
|
||||
var options = new Options()
|
||||
.addOption("h", "help", false, "Show help")
|
||||
.addOption("f", "force", false, "Overwrite current build")
|
||||
|
@ -30,7 +64,6 @@ public class Main {
|
|||
.addOption("d", "draft", false, "Render drafts. Default: only with server");
|
||||
|
||||
CommandLine commandLine;
|
||||
String wdStr;
|
||||
try {
|
||||
commandLine = new DefaultParser().parse(options, args);
|
||||
|
||||
|
@ -40,145 +73,13 @@ public class Main {
|
|||
if (commandLine.getArgs().length == 0)
|
||||
throw new ParseException("Missing required argument: WORKDIR");
|
||||
|
||||
wdStr = commandLine.getArgs()[0];
|
||||
} catch (ParseException e) {
|
||||
System.out.println(e.getMessage());
|
||||
new HelpFormatter().printHelp("blog-software-java [OPTION]... [WORKDIR]", options);
|
||||
|
||||
System.exit(1);
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
var workingDirectory = Path.of(wdStr);
|
||||
var templateDirectory = Path.of(commandLine.getOptionValue("output-dir", workingDirectory.resolve("template").toString()));
|
||||
var outputDirectory = Path.of(commandLine.getOptionValue("output-dir", workingDirectory.resolve("generated_out").toString()));
|
||||
var force = commandLine.hasOption("force");
|
||||
|
||||
var server = commandLine.hasOption("server");
|
||||
var openBrowser = !commandLine.hasOption("no-browser");
|
||||
var renderDrafts = commandLine.hasOption("draft") || server;
|
||||
|
||||
//
|
||||
|
||||
var start = System.nanoTime();
|
||||
|
||||
var repository = new RepositoryBuilder()
|
||||
.setGitDir(workingDirectory.resolve(".git").toFile())
|
||||
.build();
|
||||
var git = new Git(repository);
|
||||
|
||||
//
|
||||
|
||||
if (outputDirectory.toFile().exists()) {
|
||||
if (force) {
|
||||
PathUtils.deleteDirectory(outputDirectory);
|
||||
} else {
|
||||
throw new FileAlreadyExistsException(outputDirectory.toString(), null, "Output directory already exists. --force?");
|
||||
}
|
||||
}
|
||||
|
||||
var site = Site.fromConfig(git);
|
||||
var template = new TemplateRenderer(templateDirectory);
|
||||
|
||||
Files.createDirectory(outputDirectory);
|
||||
|
||||
copyIfExists(workingDirectory.resolve("assets"), outputDirectory.resolve("assets"));
|
||||
copyIfExists(templateDirectory.resolve("static"), outputDirectory.resolve("static"));
|
||||
|
||||
Files.createDirectory(outputDirectory.resolve("post"));
|
||||
var postDirectory = workingDirectory.resolve("posts");
|
||||
var posts = new ArrayList<Post>();
|
||||
|
||||
try (var stream = Files.walk(postDirectory)) {
|
||||
for (var path : stream.collect(Collectors.toSet())) {
|
||||
if (!Files.isRegularFile(path))
|
||||
continue; // directory is created below
|
||||
|
||||
if (!path.toString().endsWith(".html")) {
|
||||
System.out.println("Post " + path.getFileName() + ": unsupported file type");
|
||||
continue;
|
||||
}
|
||||
|
||||
path = postDirectory.relativize(path);
|
||||
var post = Post.fromFile(git, path);
|
||||
|
||||
if (post.draft() && !renderDrafts) {
|
||||
System.out.println("Post " + path.getFileName() + ": draft, ignoring");
|
||||
continue;
|
||||
}
|
||||
|
||||
var render = template.renderPost(site, post);
|
||||
var outFile = outputDirectory.resolve("post").resolve(path);
|
||||
|
||||
try {
|
||||
Files.createDirectory(outFile.getParent());
|
||||
} catch (FileAlreadyExistsException ignored) { }
|
||||
|
||||
Files.writeString(outFile, render);
|
||||
posts.add(post);
|
||||
}
|
||||
}
|
||||
|
||||
posts.sort(Comparator.comparing(Post::createdAt).reversed());
|
||||
Files.writeString(outputDirectory.resolve("index.html"), template.renderIndex(site, posts));
|
||||
|
||||
Files.writeString(outputDirectory.resolve("posts.rss"), Feed.generateRss(site, posts));
|
||||
|
||||
|
||||
var end = System.nanoTime();
|
||||
System.out.printf("Exported to %s (%.2f ms)\n", outputDirectory, (end - start) / 1000000.0);
|
||||
|
||||
if (server) {
|
||||
var listenAddress = new Server(outputDirectory).start();
|
||||
if (openBrowser) {
|
||||
try {
|
||||
var process = Runtime.getRuntime().exec(new String[] { "xdg-open", "http://" + listenAddress.getHostString() + ":" + listenAddress.getPort() });
|
||||
var code = process.waitFor();
|
||||
if (code != 0) {
|
||||
throw new Exception("Exit code " + code);
|
||||
}
|
||||
System.out.println("Opened browser");
|
||||
} catch (Exception e) {
|
||||
System.out.println("Failed to open browser: " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean copyTree(Path srcDir, Path destDir) throws IOException {
|
||||
if (!Files.isDirectory(srcDir)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try (var walk = Files.walk(srcDir)) {
|
||||
for (var src : walk.collect(Collectors.toSet())) {
|
||||
var rel = srcDir.relativize(src);
|
||||
var dest = destDir.resolve(rel);
|
||||
|
||||
if (Files.isRegularFile(src)) {
|
||||
var parent = dest.getParent();
|
||||
|
||||
if (!Files.isDirectory(parent)) {
|
||||
Files.createDirectories(parent);
|
||||
}
|
||||
|
||||
Files.copy(src, dest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private static void copyIfExists(Path srcDir, Path destDir) throws IOException {
|
||||
System.out.print(srcDir);
|
||||
|
||||
if (copyTree(srcDir, destDir)) {
|
||||
System.out.println(" copied");
|
||||
} else {
|
||||
System.out.println(" doesn't exist, not copying");
|
||||
}
|
||||
return commandLine;
|
||||
}
|
||||
}
|
|
@ -11,26 +11,39 @@ import java.nio.file.Files;
|
|||
import java.nio.file.Path;
|
||||
|
||||
public class Server implements HttpHandler {
|
||||
private final InetSocketAddress listenAddress;
|
||||
private final Path webroot;
|
||||
private InetSocketAddress listenAddress;
|
||||
|
||||
public Server(InetSocketAddress listenAddress, Path webroot) {
|
||||
this.listenAddress = listenAddress;
|
||||
public Server(Path webroot, InetSocketAddress listenAddress) {
|
||||
this.webroot = webroot;
|
||||
this.listenAddress = listenAddress;
|
||||
}
|
||||
|
||||
public Server(Path webroot) {
|
||||
this(new InetSocketAddress("localhost", 0), webroot);
|
||||
this(webroot, new InetSocketAddress("localhost", 0));
|
||||
}
|
||||
|
||||
public InetSocketAddress start() throws IOException {
|
||||
public void start() throws IOException {
|
||||
var server = HttpServer.create(listenAddress, 0);
|
||||
server.createContext("/", this);
|
||||
server.start();
|
||||
|
||||
System.out.println("Server started on http:/" + server.getAddress());
|
||||
|
||||
return server.getAddress();
|
||||
this.listenAddress = server.getAddress();
|
||||
}
|
||||
|
||||
public void openBrowser() {
|
||||
try {
|
||||
var process = Runtime.getRuntime().exec(new String[] { "xdg-open", "http://" + listenAddress.getHostString() + ":" + listenAddress.getPort() });
|
||||
var code = process.waitFor();
|
||||
if (code != 0) {
|
||||
throw new Exception("Exit code " + code);
|
||||
}
|
||||
System.out.println("Opened browser");
|
||||
} catch (Exception e) {
|
||||
System.out.println("Failed to open browser: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package eu.m724.blog.data;
|
||||
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -14,8 +14,8 @@ public record Site(
|
|||
|
||||
Map<String, Object> custom
|
||||
) {
|
||||
public static Site fromConfig(Git git) throws IOException {
|
||||
var content = Files.readString(git.getRepository().getDirectory().toPath().getParent().resolve("site-config.json"));
|
||||
public static Site fromConfig(Path path) throws IOException {
|
||||
var content = Files.readString(path);
|
||||
var json = new JSONObject(content);
|
||||
|
||||
String name = null;
|
||||
|
|
Loading…
Add table
Reference in a new issue