Skip to content

Commit

Permalink
fix(tasks): use glob patterns instead of regex
Browse files Browse the repository at this point in the history
closes #71
  • Loading branch information
brian-mulier-p committed May 30, 2024
1 parent f10e1c9 commit c34d213
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 42 deletions.
25 changes: 14 additions & 11 deletions src/main/java/io/kestra/plugin/git/AbstractPushTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@

import java.io.*;
import java.net.URI;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Stream;
Expand Down Expand Up @@ -73,7 +75,7 @@ public abstract class AbstractPushTask<O extends AbstractPushTask.Output> extend

public abstract String getGitDirectory();

public abstract Object regexes();
public abstract Object globs();

public abstract String fetchedNamespace();

Expand All @@ -83,23 +85,24 @@ private Path createGitDirectory(RunContext runContext) throws IllegalVariableEva
return flowDirectory;
}

protected abstract Map<Path, Supplier<InputStream>> instanceResourcesContentByPath(RunContext runContext, Path baseDirectory, List<String> regexes) throws Exception;
protected abstract Map<Path, Supplier<InputStream>> instanceResourcesContentByPath(RunContext runContext, Path baseDirectory, List<String> globs) throws Exception;

/**
* Removes any file from the remote that is no longer present on the instance
*/
private void deleteOutdatedResources(Git git, Path basePath, Map<Path, Supplier<InputStream>> contentByPath, List<String> regexes) throws IOException, GitAPIException {
private void deleteOutdatedResources(Git git, Path basePath, Map<Path, Supplier<InputStream>> contentByPath, List<String> globs) throws IOException, GitAPIException {
try (Stream<Path> paths = Files.walk(basePath)) {
Stream<Path> filteredPathsStream = paths.filter(path ->
!contentByPath.containsKey(path) &&
!path.getFileName().toString().equals(".git") &&
!path.equals(basePath)
);

if (regexes != null) {
filteredPathsStream = filteredPathsStream.filter(path -> regexes.stream().anyMatch(regex ->
path.getFileName().toString().matches(regex)
|| path.toString().matches(regex)
if (globs != null) {
List<PathMatcher> matchers = globs.stream().map(glob -> FileSystems.getDefault().getPathMatcher("glob:" + glob)).toList();
filteredPathsStream = filteredPathsStream.filter(path -> matchers.stream().anyMatch(matcher ->
matcher.matches(path) ||
matcher.matches(path.getFileName())
));
}

Expand Down Expand Up @@ -277,13 +280,13 @@ public O run(RunContext runContext) throws Exception {

Path localGitDirectory = this.createGitDirectory(runContext);

List<String> regexes = Optional.ofNullable(this.regexes())
.map(regexObject -> regexObject instanceof List<?> ? (List<String>) regexObject : Collections.singletonList((String) regexObject))
List<String> globs = Optional.ofNullable(this.globs())
.map(globObject -> globObject instanceof List<?> ? (List<String>) globObject : Collections.singletonList((String) globObject))
.map(throwFunction(runContext::render))
.orElse(null);
Map<Path, Supplier<InputStream>> contentByPath = this.instanceResourcesContentByPath(runContext, localGitDirectory, regexes);
Map<Path, Supplier<InputStream>> contentByPath = this.instanceResourcesContentByPath(runContext, localGitDirectory, globs);

this.deleteOutdatedResources(git, localGitDirectory, contentByPath, regexes);
this.deleteOutdatedResources(git, localGitDirectory, contentByPath, globs);

this.writeResourceFiles(contentByPath);

Expand Down
21 changes: 12 additions & 9 deletions src/main/java/io/kestra/plugin/git/PushFlows.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.URI;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -50,7 +52,7 @@ Using this task, you can push one or more flows from a given namespace (and opti
type: io.kestra.plugin.git.PushFlows
sourceNamespace: dev # the namespace from which flows are pushed
targetNamespace: prod # the target production namespace; if different than sourceNamespace, the sourceNamespace in the source code will be overwritten by the targetNamespace
flows: ".*" # optional list of Regex strings; by default, all flows are pushed
flows: "*" # optional list of glob patterns; by default, all flows are pushed
includeChildNamespaces: true # optional boolean, false by default
gitDirectory: _flows
url: https://github.com/kestra-io/scripts # required string
Expand Down Expand Up @@ -134,14 +136,14 @@ public class PushFlows extends AbstractPushTask<PushFlows.Output> {
private String targetNamespace;

@Schema(
title = "A list of Regex strings that declare which flows should be included in the Git commit.",
title = "List of glob patterns or a single one that declare which flows should be included in the Git commit.",
description = """
By default, all flows from the specified sourceNamespace will be pushed (and optionally adjusted to match the targetNamespace before pushing to Git).
If you want to push only the current flow, you can use the "{{flow.id}}" expression or specify the flow ID explicitly, e.g. myflow.
Given that this is a list of Regex strings, you can include as many flows as you wish, provided that the user is authorized to access that namespace.
Note that each regex try to match the file name OR the relative path starting from `gitDirectory`""",
Given that this is a list of glob patterns, you can include as many flows as you wish, provided that the user is authorized to access that namespace.
Note that each glob pattern try to match the file name OR the relative path starting from `gitDirectory`""",
oneOf = {String.class, String[].class},
defaultValue = ".*"
defaultValue = "**"
)
@PluginProperty(dynamic = true)
private Object flows;
Expand Down Expand Up @@ -174,7 +176,7 @@ public String getCommitMessage() {
}

@Override
public Object regexes() {
public Object globs() {
return this.flows;
}

Expand All @@ -183,7 +185,7 @@ public String fetchedNamespace() {
return this.sourceNamespace;
}

protected Map<Path, Supplier<InputStream>> instanceResourcesContentByPath(RunContext runContext, Path flowDirectory, List<String> regexes) throws IllegalVariableEvaluationException {
protected Map<Path, Supplier<InputStream>> instanceResourcesContentByPath(RunContext runContext, Path flowDirectory, List<String> globs) throws IllegalVariableEvaluationException {
FlowRepositoryInterface flowRepository = runContext.getApplicationContext().getBean(FlowRepositoryInterface.class);

Map<String, String> flowProps = Optional.ofNullable((Map<String, String>) runContext.getVariables().get("flow")).orElse(Collections.emptyMap());
Expand All @@ -197,10 +199,11 @@ protected Map<Path, Supplier<InputStream>> instanceResourcesContentByPath(RunCon
}

Stream<FlowWithSource> filteredFlowsToPush = flowsToPush.stream();
if (regexes != null) {
if (globs != null) {
List<PathMatcher> matchers = globs.stream().map(glob -> FileSystems.getDefault().getPathMatcher("glob:" + glob)).toList();
filteredFlowsToPush = filteredFlowsToPush.filter(flowWithSource -> {
String flowId = flowWithSource.getId();
return regexes.stream().anyMatch(flowId::matches);
return matchers.stream().anyMatch(matcher -> matcher.matches(Path.of(flowId)));
});
}

Expand Down
31 changes: 17 additions & 14 deletions src/main/java/io/kestra/plugin/git/PushNamespaceFiles.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@

import java.io.InputStream;
import java.net.URI;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand Down Expand Up @@ -45,8 +47,7 @@
- id: commit_and_push
type: io.kestra.plugin.git.PushNamespaceFiles
namespace: dev
files:
- "*" # optional list of Regex strings; by default, all files are pushed
files: "*" # optional list of glob patterns; by default, all files are pushed
gitDirectory: _files # optional path in Git where Namespace Files should be pushed
url: https://github.com/kestra-io/scripts # required string
username: git_username # required string needed for Auth with Git
Expand Down Expand Up @@ -101,9 +102,9 @@ public class PushNamespaceFiles extends AbstractPushTask<PushNamespaceFiles.Outp
description = """
By default, Kestra will push all Namespace Files from the specified namespace.
If you want to push only a specific file or directory e.g. myfile.py, you can set it explicitly using files: myfile.py.
Given that this is a Regex string (or a list of Regex strings), you can include as many files as you wish, provided that the user is authorized to access that namespace.
Note that each regex try to match the file name OR the relative path starting from `gitDirectory`""",
defaultValue = ".*"
Given that this is a glob pattern string (or a list of glob patterns), you can include as many files as you wish, provided that the user is authorized to access that namespace.
Note that each glob pattern try to match the file name OR the relative path starting from `gitDirectory`""",
defaultValue = "**"

)
@PluginProperty(dynamic = true)
Expand All @@ -119,7 +120,7 @@ public String getCommitMessage() {
}

@Override
public Object regexes() {
public Object globs() {
return this.files;
}

Expand All @@ -129,24 +130,26 @@ public String fetchedNamespace() {
}

@Override
protected Map<Path, Supplier<InputStream>> instanceResourcesContentByPath(RunContext runContext, Path baseDirectory, List<String> regexes) throws Exception {
protected Map<Path, Supplier<InputStream>> instanceResourcesContentByPath(RunContext runContext, Path baseDirectory, List<String> globs) throws Exception {
NamespaceFilesService namespaceFilesService = runContext.getApplicationContext().getBean(NamespaceFilesService.class);

String tenantId = runContext.tenantId();
String renderedNamespace = runContext.render(this.namespace);
Stream<URI> pathsStream = namespaceFilesService.recursiveList(
Stream<URI> uriStream = namespaceFilesService.recursiveList(
tenantId,
renderedNamespace,
null
).stream();
if (regexes != null) {
pathsStream = pathsStream.filter(path ->
regexes.stream().anyMatch(path.getPath()::matches)
|| regexes.stream().anyMatch(Path.of(path.getPath()).getFileName().toString()::matches)
);
if (globs != null) {
List<PathMatcher> matchers = globs.stream().map(glob -> FileSystems.getDefault().getPathMatcher("glob:" + glob)).toList();
uriStream = uriStream.filter(uri ->
{
Path path = Path.of(uri.getPath());
return matchers.stream().anyMatch(matcher -> matcher.matches(path) || matcher.matches(path.getFileName()));
});
}

return pathsStream.map(uri -> {
return uriStream.map(uri -> {
String path = uri.toString();
return path.startsWith("/") ? path.substring(1) : path;
}).collect(Collectors.toMap(
Expand Down
8 changes: 4 additions & 4 deletions src/test/java/io/kestra/plugin/git/PushFlowsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ void defaultCase_SingleRegex() throws Exception {
.authorName("{{name}}")
.sourceNamespace("{{sourceNamespace}}")
.targetNamespace("{{targetNamespace}}")
.flows("second.*")
.flows("second*")
.includeChildNamespaces(true)
.gitDirectory("{{gitDirectory}}")
.build();
Expand Down Expand Up @@ -175,7 +175,7 @@ void defaultCase_SingleRegexDryRun() throws Exception {
.authorName("{{name}}")
.sourceNamespace("{{sourceNamespace}}")
.targetNamespace("{{targetNamespace}}")
.flows("second.*")
.flows("second*")
.includeChildNamespaces(true)
.gitDirectory("{{gitDirectory}}")
.dryRun(true)
Expand Down Expand Up @@ -271,7 +271,7 @@ void defaultCase_SingleRegex_DeleteScopedToRegex() throws Exception {

flowRepositoryInterface.delete(deletedFlowOnSecondPush);
pushOutput = pushFlows.toBuilder()
.flows("second.*")
.flows("second*")
.build().run(runContext(tenantId, url, authorEmail, authorName, branch, sourceNamespace, targetNamespace, gitDirectory));

cloneOutput = clone.run(runContextFactory.of());
Expand Down Expand Up @@ -409,7 +409,7 @@ void defaultCase_MultipleRegex() throws Exception {
.authorName("{{name}}")
.sourceNamespace("{{sourceNamespace}}")
.targetNamespace("{{targetNamespace}}")
.flows(List.of("first.*", "second.*"))
.flows(List.of("first*", "second*"))
.includeChildNamespaces(true)
.gitDirectory("{{gitDirectory}}")
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ void defaultCase_SingleRegex() throws Exception {
.authorEmail("{{email}}")
.authorName("{{name}}")
.namespace("{{namespace}}")
.files("second.*")
.files("second*")
.gitDirectory("{{gitDirectory}}")
.build();

Expand Down Expand Up @@ -170,7 +170,7 @@ void defaultCase_SingleRegexDryRun() throws Exception {
.authorEmail("{{email}}")
.authorName("{{name}}")
.namespace("{{namespace}}")
.files("second.*")
.files("second*")
.gitDirectory("{{gitDirectory}}")
.dryRun(true)
.build();
Expand Down Expand Up @@ -267,7 +267,7 @@ void defaultCase_SingleRegex_DeleteScopedToRegex() throws Exception {

storage.delete(tenantId, toDeleteURI);
pushOutput = pushNamespaceFiles.toBuilder()
.files("second.*")
.files("second*")
.build().run(runContext(tenantId, url, authorEmail, authorName, branch, namespace, gitDirectory));

cloneOutput = clone.run(runContextFactory.of());
Expand Down Expand Up @@ -407,7 +407,7 @@ void defaultCase_MultipleRegex() throws Exception {
.authorEmail("{{email}}")
.authorName("{{name}}")
.namespace("{{namespace}}")
.files(List.of("first.*", "second.*"))
.files(List.of("first*", "second*"))
.gitDirectory("{{gitDirectory}}")
.build();

Expand Down

0 comments on commit c34d213

Please sign in to comment.