feat(provider): GitLab forge and ClickUp tracker providers #37
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/gitlab-clickup-providers"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Ploeg shipped one provider per seam — Vikunja and Forgejo — which is enough to prove the SPI and not enough to run anywhere else. Code14's staging cluster is GitLab and ClickUp, so both seams needed a second implementation before Ploeg could be exercised there at all (see RFC-0007 in
code14/staging-cluster).Both are pure SPI implementations. No core change, nothing vendor-shaped escapes either package (R7), and each registers under its dialect name so the existing
/webhooks/{tracker,forge}/{provider}routes reach them unchanged.GitLab — three things a copy-paste of the Forgejo provider gets silently wrong
X-Gitlab-Token— it authenticates the sender, not the payload. Compared in constant time; a bare==leaks a secret a byte at a time.id(instance-global, useless in a URL) andiid(what humans and the API use). This usesiidthroughout.group/sub/proj), so the path is URL-encoded whole rather than split into owner/name.Review outcomes arrive as
merge_requestactions rather than a review object, and a branch pipeline has no MR — reported asPR 0, which the core reads as "nothing to route this to" rather than as merge request zero.ClickUp — three more
Authorization: pk_…with noBearerprefix; ClickUp 401s the prefixed form. There is a regression test for exactly this.SetStatusneedsDoneStatusconfigured and skips loudly without it, instead of guessing a name that would 400 or move the task somewhere nobody chose.ClickUp's webhook is thin and carries no List, so
Scopeis empty at parse time and resolved inFetchItem— the thin-payload rule doing what it is for.Wiring
Trackers become a registry built once rather than two inline literals. The forge instance id (ADR-0016) is now bound explicitly: with one forge configured it takes the id; with two and
PLOEG_TARGET_FORGEnaming neither, nothing is bound and it logs that — publishing findings to the wrong forge is worse than not publishing.New env, all optional:
PLOEG_CLICKUP_{SECRET,TOKEN,URL,DONE_STATUS},PLOEG_GITLAB_{URL,TOKEN,SECRET}. Absent, behaviour is byte-identical to before.Gates
Tests fake both APIs with
httptestand never touch the network.Not in scope
No chart wiring for the new env yet, and no ClickUp Space/Folder traversal beyond folderless lists —
ListsByNamedeliberately does not conflate folders, since two folders can reuse a list name.Ploeg shipped one provider per seam — Vikunja and Forgejo — which is enough to prove the SPI and not enough to run anywhere else. Code14's staging cluster is GitLab and ClickUp, so both seams needed a second implementation before Ploeg could be exercised there at all. Both are pure SPI implementations: no core change, nothing vendor-shaped escapes either package (R7), and each is registered under its dialect name so the existing /webhooks/{tracker,forge}/{provider} routes reach them unchanged. GitLab differs from Forgejo in three ways a copy-paste gets silently wrong, and each is commented where it bites: - GitLab does NOT sign webhooks. It echoes a shared secret in X-Gitlab-Token, which authenticates the sender but not the payload. Compared in constant time, since a bare == leaks a secret a byte at a time. - A merge request has two numbers; only `iid` works in an API path. - Projects live at arbitrary subgroup depth, so the path is URL-encoded whole rather than split into owner/name. Review outcomes also arrive as merge_request actions rather than a review object, and a branch pipeline has no MR — reported as PR 0, which the core reads as "nothing to route this to" rather than as merge request zero. ClickUp differs from Vikunja in three ways, likewise commented: - Auth is the raw token, no "Bearer " prefix (ClickUp 401s the prefixed form). - Priority is inverted — id 1 is urgent — and is flipped on the way in rather than leaking backwards ordering into scheduling. - There is no global "done": status is a per-List custom string, so SetStatus needs DoneStatus configured and skips loudly without it instead of guessing a name that would 400 or move the task somewhere nobody chose. ClickUp's webhook is thin and carries no List, so Scope is left empty at parse time and resolved in FetchItem — the thin-payload rule doing exactly what it is for. Wiring: trackers become a registry built once rather than two inline literals, and the forge instance id (ADR-0016) is bound explicitly. With one forge configured it takes the id; with two and PLOEG_TARGET_FORGE naming neither, nothing is bound and it says so — publishing findings to the wrong forge is worse than not publishing. New env: PLOEG_CLICKUP_{SECRET,TOKEN,URL,DONE_STATUS}, PLOEG_GITLAB_{URL,TOKEN,SECRET}. Absent, behaviour is byte-identical to before. Gates: go build, go vet, go test ./... (all green, including cmd/ploegd's existing wiring tests), gofmt clean, helm lint and all three chart renderings. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>