generated from kestra-io/plugin-template
-
Notifications
You must be signed in to change notification settings - Fork 2
feat: Add GitLab plugin #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
fdelbrayelle
merged 10 commits into
kestra-io:master
from
bishalbera:feat-implement-gitlab-plugin
Jul 30, 2025
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
3eea6ec
feat: implemeted gitlab plugin with create and search issues task andβ¦
bishalbera 47fbc87
fix: replaced pluginProperty with Property, added task icons,added stβ¦
bishalbera 1d377c8
Update build.gradle
bishalbera d741973
Update build.gradle
bishalbera 636c6d2
fix: fixed formatting issues, removed unnecessary null check
bishalbera 3618c73
Update src/main/java/io/kestra/plugin/gitlab/AbstractGitLabTask.java
bishalbera 590f02f
Update src/main/java/io/kestra/plugin/gitlab/AbstractGitLabTask.java
bishalbera da8d9d8
Update src/main/java/io/kestra/plugin/gitlab/issues/Search.java
bishalbera c43b871
fix: used proper example url in mergerequest task
bishalbera bee1c9b
fix: used Property.ofValue() insted of Property.of as its deprecated
bishalbera File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| # for dev purposes only | ||
| FROM kestra/kestra:latest | ||
|
|
||
| # COPY build/libs/* /app/plugins/ # this is already handled in docker-compose.yml | ||
| COPY build/libs/* /app/plugins/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| rootProject.name = 'plugin-template' | ||
| rootProject.name = 'plugin-gitlab' |
62 changes: 62 additions & 0 deletions
62
src/main/java/io/kestra/plugin/gitlab/AbstractGitLabTask.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| package io.kestra.plugin.gitlab; | ||
|
|
||
| import io.kestra.core.exceptions.IllegalVariableEvaluationException; | ||
| import io.kestra.core.http.HttpRequest; | ||
| import io.kestra.core.http.client.HttpClient; | ||
| import io.kestra.core.http.client.configurations.HttpConfiguration; | ||
| import io.kestra.core.models.property.Property; | ||
| import io.kestra.core.models.tasks.Task; | ||
| import io.kestra.core.runners.RunContext; | ||
| import io.swagger.v3.oas.annotations.media.Schema; | ||
| import jakarta.validation.constraints.NotNull; | ||
| import lombok.*; | ||
| import lombok.experimental.SuperBuilder; | ||
|
|
||
| import java.net.URI; | ||
|
|
||
| @SuperBuilder | ||
| @ToString | ||
| @EqualsAndHashCode | ||
| @Getter | ||
| @NoArgsConstructor | ||
| public abstract class AbstractGitLabTask extends Task { | ||
|
|
||
| @Schema(title = "GitLab URL", description = "GitLab URL") | ||
| @Builder.Default | ||
| private Property<String> url = Property.ofValue("https://gitlab.com"); | ||
|
|
||
| @Schema(title = "Personal Access Token", description = "GitLab Personal Access Token") | ||
| @NotNull | ||
| private Property<String> token; | ||
|
|
||
| @Schema(title = "Project ID", description = "GitLab project ID") | ||
| @NotNull | ||
| private Property<String> projectId; | ||
|
|
||
| @Schema(title = "API Path", description = "Custom API path for GitLab API endpoints") | ||
| @Builder.Default | ||
| private Property<String> apiPath = Property.ofValue("/api/v4/projects"); | ||
|
|
||
| protected HttpClient httpClient(RunContext runContext) throws IllegalVariableEvaluationException { | ||
|
|
||
| HttpConfiguration config = null; | ||
| return new HttpClient(runContext, config); | ||
| } | ||
|
|
||
| protected HttpRequest.HttpRequestBuilder authenticatedRequestBuilder(String endpoint, RunContext runContext) throws IllegalVariableEvaluationException { | ||
| String baseUrl = runContext.render(this.url).as(String.class).orElse("https://gitlab.com"); | ||
| String renderedToken = runContext.render(this.token).as(String.class).orElseThrow(); | ||
| String fullUrl = baseUrl + endpoint; | ||
| return HttpRequest.builder() | ||
| .uri(URI.create(fullUrl)) | ||
| .addHeader("PRIVATE-TOKEN", renderedToken) | ||
| .addHeader("Content-Type", "application/json"); | ||
| } | ||
|
|
||
| protected String buildApiEndpoint(String resource, RunContext runContext) throws IllegalVariableEvaluationException { | ||
| String renderedApiPath = runContext.render(this.apiPath).as(String.class).orElse("/api/v4/projects"); | ||
| String renderedProjectId = runContext.render(this.getProjectId()).as(String.class).orElseThrow(); | ||
| return renderedApiPath + "/" + renderedProjectId + "/" + resource; | ||
| } | ||
|
|
||
| } |
119 changes: 119 additions & 0 deletions
119
src/main/java/io/kestra/plugin/gitlab/MergeRequest.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,119 @@ | ||
| package io.kestra.plugin.gitlab; | ||
|
|
||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||
| import io.kestra.core.http.HttpRequest; | ||
| import io.kestra.core.http.HttpResponse; | ||
| import io.kestra.core.http.client.HttpClient; | ||
| import io.kestra.core.models.annotations.Example; | ||
| import io.kestra.core.models.annotations.Plugin; | ||
| import io.kestra.core.models.property.Property; | ||
| import io.kestra.core.models.tasks.RunnableTask; | ||
| import io.kestra.core.runners.RunContext; | ||
| import io.swagger.v3.oas.annotations.media.Schema; | ||
| import jakarta.validation.constraints.NotNull; | ||
| import lombok.*; | ||
| import lombok.experimental.SuperBuilder; | ||
|
|
||
| import java.nio.charset.StandardCharsets; | ||
| import java.util.HashMap; | ||
| import java.util.Map; | ||
|
|
||
| @SuperBuilder | ||
| @ToString | ||
| @EqualsAndHashCode | ||
| @Getter | ||
| @NoArgsConstructor | ||
| @Schema( | ||
| title = "Create a GitLab merge request", | ||
| description = "Create a new merger request in a GitLab project" | ||
| ) | ||
| @Plugin(examples = { | ||
| @Example( | ||
| title = "Create a merge request in a GitLab project using a project access token.", | ||
| full = true, | ||
| code = """ | ||
| id: gitlab_merge_request | ||
| namespace: company.team | ||
|
|
||
| tasks: | ||
| - id: create_merge_request | ||
| type: io.kestra.plugin.gitlab.MergeRequest | ||
| url: https://gitlab.example.com | ||
| token: "{{ secret('GITLAB_TOKEN') }}" | ||
| projectId: "123" | ||
| title: "Feature: Add new functionality" | ||
| mergeRequestDescription: "This merge request adds new functionality to the project" | ||
| sourceBranch: "feat-testing" | ||
| targetBranch: "main" | ||
| """ | ||
| ) | ||
| }) | ||
| public class MergeRequest extends AbstractGitLabTask implements RunnableTask<MergeRequest.Output> { | ||
|
|
||
| @Schema(title = "Merge request title") | ||
| @NotNull | ||
| private Property<String> title; | ||
|
|
||
| @Schema(title = "Source branch") | ||
| @NotNull | ||
| private Property<String> sourceBranch; | ||
|
|
||
| @Schema(title = "Target branch") | ||
| @NotNull | ||
| private Property<String> targetBranch; | ||
|
|
||
| @Schema(title = "Merge request description") | ||
| private Property<String> mergeRequestDescription; | ||
|
|
||
| @Override | ||
| public Output run(RunContext runContext) throws Exception { | ||
| try (HttpClient client = httpClient(runContext)) { | ||
|
|
||
| Map<String, Object> body = new HashMap<>(); | ||
|
|
||
| // Required fields for merge request creation | ||
| body.put("title", runContext.render(this.title).as(String.class).orElseThrow()); | ||
|
|
||
| body.put("source_branch", runContext.render(this.sourceBranch).as(String.class).orElseThrow()); | ||
|
|
||
| body.put("target_branch", runContext.render(this.targetBranch).as(String.class).orElseThrow()); | ||
|
|
||
| // Optional fields | ||
| if (this.mergeRequestDescription != null) { | ||
| body.put("description", runContext.render(this.mergeRequestDescription).as(String.class).orElseThrow()); | ||
| } | ||
|
|
||
| ObjectMapper mapper = new ObjectMapper(); | ||
| String jsonBody = mapper.writeValueAsString(body); | ||
| String endpoint = buildApiEndpoint("merge_requests", runContext); | ||
|
|
||
| HttpRequest request = authenticatedRequestBuilder(endpoint, runContext) | ||
| .method("POST") | ||
| .body(new HttpRequest.StringRequestBody("application/json", StandardCharsets.UTF_8, jsonBody)) | ||
| .build(); | ||
|
|
||
| HttpResponse<Map> response = client.request(request, Map.class); | ||
| Map<String, Object> result = response.getBody(); | ||
|
|
||
| return Output.builder() | ||
| .mergeReqID(result.get("id").toString()) | ||
| .webUrl(result.get("web_url").toString()) | ||
| .statusCode(response.getStatus().getCode()) | ||
| .build(); | ||
| } | ||
| } | ||
|
|
||
| @Builder | ||
| @Getter | ||
| public static class Output implements io.kestra.core.models.tasks.Output { | ||
| @Schema(title = "Created merge request ID") | ||
| private String mergeReqID; | ||
|
|
||
| @Schema(title = "web URL") | ||
| private String webUrl; | ||
|
|
||
| @Schema(title = "HTTP status code") | ||
| private Integer statusCode; | ||
| } | ||
|
|
||
| } |
132 changes: 132 additions & 0 deletions
132
src/main/java/io/kestra/plugin/gitlab/issues/Create.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,132 @@ | ||
| package io.kestra.plugin.gitlab.issues; | ||
|
|
||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||
| import io.kestra.core.http.HttpRequest; | ||
| import io.kestra.core.http.HttpResponse; | ||
| import io.kestra.core.http.client.HttpClient; | ||
| import io.kestra.core.models.annotations.Example; | ||
| import io.kestra.core.models.annotations.Plugin; | ||
| import io.kestra.core.models.property.Property; | ||
| import io.kestra.core.models.tasks.RunnableTask; | ||
| import io.kestra.core.runners.RunContext; | ||
| import io.kestra.plugin.gitlab.AbstractGitLabTask; | ||
| import io.swagger.v3.oas.annotations.media.Schema; | ||
| import jakarta.validation.constraints.NotNull; | ||
| import lombok.*; | ||
| import lombok.experimental.SuperBuilder; | ||
|
|
||
| import java.nio.charset.StandardCharsets; | ||
| import java.util.HashMap; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
|
|
||
| @SuperBuilder | ||
| @ToString | ||
| @EqualsAndHashCode | ||
| @Getter | ||
| @NoArgsConstructor | ||
| @Schema( | ||
| title = "Create a GitLab issue", | ||
| description = "Create a new issue in a GitLab project" | ||
| ) | ||
| @Plugin(examples = { | ||
| @Example( | ||
| title = "Create an issue in a GitLab project using a project access token.", | ||
| full = true, | ||
| code = """ | ||
| id: gitlab_create_issue | ||
| namespace: company.team | ||
|
|
||
| tasks: | ||
| - id: create_issue | ||
| type: io.kestra.plugin.gitlab.issues.Create | ||
| url: https://gitlab.example.com | ||
| token: "{{ secret('GITLAB_TOKEN') }}" | ||
| projectId: "123" | ||
| title: "Bug report" | ||
| issueDescription: "Found a critical bug" | ||
| labels: | ||
| - bug | ||
| - critical | ||
| """ | ||
| ), | ||
| @Example( | ||
| title = "Create an issue with custom API path for self-hosted GitLab.", | ||
| full = true, | ||
| code = """ | ||
| id: gitlab_create_issue_custom | ||
| namespace: company.team | ||
|
|
||
| tasks: | ||
| - id: create_issue | ||
| type: io.kestra.plugin.gitlab.issues.Create | ||
| url: https://gitlab.example.com | ||
| apiPath: /api/v4/projects | ||
| token: "{{ secret('GITLAB_TOKEN') }}" | ||
| projectId: "123" | ||
| title: "Bug report" | ||
| issueDescription: "Found a critical bug" | ||
| """ | ||
| ) | ||
| }) | ||
| public class Create extends AbstractGitLabTask implements RunnableTask<Create.Output> { | ||
|
|
||
| @Schema(title = "Issue title") | ||
| @NotNull | ||
| private Property<String> title; | ||
|
|
||
| @Schema(title = "Issue description") | ||
| private Property<String> issueDescription; | ||
|
|
||
| @Schema(title = "Labels to assign to the issue") | ||
| private Property<List<String>> labels; | ||
|
|
||
| @Override | ||
| public Output run(RunContext runContext) throws Exception { | ||
| try (HttpClient client = httpClient(runContext)) { | ||
|
|
||
| Map<String, Object> body = new HashMap<>(); | ||
| body.put("title", runContext.render(this.title).as(String.class).orElseThrow()); | ||
| if (this.issueDescription != null) { | ||
| body.put("description", runContext.render(this.issueDescription).as(String.class).orElseThrow()); | ||
| } | ||
| if (this.labels != null) { | ||
| List<String> renderedLabels = runContext.render(this.labels).asList(String.class); | ||
| body.put("labels", renderedLabels); | ||
| } | ||
| ObjectMapper mapper = new ObjectMapper(); | ||
| String jsonBody = mapper.writeValueAsString(body); | ||
| String endpoint = buildApiEndpoint("issues", runContext); | ||
|
|
||
| HttpRequest request = authenticatedRequestBuilder(endpoint, runContext) | ||
| .method("POST") | ||
| .body(new HttpRequest.StringRequestBody("application/json", | ||
| StandardCharsets.UTF_8, | ||
| jsonBody)) | ||
| .build(); | ||
|
|
||
| HttpResponse<Map> response = client.request(request, Map.class); | ||
|
|
||
| Map<String, Object> result = response.getBody(); | ||
|
|
||
| return Output.builder() | ||
| .issueId(result.get("id").toString()) | ||
| .webUrl(result.get("web_url").toString()) | ||
| .statusCode(response.getStatus().getCode()) | ||
| .build(); | ||
| } | ||
| } | ||
|
|
||
| @Builder | ||
| @Getter | ||
| public static class Output implements io.kestra.core.models.tasks.Output { | ||
| @Schema(title = "Created issue ID") | ||
| private String issueId; | ||
|
|
||
| @Schema(title = "Issue web URL") | ||
| private String webUrl; | ||
|
|
||
| @Schema(title = "HTTP status code") | ||
| private Integer statusCode; | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.