[DEV-85] Support GitLab Cloud and GitLab self-hosted as task trackers across devpm CLI, devintern code, and pm-desktop - #54
Merged
Conversation
added 2 commits
August 24, 2026 15:32
…as task trackers across devpm CLI, devintern code, and pm-desktop
- security (critical): only fetch /uploads/ attachment links whose origin matches the configured GitLab instance; external hosts are skipped so a planted link can no longer exfiltrate the PRIVATE-TOKEN - bug (high): updateIssueComment now targets the noteable-scoped Notes API (PUT /projects/:id/issues/:iid/notes/:note_id) and takes the issue iid; updateEstimationComment passes it through, fixing guaranteed 404s - bug (medium): GitLabTaskTrackerClient normalizes GITLAB_BASE_URL via sanitizeGitlabBaseUrl so protocol-less values like gitlab.example.com work from every entry point - bug (medium): guard decodeURIComponent in parseGitLabProject and in attachment filename extraction; malformed escapes fall back to the raw segment / skip that URL instead of throwing or aborting all downloads - bug (medium): listIssueComments paginates until exhausted (filtering system notes per page) so dedup checks see comments beyond page 1 Tests: regression tests for token-leak prevention, noteable-scoped comment updates, pagination, malformed percent-escapes, and base URL sanitization
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Task: DEV-85
Summary: Support GitLab Cloud and GitLab self-hosted as task trackers across devpm CLI, devintern code, and pm-desktop
Implementation Details
I'll start by analyzing the repository state and understanding the existing patterns before implementing GitLab support.
I'm on
feature/dev-85-attempt-3with a clean tree. Let me check for partial work from previous attempts and explore the codebase structure.No partial work on previous branches — starting fresh. Let me explore the codebase thoroughly to understand all integration points.
Subagents failed due to network errors — I'll explore directly.
Now let me explore the pm package structure:
I have a good picture of the architecture now. Let me examine the remaining integration points:
I now have full context. Let me set up my implementation plan:
Now implementing. Starting with the task-trackers package — types and metadata:
Now tracker-meta.ts:
Now the core piece — the GitLab client:
Now wire the client into exports and the probe:
Now the config loader — add GitLab URL/project parsing and env config resolution:
Now export the new helpers from the package index:
I made an error —
DEFAULT_GITLAB_BASE_URLcomes from the client, not utils, and would duplicate withexport * from "./clients/index.ts". Fixing:Existing tests pass. Now the GitLab client tests:
Tokenizer doesn't handle
key:"quoted value"prefixes. Fixing the client and test typo:Now extend the tracker-meta tests with gitlab coverage:
task-trackers done. Now the pm package — GitLabBackend:
Now the pm init-shared metadata (wizard prompts, docs, names):
Now pm backend tests — add a GitLabBackend describe block modeled after GitHubBackend:
Now the code package. First, check settings types:
Now the GitLab task tracker client for code:
Let me clean up that regex — I accidentally wrote escaped slashes. Fixing to mirror the GitHub client style:
Now wire the manager, capabilities, change detector, init-scaffold, normalize-task-keys, and resolveProjectKey:
Now the change detector:
Now init-scaffold (TRACKER_SETUP, TRACKER_DOCS, settings example):
Now normalize-task-keys and resolveProjectKey in index.ts:
The code init wizard is metadata-driven — good. Now let me check the GitHub client test to model the GitLab one:
The reference regex needs to require a
/in any project-path prefix (like GitHub's), soPROJ-123isn't misparsed:All code tests pass. Now the docs:
Now the
.env.examplefiles:Now
packages/code/.env.example:Now the docs enumerations:
Typecheck passes everywhere. Now format and lint:
No warnings from new files. Now run the full monorepo test suite:
All green. Let me check for any remaining exact-set assertions in tests:
Let me strengthen the code init-wizard tests with GitLab coverage:
All suites pass. Final verification — format check and a review of the complete diff:
Final full-repo verification:
DEV-85 is implemented across all four surfaces. Summary of the change (36 files, ~434 insertions):
@devintern/task-trackers(foundation)src/clients/gitlab.ts: GitLab REST v4 client (PRIVATE-TOKENauth, subgroup paths viaencodeURIComponent), with issue CRUD, subtask task-lists, labels (add_labels/remove_labelsso existing labels survive), notes (system notes filtered), project/user listing, and asearchIssuestranslator mapping GitHub-style qualifiers (is:open,label:,assignee:@me,updated:>=) to list filters — includingx-totalpagination countgitlabadded toTrackerType/TRACKER_META(GITLAB_TOKEN+GITLAB_PROJECTrequired;GITLAB_BASE_URLoptional →https://gitlab.comdefault, self-hosted keeps protocol incl. http)sanitizeGitlabBaseUrl/parseGitLabProject(acceptsgroup/sub/repo, numeric IDs, pasted URLs) and a credential probe viaGET /user@getdevintern/pmGitLabBackend(capabilities mirror GitHub: labels ✓, freeform ✗, epic-link ✗, attachments ✗, type→label mapping Story→enhancement etc.) + factory case with env-var-naming errors<instance>/-/user_settings/personal_access_tokens) → project path; docs URL wired@getdevintern/codeGitLabTaskTrackerClient: fullTaskTrackerClientparity — label-based status transitions w/ reopen-close, comment-only estimation,/uploads/attachment download,parseGitLabIssueReferenceaccepting123,#123,group/sub/repo#123, URLs (/-/issues/) while rejecting Jira-style keysGITLAB_STATUS_LABELS), capabilities, worker change detector, init scaffold/settings template, key normalization,resolveProjectKey, CLI helppm-desktop: zero code changes needed — verified the wizard/tracker-switcher pick GitLab up via
PM_TRACKER_SETUP/TRACKER_META(test now asserts it explicitly).Docs & config:
docs/pm/gitlab-integration.md+docs/code/gitlab-integration.md(Cloud vs self-hosted, token scopes), both.env.examplefiles, CLAUDE/AGENTS enumerations.Verification: 22 new GitLab client tests, new pm backend tests, 14 new code adapter tests; full monorepo green — 11/11 test tasks pass (code: 813, pm: 323, desktop: 543, task-trackers: 165+), typecheck 11/11, lint 0 errors, oxfmt applied.
This PR was automatically created by @devintern/code