Repository navigation
Conversation
Add GitLab as a first-class TaskSpawner source: spec.when.gitlab polls a project for issues and merge requests with label, state, comment-command, pipelineStatus and reviewState gates and reports Task status as notes; spec.when.gitlabWebhook spawns Tasks from GitLab webhooks via a new kelos-webhook-gitlab server or a WebhookGateway with spec.gitlab. Introduce Workspace.spec.provider (github|gitlab) so the secret key, agent credentials, git username and preconfigured CLI follow the git host. GitLab workspaces require a GITLAB_TOKEN key and get glab installed and configured in all agent images. GitLab fields live in v1alpha2 only and are preserved across v1alpha1 round-trips. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
There was a problem hiding this comment.
Review completed against the latest diff
Tip: cubic used a learning from your PR history. Let your coding agent read cubic learnings directly with the cubic MCP.
Re-trigger cubic
There was a problem hiding this comment.
All reported issues were addressed across 86 files (changes from recent commits).
Requires human review: Auto-approval blocked because this review re-detected 15 unresolved issues already reported by Cubic.
Re-trigger cubic
…ity, more careful orchestration of providers to make them look-alike
|
TODO for myself - validate webhook based setup for Gitlab, I would like to run the changed code for couple days to verify |
…t gitlab (and bitbucket) with unified approach
There was a problem hiding this comment.
8 issues found across 75 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="internal/source/comment_policy.go">
<violation number="1" location="internal/source/comment_policy.go:48">
P2: When `excludeComments` contains an empty string, every empty body or blank line matches it as an exclude command, so the source can block all items unexpectedly. Ignore empty commands or reject them with API validation before evaluating the policy.</violation>
</file>
<file name="internal/webhook/provider_generic.go">
<violation number="1" location="internal/webhook/provider_generic.go:24">
P2: When multiple generic TaskSpawners share a source, this can deduplicate using the wrong identity. If the first spawner's `id` path is absent but a later spawner's path resolves, retries with equivalent JSON but different formatting use different body hashes and create duplicate Tasks; derive delivery identity per matched spawner or enforce one shared mapping per source.</violation>
</file>
<file name="api/v1alpha2/taskspawner_tracker.go">
<violation number="1" location="api/v1alpha2/taskspawner_tracker.go:15">
P3: For webhook sources, an empty `Repository` or `Project` means unrestricted delivery, not the Workspace repository. Clarify this contract so future callers do not treat an empty webhook restriction as a workspace fallback.</violation>
<violation number="2" location="api/v1alpha2/taskspawner_tracker.go:41">
P1: When a `When` object contains both `jira` and `gitlab`, this switch returns GitLab while the spawner constructs the Jira source, causing provider validation and reporting to target the wrong system. Reject multiple non-nil source fields before selecting a tracker, or add one-of validation to `When`.</violation>
</file>
<file name="internal/webhook/provider_linear.go">
<violation number="1" location="internal/webhook/provider_linear.go:51">
P2: When a Comment delivery reaches an Issue-only spawner with an unscoped label filter, this condition still performs a Linear API lookup even though the spawner cannot match Comment events. Include the parent `LinearWebhook.Types` in the enrichment decision so only spawners that listen for Comment can trigger the fetch.</violation>
</file>
<file name="gemini/Dockerfile">
<violation number="1" location="gemini/Dockerfile:34">
P3: `grep "${DEB}"` treats ${DEB} as a basic regex (dots match any char) and does substring matching, so it can match unintended lines in the checksums file. If more than one line matches, `sha256sum -c -` tries to verify files that are not present in /tmp and the build fails; if none matches, empty stdin makes sha256sum abort. Anchor to the exact filename with a fixed-string match: `grep -Fx "${DEB}" glab-checksums.txt | sha256sum -c -`.</violation>
</file>
<file name="cursor/Dockerfile">
<violation number="1" location="cursor/Dockerfile:34">
P3: `grep "${DEB}"` is an unanchored substring (and regex — dots are unescaped) match. If the GitLab checksums.txt ever lists a sibling asset whose name embeds the DEB string (e.g. a `.deb.asc`/`.deb.sha256` entry), grep matches multiple lines and `sha256sum -c -` then checks every matched file; any matched-but-absent file fails the build where the previous `--ignore-missing -c` variant would have passed. Anchor the match to the end-of-line filename (and treat it as a fixed string) so only the exact downloaded DEB is verified.</violation>
</file>
<file name="internal/controller/taskspawner_deployment_builder.go">
<violation number="1" location="internal/controller/taskspawner_deployment_builder.go:63">
P2: For a `spec.when.gitlabWebhook` spawner, Tracker() reports Provider == GitLab while `ts.Spec.When.GitLab` is nil, so `gitLabSourceArgs(ts.Spec.When.GitLab, ...)` dereferences a nil pointer (gl.BaseURL/gl.Project). The condition was broadened from `ts.Spec.When.GitLab != nil` to the provider check, which no longer guarantees the field is set. Webhook spawners currently route around Build(), so this is latent, but keep the check and the dereference consistent: branch on `ts.Spec.When.GitLab != nil` (or guard the nil) rather than the GitLab provider value.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
| // webhook, slack) or when no source is set. | ||
| func (w When) Tracker() (TrackerSource, bool) { | ||
| switch { | ||
| case w.GitHubIssues != nil: |
There was a problem hiding this comment.
P1: When a When object contains both jira and gitlab, this switch returns GitLab while the spawner constructs the Jira source, causing provider validation and reporting to target the wrong system. Reject multiple non-nil source fields before selecting a tracker, or add one-of validation to When.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At api/v1alpha2/taskspawner_tracker.go, line 41:
<comment>When a `When` object contains both `jira` and `gitlab`, this switch returns GitLab while the spawner constructs the Jira source, causing provider validation and reporting to target the wrong system. Reject multiple non-nil source fields before selecting a tracker, or add one-of validation to `When`.</comment>
<file context>
@@ -0,0 +1,90 @@
+// webhook, slack) or when no source is set.
+func (w When) Tracker() (TrackerSource, bool) {
+ switch {
+ case w.GitHubIssues != nil:
+ return githubTracker(w.GitHubIssues.Repo, w.GitHubIssues.PriorityLabels, w.GitHubIssues.Reporting, false), true
+ case w.GitHubPullRequests != nil:
</file context>
| } | ||
|
|
||
| bodyHasTrigger := cmds.Trigger != "" && containsCommand(body, cmds.Trigger) | ||
| bodyHasExclude := len(cmds.Excludes) > 0 && containsAnyCommand(body, cmds.Excludes) |
There was a problem hiding this comment.
P2: When excludeComments contains an empty string, every empty body or blank line matches it as an exclude command, so the source can block all items unexpectedly. Ignore empty commands or reject them with API validation before evaluating the policy.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At internal/source/comment_policy.go, line 48:
<comment>When `excludeComments` contains an empty string, every empty body or blank line matches it as an exclude command, so the source can block all items unexpectedly. Ignore empty commands or reject them with API validation before evaluating the policy.</comment>
<file context>
@@ -0,0 +1,191 @@
+ }
+
+ bodyHasTrigger := cmds.Trigger != "" && containsCommand(body, cmds.Trigger)
+ bodyHasExclude := len(cmds.Excludes) > 0 && containsAnyCommand(body, cmds.Excludes)
+ var bodyMatches bodyMatch
+ if bodyHasTrigger || bodyHasExclude {
</file context>
| if err != nil { | ||
| return "", "", &httpError{status: http.StatusBadRequest, message: err.Error()} | ||
| } | ||
| return sourceName, extractGenericDeliveryID(sourceName, body, spawners()), nil |
There was a problem hiding this comment.
P2: When multiple generic TaskSpawners share a source, this can deduplicate using the wrong identity. If the first spawner's id path is absent but a later spawner's path resolves, retries with equivalent JSON but different formatting use different body hashes and create duplicate Tasks; derive delivery identity per matched spawner or enforce one shared mapping per source.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At internal/webhook/provider_generic.go, line 24:
<comment>When multiple generic TaskSpawners share a source, this can deduplicate using the wrong identity. If the first spawner's `id` path is absent but a later spawner's path resolves, retries with equivalent JSON but different formatting use different body hashes and create duplicate Tasks; derive delivery identity per matched spawner or enforce one shared mapping per source.</comment>
<file context>
@@ -0,0 +1,77 @@
+ if err != nil {
+ return "", "", &httpError{status: http.StatusBadRequest, message: err.Error()}
+ }
+ return sourceName, extractGenericDeliveryID(sourceName, body, spawners()), nil
+}
+
</file context>
| // them by label, because Linear omits labels from Comment payloads. | ||
| func (linearProvider) prepare(ctx context.Context, _ *WebhookHandler, log logr.Logger, parsed *ParsedWebhook, spawners []*kelos.TaskSpawner) { | ||
| for _, spawner := range spawners { | ||
| if spawnerNeedsLinearLabels(spawner, parsed.Linear) { |
There was a problem hiding this comment.
P2: When a Comment delivery reaches an Issue-only spawner with an unscoped label filter, this condition still performs a Linear API lookup even though the spawner cannot match Comment events. Include the parent LinearWebhook.Types in the enrichment decision so only spawners that listen for Comment can trigger the fetch.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At internal/webhook/provider_linear.go, line 51:
<comment>When a Comment delivery reaches an Issue-only spawner with an unscoped label filter, this condition still performs a Linear API lookup even though the spawner cannot match Comment events. Include the parent `LinearWebhook.Types` in the enrichment decision so only spawners that listen for Comment can trigger the fetch.</comment>
<file context>
@@ -0,0 +1,69 @@
+// them by label, because Linear omits labels from Comment payloads.
+func (linearProvider) prepare(ctx context.Context, _ *WebhookHandler, log logr.Logger, parsed *ParsedWebhook, spawners []*kelos.TaskSpawner) {
+ for _, spawner := range spawners {
+ if spawnerNeedsLinearLabels(spawner, parsed.Linear) {
+ enrichLinearCommentLabels(ctx, log, parsed.Linear)
+ return
</file context>
| // in that case keep the workspace host so GHES API URLs are preserved. | ||
| if overrideHost != "" { | ||
| host = overrideHost | ||
| if tracker, _ := ts.Spec.When.Tracker(); tracker.Provider == kelos.WorkspaceProviderGitLab { |
There was a problem hiding this comment.
P2: For a spec.when.gitlabWebhook spawner, Tracker() reports Provider == GitLab while ts.Spec.When.GitLab is nil, so gitLabSourceArgs(ts.Spec.When.GitLab, ...) dereferences a nil pointer (gl.BaseURL/gl.Project). The condition was broadened from ts.Spec.When.GitLab != nil to the provider check, which no longer guarantees the field is set. Webhook spawners currently route around Build(), so this is latent, but keep the check and the dereference consistent: branch on ts.Spec.When.GitLab != nil (or guard the nil) rather than the GitLab provider value.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At internal/controller/taskspawner_deployment_builder.go, line 63:
<comment>For a `spec.when.gitlabWebhook` spawner, Tracker() reports Provider == GitLab while `ts.Spec.When.GitLab` is nil, so `gitLabSourceArgs(ts.Spec.When.GitLab, ...)` dereferences a nil pointer (gl.BaseURL/gl.Project). The condition was broadened from `ts.Spec.When.GitLab != nil` to the provider check, which no longer guarantees the field is set. Webhook spawners currently route around Build(), so this is latent, but keep the check and the dereference consistent: branch on `ts.Spec.When.GitLab != nil` (or guard the nil) rather than the GitLab provider value.</comment>
<file context>
@@ -60,8 +60,8 @@ func (b *DeploymentBuilder) buildPodParts(ts *kelos.TaskSpawner, workspace *kelo
if workspace != nil {
- if gl := ts.Spec.When.GitLab; gl != nil {
- args = append(args, gitLabSourceArgs(gl, workspace.Repo)...)
+ if tracker, _ := ts.Spec.When.Tracker(); tracker.Provider == kelos.WorkspaceProviderGitLab {
+ args = append(args, gitLabSourceArgs(ts.Spec.When.GitLab, workspace.Repo)...)
} else {
</file context>
| Webhook bool | ||
| // Repo scopes the source to a repository other than the Workspace's: the | ||
| // GitHub "owner/repo" override or restriction, or the GitLab project path. | ||
| // Empty means the Workspace repository. |
There was a problem hiding this comment.
P3: For webhook sources, an empty Repository or Project means unrestricted delivery, not the Workspace repository. Clarify this contract so future callers do not treat an empty webhook restriction as a workspace fallback.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At api/v1alpha2/taskspawner_tracker.go, line 15:
<comment>For webhook sources, an empty `Repository` or `Project` means unrestricted delivery, not the Workspace repository. Clarify this contract so future callers do not treat an empty webhook restriction as a workspace fallback.</comment>
<file context>
@@ -0,0 +1,90 @@
+ Webhook bool
+ // Repo scopes the source to a repository other than the Workspace's: the
+ // GitHub "owner/repo" override or restriction, or the GitLab project path.
+ // Empty means the Workspace repository.
+ Repo string
+ // PriorityLabels orders discovered items for polling sources that support it.
</file context>
| && DEB="glab_${GLAB_VERSION}_linux_${ARCH}.deb" \ | ||
| && curl -fsSL -o "/tmp/${DEB}" "https://gitlab.com/gitlab-org/cli/-/releases/v${GLAB_VERSION}/downloads/${DEB}" \ | ||
| && curl -fsSL -o /tmp/glab-checksums.txt "https://gitlab.com/gitlab-org/cli/-/releases/v${GLAB_VERSION}/downloads/checksums.txt" \ | ||
| && (cd /tmp && grep "${DEB}" glab-checksums.txt | sha256sum -c -) \ |
There was a problem hiding this comment.
P3: grep "${DEB}" treats ${DEB} as a basic regex (dots match any char) and does substring matching, so it can match unintended lines in the checksums file. If more than one line matches, sha256sum -c - tries to verify files that are not present in /tmp and the build fails; if none matches, empty stdin makes sha256sum abort. Anchor to the exact filename with a fixed-string match: grep -Fx "${DEB}" glab-checksums.txt | sha256sum -c -.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At gemini/Dockerfile, line 34:
<comment>`grep "${DEB}"` treats ${DEB} as a basic regex (dots match any char) and does substring matching, so it can match unintended lines in the checksums file. If more than one line matches, `sha256sum -c -` tries to verify files that are not present in /tmp and the build fails; if none matches, empty stdin makes sha256sum abort. Anchor to the exact filename with a fixed-string match: `grep -Fx "${DEB}" glab-checksums.txt | sha256sum -c -`.</comment>
<file context>
@@ -31,7 +31,7 @@ RUN ARCH=$(dpkg --print-architecture) \
&& curl -fsSL -o "/tmp/${DEB}" "https://gitlab.com/gitlab-org/cli/-/releases/v${GLAB_VERSION}/downloads/${DEB}" \
&& curl -fsSL -o /tmp/glab-checksums.txt "https://gitlab.com/gitlab-org/cli/-/releases/v${GLAB_VERSION}/downloads/checksums.txt" \
- && (cd /tmp && sha256sum --ignore-missing -c glab-checksums.txt) \
+ && (cd /tmp && grep "${DEB}" glab-checksums.txt | sha256sum -c -) \
&& dpkg -i "/tmp/${DEB}" \
&& rm "/tmp/${DEB}" /tmp/glab-checksums.txt
</file context>
| && (cd /tmp && grep "${DEB}" glab-checksums.txt | sha256sum -c -) \ | |
| && (cd /tmp && grep -Fx "${DEB}" glab-checksums.txt | sha256sum -c -) \ |
| && DEB="glab_${GLAB_VERSION}_linux_${ARCH}.deb" \ | ||
| && curl -fsSL -o "/tmp/${DEB}" "https://gitlab.com/gitlab-org/cli/-/releases/v${GLAB_VERSION}/downloads/${DEB}" \ | ||
| && curl -fsSL -o /tmp/glab-checksums.txt "https://gitlab.com/gitlab-org/cli/-/releases/v${GLAB_VERSION}/downloads/checksums.txt" \ | ||
| && (cd /tmp && grep "${DEB}" glab-checksums.txt | sha256sum -c -) \ |
There was a problem hiding this comment.
P3: grep "${DEB}" is an unanchored substring (and regex — dots are unescaped) match. If the GitLab checksums.txt ever lists a sibling asset whose name embeds the DEB string (e.g. a .deb.asc/.deb.sha256 entry), grep matches multiple lines and sha256sum -c - then checks every matched file; any matched-but-absent file fails the build where the previous --ignore-missing -c variant would have passed. Anchor the match to the end-of-line filename (and treat it as a fixed string) so only the exact downloaded DEB is verified.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cursor/Dockerfile, line 34:
<comment>`grep "${DEB}"` is an unanchored substring (and regex — dots are unescaped) match. If the GitLab checksums.txt ever lists a sibling asset whose name embeds the DEB string (e.g. a `.deb.asc`/`.deb.sha256` entry), grep matches multiple lines and `sha256sum -c -` then checks every matched file; any matched-but-absent file fails the build where the previous `--ignore-missing -c` variant would have passed. Anchor the match to the end-of-line filename (and treat it as a fixed string) so only the exact downloaded DEB is verified.</comment>
<file context>
@@ -31,7 +31,7 @@ RUN ARCH=$(dpkg --print-architecture) \
&& curl -fsSL -o "/tmp/${DEB}" "https://gitlab.com/gitlab-org/cli/-/releases/v${GLAB_VERSION}/downloads/${DEB}" \
&& curl -fsSL -o /tmp/glab-checksums.txt "https://gitlab.com/gitlab-org/cli/-/releases/v${GLAB_VERSION}/downloads/checksums.txt" \
- && (cd /tmp && sha256sum --ignore-missing -c glab-checksums.txt) \
+ && (cd /tmp && grep "${DEB}" glab-checksums.txt | sha256sum -c -) \
&& dpkg -i "/tmp/${DEB}" \
&& rm "/tmp/${DEB}" /tmp/glab-checksums.txt
</file context>
There was a problem hiding this comment.
1 issue found across 7 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="examples/19-taskspawner-gitlab/README.md">
<violation number="1" location="examples/19-taskspawner-gitlab/README.md:113">
P3: The new sentence leaves line 113 at 127 characters, breaking the ~80-char wrapping used throughout the rest of the file and mixing the filter sentence with the next paragraph's continuation. Wrap the added text so 'For an in-cluster GitLab, point the webhook at the' starts on its own line.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
| `gitlab-webhook-secret.yaml`, and the **Issues events**, **Comments** and | ||
| **Pipeline events** triggers enabled. Filters accept `labels` (all required) | ||
| and `excludeLabels` (any rejects) on `issue`, `merge_request` and `note` | ||
| events; note filters use the labels of the commented issue or merge request. For an in-cluster GitLab, point the webhook at the |
There was a problem hiding this comment.
P3: The new sentence leaves line 113 at 127 characters, breaking the ~80-char wrapping used throughout the rest of the file and mixing the filter sentence with the next paragraph's continuation. Wrap the added text so 'For an in-cluster GitLab, point the webhook at the' starts on its own line.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At examples/19-taskspawner-gitlab/README.md, line 113:
<comment>The new sentence leaves line 113 at 127 characters, breaking the ~80-char wrapping used throughout the rest of the file and mixing the filter sentence with the next paragraph's continuation. Wrap the added text so 'For an in-cluster GitLab, point the webhook at the' starts on its own line.</comment>
<file context>
@@ -107,8 +107,10 @@ interval. It needs the GitLab webhook server: set
+`gitlab-webhook-secret.yaml`, and the **Issues events**, **Comments** and
+**Pipeline events** triggers enabled. Filters accept `labels` (all required)
+and `excludeLabels` (any rejects) on `issue`, `merge_request` and `note`
+events; note filters use the labels of the commented issue or merge request. For an in-cluster GitLab, point the webhook at the
`kelos-webhook-gitlab` Service and allow local network requests in the GitLab
admin settings (Admin → Settings → Network → Outbound requests). To get status
</file context>
| events; note filters use the labels of the commented issue or merge request. For an in-cluster GitLab, point the webhook at the | |
| events; note filters use the labels of the commented issue or merge request. | |
| For an in-cluster GitLab, point the webhook at the |
|
@gjkim42 hey! would love to hear your review. is this acceptable/in right direction? anything to change, update, address differently? I have since tested with Gitlab webhook configuration and all seems in order. Openrouter via Opencode addition is also tested, happy to provide evidence or separate Gitlab deployment so that we can play around and test if needed. |
gjkim42
left a comment
There was a problem hiding this comment.
this is too huge to review.
Can you separate this PR?
maybe gitlab, gitlabwebhook...
and what's the tracksource?
|
let me clean it up and break apart to be more digestible. i will do more testing to ensure all works nicely, so should be back next weekend hopefully with cleaned version. thanks! |
What type of PR is this?
/kind api
/kind feature
What this PR does / why we need it:
Adds GitLab (gitlab.com and self-managed) as a TaskSpawner source: polling and webhooks for issues and merge requests, CI/review gates, and Task status reported back as notes. Covers the GitLab half of #906.
Workspace provider
Workspace.spec.provider(githubdefault,gitlab) selects the secret key, agent credentials, git credential username, and preconfigured CLI. One provider table (internal/controller/workspace_provider.go) drives every workspace-secret read.provider: gitlabrequires a non-blankGITLAB_TOKENkey; a missing key fails the Task before Job creation and sets the TaskSpawnerFailed. A GitLab source on a GitHub Workspace (or vice versa) fails the TaskSpawner. No fallback toGITHUB_TOKEN.ghproxywithprovider: gitlabis rejected by CEL.GITLAB_TOKEN,GITLAB_HOST, a mounted token file,GLAB_CONFIG_DIR, git usernameoauth2, and noGITHUB_TOKEN/GH_*. GitHub pods are unchanged.Polling
spec.when.gitlabbaseUrl,project),types: [issues, mergeRequests],labels,excludeLabels,state,commentPolicy(trigger, excludes, allowed users).pipelineStatus(head pipeline, one detail call per MR since the list endpoint omits it) andreviewState(approvedfrom/approvals,changes_requestedfromdetailed_merge_status == requested_changes).{{.Kind}},{{.Branch}},{{.PipelineStatus}},{{.PipelineURL}},{{.ReviewState}},{{.ReviewComments}}(diff notes aspath:line),{{.Comments}}(discussion notes).Webhooks
spec.when.gitlabWebhookissue,merge_request,note,pipeline,push,tag_pushwith per-eventfilters. Token checked againstX-Gitlab-Token(constant-time); dedup byIdempotency-Key, thenX-Gitlab-Event-UUID, then body hash.kelos-webhook-gitlab(--source=gitlab,--gitlab-base-url/ HelmwebhookServer.sources.gitlab.baseUrl) or a WebhookGateway withspec.gitlab(secretRef,apiBaseURL,credentialsRef). The instance URL for status notes comes only from that config, never from the payload.Reporting
GitLabReporterposts and updates notes;reporting.comments.modeisPerTaskorSticky. The watcher takes aCommentTarget{Kind, Number}so a reporter picks the endpoint.kelos.dev/comment-*,kelos.dev/check-*); the oldkelos.dev/github-*keys are still read so in-flight Tasks keep their comment/check across the upgrade.Shared code (GitHub and GitLab)
When.Tracker()gives a provider-neutral view of the tracker source;CommentsReportingreplaces the per-provider reporting types.internal/source:trackerPoller(list/enrich/commentPolicy) drives onediscoverTrackerpipeline; onerestClientwith per-provider auth and pagination; one comment policy (comment_policy.go) with per-provider authorizers.internal/webhook:webhookProviderinterface with one file per source; the handler has no per-source switches.Also:
--gitlab-*deployment flags, WebhookGateway secret validation, HelmwebhookServer.sources.gitlab,kelos get taskspawneroutput, telemetry kindsgitlab/gitlab_webhook,glab1.116.0 in all agent images with a token-refreshing wrapper,docs/reference.md,docs/integration.md,examples/19-taskspawner-gitlab.Design notes
gitlabfield withtypesinstead ofgitlabIssues/gitlabMergeRequests(Integration: Add GitLab support for Workspace and TaskSpawner source #373).providerrather than hostname detection; self-managed hosts are not detectable, and Bitbucket needs the same hook.kelos.dev/v1alpha2-*annotations.assignee/author/targetBranchfilters,--providerforkelos create workspace/kelos run, pre-clone token check in WorkerPool/Session controllers.Which issue(s) this PR is related to:
Related to #906 (GitLab portion). Related to #373 and #701 (closed as duplicates of #906).
Special notes for your reviewer:
reviewState: changes_requestedworks on every tier./kelos fixnotes,pipelineStatus: failed,reviewState: changes_requested, per-source webhook server end to end (token validation, resend dedup, Sticky notes, own notes filtered), fail-fast on provider mismatch and missingGITLAB_TOKEN,glabfrom a real Task. WebhookGateway mode for GitLab is covered by unit tests only.provider: githubthe generated pods are identical apart from env ordering.helm upgrade --reuse-valuesfails on the newwebhookServer.sources.gitlabblock, as it did forlinearandgeneric; use--reset-then-reuse-values.make test,make test-integration,make verifyclean. No e2e, consistent with the Linear and generic sources.Does this PR introduce a user-facing change?