96 lines
3.8 KiB
Java
96 lines
3.8 KiB
Java
package io.gitlab.jfronny.woodpecker.include;
|
|
|
|
import io.gitlab.jfronny.commons.HttpUtils;
|
|
import io.gitlab.jfronny.commons.StreamIterable;
|
|
import io.gitlab.jfronny.woodpecker.include.model.Pipeline;
|
|
|
|
import java.io.*;
|
|
import java.net.URI;
|
|
import java.net.URISyntaxException;
|
|
import java.nio.file.Paths;
|
|
import java.util.*;
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
import java.util.function.*;
|
|
import java.util.regex.Matcher;
|
|
import java.util.regex.Pattern;
|
|
|
|
final class PipelineUnpacker implements BiConsumer<Pipeline, Consumer<Pipeline>> {
|
|
private static final String URL = "https?://(?:www\\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b[-a-zA-Z0-9()@:%_+.~#?&/=]*";
|
|
private static final Pattern INCLUDE = Pattern.compile("^#include (" + URL + ")$");
|
|
private static final Pattern LINK = Pattern.compile("^#link (" + URL + ")$");
|
|
|
|
private final AtomicBoolean changed;
|
|
private final Set<String> linked = new HashSet<>();
|
|
|
|
public PipelineUnpacker(AtomicBoolean changed) {
|
|
this.changed = changed;
|
|
}
|
|
|
|
@Override
|
|
public void accept(Pipeline pipeline, Consumer<Pipeline> pipelineConsumer) {
|
|
try {
|
|
processPipeline(pipeline, pipelineConsumer, 0);
|
|
} catch (URISyntaxException e) {
|
|
throw new UncheckedIOException(new IOException("Could not find URL", e));
|
|
} catch (IOException e) {
|
|
throw new UncheckedIOException(e);
|
|
}
|
|
}
|
|
|
|
private void processPipeline(Pipeline pipeline, Consumer<Pipeline> pipelineConsumer, int depth) throws URISyntaxException, IOException {
|
|
if (depth > 5) throw new IOException("Too many nested includes, a maximum of 5 is supported");
|
|
List<String> toLink = pipeline.data().lines()
|
|
.map(LINK::matcher)
|
|
.filter(Matcher::matches)
|
|
.map(s -> s.group(1))
|
|
.filter(s -> !linked.contains(s))
|
|
.toList();
|
|
boolean hasIncludes = pipeline.data().lines().anyMatch(INCLUDE.asPredicate());
|
|
if (toLink.isEmpty() && !hasIncludes) {
|
|
// Has no includes
|
|
pipelineConsumer.accept(pipeline);
|
|
return;
|
|
}
|
|
changed.set(true);
|
|
|
|
// Fill in includes and reprocess
|
|
if (hasIncludes) {
|
|
StringBuilder newData = new StringBuilder();
|
|
for (String line : new StreamIterable<>(pipeline.data().lines())) {
|
|
Matcher matcher = INCLUDE.matcher(line);
|
|
if (!matcher.matches()) newData.append(line);
|
|
else {
|
|
newData.append(download(matcher.group(1)).data());
|
|
}
|
|
newData.append('\n');
|
|
}
|
|
processPipeline(new Pipeline(pipeline.name(), newData.toString()), pipelineConsumer, depth + 1);
|
|
return;
|
|
}
|
|
|
|
// Link additional pipelines
|
|
for (String url : toLink) {
|
|
if (linked.contains(url)) continue;
|
|
linked.add(url);
|
|
processPipeline(download(url), pipelineConsumer, depth + 1);
|
|
}
|
|
|
|
if (pipeline.data().lines()
|
|
.anyMatch(INCLUDE.asPredicate().negate()
|
|
.and(LINK.asPredicate().negate())
|
|
.and(Predicate.not(String::isBlank)))) {
|
|
// More than just includes: generate override without include node
|
|
pipelineConsumer.accept(pipeline);
|
|
}
|
|
}
|
|
|
|
private Pipeline download(String url) throws URISyntaxException, IOException {
|
|
URI uri = new URI(url.replace(" ", "%20"));
|
|
if (!"http".equalsIgnoreCase(uri.getScheme()) && !"https".equalsIgnoreCase(uri.getScheme())) {
|
|
throw new URISyntaxException(url, "Could not find scheme");
|
|
}
|
|
String fileName = Paths.get(new URI(url.replace(" ", "%20")).getPath()).getFileName().toString();
|
|
return new Pipeline(fileName, HttpUtils.get(url).sendString());
|
|
}
|
|
}
|