Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/main/java/io/kestra/plugin/git/AbstractCloningTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
@Getter
public abstract class AbstractCloningTask extends AbstractGitTask {
@Schema(
title = "Whether to clone submodules"
title = "Clone submodules",
description = "Default false; enable to fetch and initialize nested submodules."
)
protected Property<Boolean> cloneSubmodules;

Expand Down Expand Up @@ -85,4 +86,4 @@ protected void checkoutBranch(Git git, String branch, Logger logger) throws Exce
logger.info("Checked out branch {}", branch);
}
}
}
}
52 changes: 34 additions & 18 deletions src/main/java/io/kestra/plugin/git/AbstractGitTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,52 +56,68 @@ public abstract class AbstractGitTask extends Task {
private static final AtomicReference<String> SSL_CONFIGURED_KEY = new AtomicReference<>(null);
private static final Object SSL_CONFIG_LOCK = new Object();

@Schema(title = "The URI to clone from")
@Schema(
title = "Repository URL",
description = "HTTP(S) or SSH URI used for clone and push operations."
)
protected Property<String> url;

@Schema(title = "The username or organization")
@Schema(
title = "Username or organization",
description = "Used for HTTP basic authentication and as a fallback commit author."
)
protected Property<String> username;

@Schema(title = "The password or Personal Access Token (PAT) – when you authenticate the task with a PAT, any flows or files pushed to Git from Kestra will be pushed from the user associated with that PAT. This way, you don't need to configure the commit author (the `authorName` and `authorEmail` properties).")
@Schema(
title = "Password or personal access token",
description = "Supplies HTTP credentials. When a PAT is used, pushes are recorded under that PAT’s user without needing `authorName` and `authorEmail`."
)
protected Property<String> password;

@Schema(
title = "PEM-format private key content that is paired with a public key registered on Git",
description = "To generate an ECDSA PEM format key from OpenSSH, use the following command: `ssh-keygen -t ecdsa -b 256 -m PEM`. " +
"You can then set this property with your private key content and put your public key on Git."
title = "PEM private key",
description = "PEM-formatted private key matching a public key registered on the Git server. Generate with `ssh-keygen -t ecdsa -b 256 -m PEM`."
)
protected Property<String> privateKey;

@Schema(title = "The passphrase for the `privateKey`")
@Schema(title = "Passphrase for `privateKey`")
protected Property<String> passphrase;

@Schema(
title = "Optional path to a PEM-encoded CA certificate to trust (in addition to the JVM default truststore)",
description = "Equivalent to `git config http.sslCAInfo <path>`. Use this for self-signed/internal CAs."
title = "Extra trusted CA PEM path",
description = "Optional PEM-encoded CA bundle added to the JVM truststore; equivalent to `git config http.sslCAInfo <path>` for self-signed or internal CAs."
)
protected Property<String> trustedCaPemPath;

@Schema(title = "Specify whether to disable proxy.")
@Schema(
title = "Disable proxy for HTTP",
description = "When true, forces direct connections instead of using the JVM proxy settings."
)
protected Property<Boolean> noProxy;

@Schema(title = "The initial Git branch")
@Schema(title = "Initial Git branch")
public abstract Property<String> getBranch();

@Schema(title = "Connect timeout in milliseconds for HTTP connections.")
@Schema(
title = "HTTP connect timeout (ms)",
description = "Default 10000 ms."
)
@Builder.Default
protected Property<Integer> connectTimeout = Property.ofValue(10000);

@Schema(title = "Read timeout in milliseconds for HTTP connections.")
@Schema(
title = "HTTP read timeout (ms)",
description = "Default 60000 ms."
)
@Builder.Default
protected Property<Integer> readTimeout = Property.ofValue(60000);

@Schema(
title = "Git configuration to apply to the repository",
title = "Git configuration overrides",
description = """
Map of Git config keys and values, applied after clone
few examples:
- 'core.fileMode': false -> ignore file permission changes
- 'core.autocrlf': false -> prevent line ending conversion
Map of git config keys and values applied after clone, e.g.:
- core.fileMode: false (ignore permission flips)
- core.autocrlf: false (preserve line endings)
"""
)
protected Property<Map<String, Object>> gitConfig;
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/io/kestra/plugin/git/AbstractKestraTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public abstract class AbstractKestraTask extends AbstractGitTask {
@Schema(title = "Kestra API URL. If null, uses 'kestra.url' from [configuration](https://kestra.io/docs/configuration#kestra-url). If that is also null, defaults to 'http://localhost:8080'.")
private Property<String> kestraUrl;

@Schema(title = "Authentication information.")
@Schema(title = "Authentication information")
@NotNull
private Auth auth;

Expand Down
16 changes: 8 additions & 8 deletions src/main/java/io/kestra/plugin/git/AbstractPushTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,27 +65,27 @@ public abstract class AbstractPushTask<O extends AbstractPushTask.Output> extend
protected Property<String> commitMessage;

@Schema(
title = "If `true`, the task will only output modifications without pushing any file to Git yet. If `false` (default), all listed files will be pushed to Git immediately."
title = "Dry run only",
description = "When true, writes a diff file without pushing. Default false pushes immediately."
)
@Builder.Default
private Property<Boolean> dryRun = Property.ofValue(false);

@Schema(
title = "The commit author email",
description = "If null, no author will be set on this commit."
title = "Commit author email",
description = "If null, no author is set."
)
private Property<String> authorEmail;

@Schema(
title = "The commit author name",
description = "If null, the username will be used instead.",
defaultValue = "`username`"
title = "Commit author name",
description = "Defaults to `username` when empty."
)
private Property<String> authorName;

@Schema(
title = "Whether to delete flows/files from Git that are no longer present in Kestra",
description = "If `true` (default), files present in Git but not in Kestra will be deleted from the Git repository."
title = "Delete removed resources",
description = "If true (default), removes Git files that no longer exist in Kestra."
)
@Builder.Default
private Property<Boolean> delete = Property.ofValue(true);
Expand Down
8 changes: 5 additions & 3 deletions src/main/java/io/kestra/plugin/git/AbstractSyncTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,15 @@
public abstract class AbstractSyncTask<T, O extends AbstractSyncTask.Output> extends AbstractCloningTask implements RunnableTask<O> {

@Schema(
title = "If `true`, the task will only output modifications without performing any modification to Kestra. If `false` (default), all listed modifications will be applied."
title = "Dry run only",
description = "When true, writes a diff without applying changes to Kestra."
)
@Builder.Default
private Property<Boolean> dryRun = Property.ofValue(false);

@Schema(
title = "If `true` (default), the task will fail if the specified directory doesn't exist. If `false`, missing directories will be skipped."
title = "Fail if git directory missing",
description = "Default true. If false, skips when the rendered `gitDirectory` path does not exist."
)
@Builder.Default
private Property<Boolean> failOnMissingDirectory = Property.ofValue(true);
Expand Down Expand Up @@ -264,4 +266,4 @@ public enum SyncState {
UPDATED,
UNCHANGED
}
}
}
25 changes: 16 additions & 9 deletions src/main/java/io/kestra/plugin/git/Clone.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
@Getter
@NoArgsConstructor
@Schema(
title = "Clone a Git repository."
title = "Clone a Git repository",
description = "Clones a repository over HTTP(S) or SSH, optionally checking out a branch, tag, or commit. Defaults to a shallow clone (depth 1) unless a tag or commit is requested; set `cloneSubmodules` to fetch submodules."
)
@Plugin(
examples = {
Expand Down Expand Up @@ -122,28 +123,34 @@
)
public class Clone extends AbstractCloningTask implements RunnableTask<Clone.Output> {
@Schema(
title = "The optional directory associated with the clone operation",
description = "If the directory isn't set, the current directory will be used."
title = "Target directory",
description = "Subdirectory under the working directory where the repo is cloned; defaults to the working directory root."
)
private Property<String> directory;

@Schema(
title = "The branch to checkout – ignored if \"commit\" is provided."
title = "Branch to checkout",
description = "Used only when no commit or tag is specified."
)
private Property<String> branch;

@Schema(
title = "Creates a shallow clone with a history truncated to the specified number of commits.\nIgnored when `commit` is provided to guarantee the commit is available.")
title = "Shallow clone depth",
description = "Defaults to 1. Ignored when `commit` or `tag` is set to ensure history is available."
)
@Builder.Default
private Property<Integer> depth = Property.ofValue(1);

@Schema(
title = "Commit SHA1 to checkout (detached HEAD) – works also with a shortened SHA1.",
description = "If set, the repository is cloned and the specified commit is checked out. This takes precedence over `branch` and disables shallow cloning to ensure the commit is present."
title = "Commit SHA to checkout",
description = "Detached HEAD checkout; short SHA allowed. Overrides `branch` and disables shallow clone."
)
private Property<String> commit;

@Schema(title = "Tag to checkout – ignored if `commit` is provided.")
@Schema(
title = "Tag to checkout",
description = "Ignored when `commit` is set; performs a full fetch to reach the tag."
)
private Property<String> tag;

@Override
Expand Down Expand Up @@ -220,7 +227,7 @@ public Property<String> getUrl() {
@Getter
public static class Output implements io.kestra.core.models.tasks.Output {
@Schema(
title = "The path where the repository is cloned"
title = "Path where the repository is cloned"
)
private final String directory;
}
Expand Down
67 changes: 41 additions & 26 deletions src/main/java/io/kestra/plugin/git/NamespaceSync.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@
@Getter
@NoArgsConstructor
@Schema(
title = "Unidirectional namespace sync between Kestra and Git.",
description = "Create/update is driven by 'sourceOfTruth'; delete/keep/fail is driven by 'whenMissingInSource'."
title = "Sync a namespace with Git",
description = "Syncs flows and Namespace Files for a single namespace between Git and Kestra. Direction is controlled by `sourceOfTruth`; deletions follow `whenMissingInSource` and respect `protectedNamespaces`. Supports dry-run diff output and optional subdirectory via `gitDirectory`."
)
@Plugin(
priority = Plugin.Priority.SECONDARY,
Expand Down Expand Up @@ -116,58 +116,73 @@ public enum WhenMissingInSource {DELETE, KEEP, FAIL}
public enum OnInvalidSyntax {SKIP, WARN, FAIL}

@Schema(
title = "The branch to read from / write to (required).",
description = "Branch prefixed with `origin/` or `refs/heads/` are not supported."
title = "Branch to sync",
description = "Required branch name (no `origin/` or `refs/heads/` prefixes); must exist on the remote."
)
@NotNull
private Property<String> branch;

@Schema(
title = "Subdirectory inside the repo used to store Kestra code and files; if empty, repo root is used.",
description = """
This is the base folder in your Git repository where Kestra will look for code and files.
If you don't set it, the repo root will be used. Inside that folder, Kestra always expects
a structure like <namespace>/flows, <namespace>/files, etc.

| gitDirectory | namespace | Expected Git path |
| ------------ | --------------- | -----------------------------------------|
| (not set) | company | company/flows/my-flow.yaml |
| monorepo | system | monorepo/system/flows/my-flow.yaml |
| projectA | company.team | projectA/company.team/flows/my-flow.yaml |"""
title = "Git base directory",
description = "Optional subfolder in the repo; default is repo root. Within it, files are expected under `<namespace>/flows` and `<namespace>/files`."
)
private Property<String> gitDirectory;

@Schema(title = "Target namespace to sync (required).")
@Schema(
title = "Namespace to sync",
description = "Required; syncs only this namespace (no child namespaces)."
)
@NotNull
private Property<String> namespace;

@Schema(title = "Select the source of truth.")
@Schema(
title = "Source of truth",
description = "KESTRA (default) pushes Kestra state to Git; GIT applies Git state into Kestra."
)
@Builder.Default
private Property<SourceOfTruth> sourceOfTruth = Property.ofValue(SourceOfTruth.KESTRA);

@Schema(title = "Behavior when an object is missing from the selected source of truth.")
@Schema(
title = "Handling when missing in source",
description = "Default DELETE. Options: DELETE removes from target, KEEP leaves untouched, FAIL stops the run. Protected namespaces override deletions."
)
@Builder.Default
private Property<WhenMissingInSource> whenMissingInSource = Property.ofValue(WhenMissingInSource.DELETE);

@Schema(title = "Namespaces protected from deletion regardless of policies.")
@Schema(
title = "Protected namespaces",
description = "List that cannot be deleted even when policy is DELETE; defaults to `system`."
)
@Builder.Default
private Property<List<String>> protectedNamespaces = Property.ofValue(List.of("system"));

@Schema(title = "If true, only compute the plan and output a diff without applying changes.")
@Schema(
title = "Dry run only",
description = "When true, produces a diff file without applying changes or pushing."
)
@Builder.Default
private Property<Boolean> dryRun = Property.ofValue(false);

@Schema(title = "Behavior when encountering invalid syntax while syncing.")
@Schema(
title = "On invalid syntax",
description = "Default FAIL. Options: SKIP, WARN, FAIL."
)
@Builder.Default
private Property<OnInvalidSyntax> onInvalidSyntax = Property.ofValue(OnInvalidSyntax.FAIL);

@Schema(title = "Git commit message when pushing back to Git.")
@Schema(
title = "Git commit message",
description = "Used when committing back to Git; defaults to \"Namespace sync from Kestra\"."
)
private Property<String> commitMessage;

@Schema(title = "The commit author email.")
@Schema(title = "Commit author email")
private Property<String> authorEmail;

@Schema(title = "The commit author name (defaults to username if null).")
@Schema(
title = "Commit author name",
description = "Defaults to `username` when not set."
)
private Property<String> authorName;

// Directory names (namespace-first structure: <namespace>/<kind>/<id>.yaml)
Expand Down Expand Up @@ -720,11 +735,11 @@ public static class Output implements io.kestra.core.models.tasks.Output {
@Schema(title = "A file containing all changes applied (or not in case of dry run) to/from Git.")
private URI diff;

@Schema(title = "ID of the commit pushed (if any).")
@Schema(title = "ID of the commit pushed (if any)")
@Nullable
private String commitId;

@Schema(title = "URL to the commit (if any).")
@Schema(title = "URL to the commit (if any)")
@Nullable
private String commitURL;
}
Expand Down
Loading
Loading