feat: install plugins from GitLab repositories#1801
Conversation
🦋 Changeset detectedLatest commit: 432b295 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cc286658b3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const isGitlabHost = url.hostname.toLowerCase().split('.').includes('gitlab'); | ||
| if (!isGitlabHost && separatorIndex === -1 && !hasGitSuffix) return undefined; |
There was a problem hiding this comment.
Recognize hostnames that contain GitLab
For a bare repository URL on hosts such as mygitlab.example.com or gitlab01.example.com, this exact-label check returns false even though the documented detection rule says hostnames containing gitlab are recognized. Without a /-/… route or .git suffix, the source falls through to zip-url, so installation downloads the GitLab HTML page and then fails to extract it as a ZIP. The legacy resolver mirrors the same predicate.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Thanks—fixed in cdfbfab. Hostname detection now recognizes any hostname containing gitlab, and regression coverage in both plugin implementations includes mygitlab.example.com and gitlab01.example.com.
| const projectPath = decodeRefSegments([...projectSegments.slice(0, -1), repo]); | ||
| const base = { kind: 'gitlab' as const, baseUrl: url.origin, projectPath }; |
There was a problem hiding this comment.
Preserve self-managed GitLab path prefixes
When a self-managed instance is served below a relative URL, such as https://code.example.com/gitlab, its documented HTTPS clone URL https://code.example.com/gitlab/team/sample.git is parsed with baseUrl reduced to the origin and gitlab folded into projectPath. The resolver consequently requests /api/v4/projects/gitlab%2Fteam%2Fsample instead of the instance endpoint /gitlab/api/v4/projects/team%2Fsample, so these supported self-managed installs fail. Preserve or otherwise resolve the instance path prefix; the legacy parser has the same issue.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Thanks for catching this. Supporting arbitrary relative URL roots requires distinguishing the instance path prefix from the project namespace, which cannot be determined unambiguously from the clone URL alone. To keep this PR small and its support scope clear, cdfbfab narrows the English and Chinese docs to self-managed GitLab instances served from the origin root and explicitly notes that relative URL roots are not currently supported. Full relative-root discovery/support can be handled separately.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cdfbfab3a0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| ): Promise<string | undefined> { | ||
| const projectId = encodeURIComponent(input.projectPath); | ||
| const url = `${input.baseUrl}/api/v4/projects/${projectId}/releases/permalink/latest`; | ||
| const resp = await fetch(url); |
There was a problem hiding this comment.
Bound the legacy latest-release request
When installing a bare GitLab repository URL through the legacy engine, this preliminary release lookup has no abort signal, so a self-managed server that accepts the request but stalls can leave /plugins install pending indefinitely before the bounded archive download begins. The v2 implementation already applies a 10-second timeout; apply an equivalent timeout here.
Useful? React with 👍 / 👎.
| @@ -0,0 +1,51 @@ | |||
| import { afterEach, describe, expect, it, vi } from 'vitest'; | |||
There was a problem hiding this comment.
Add the required test scenario header
The applicable agent-core-review/test guidance requires every new v2 test file to start with a header describing the scenario, responsibilities, real versus stubbed wiring, and the command to run it. This new file starts directly with imports, so add that header for consistency with the v2 test contract.
Useful? React with 👍 / 👎.
| }); | ||
|
|
||
| it('uses the latest release or falls back to the default branch', async () => { | ||
| const fetch = vi |
There was a problem hiding this comment.
Split release and fallback resolver scenarios
This test verifies two distinct behaviors—selecting a latest release and falling back after a 404—using ordered mock responses in one it, contrary to the applicable one-behavior-per-test rule. Split them so each failure identifies the broken contract directly and neither scenario depends on the other consuming the first mocked response.
Useful? React with 👍 / 👎.
| }); | ||
| }); | ||
|
|
||
| it('resolves GitLab.com and self-managed GitLab URLs', () => { |
There was a problem hiding this comment.
Separate GitLab source recognition conditions
This it combines GitLab.com, hostname-based self-managed detection, and .git-suffix detection, which are separate parser conditions and violates the applicable one-behavior-per-test guidance. Convert these cases to individually named tests or an it.each table whose case names identify the condition and expected result.
Useful? React with 👍 / 👎.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5a082100e8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return { ...base, ref: { kind, value } }; | ||
| } | ||
|
|
||
| if (head === 'releases' && rest.length >= 2) { |
There was a problem hiding this comment.
Preserve GitLab release asset URLs as zip sources
When an existing zip source is a GitLab release asset such as https://gitlab.example.com/team/plugin/-/releases/v1/downloads/plugin.zip, this broad condition treats every segment after releases as the tag and requests a repository archive for v1/downloads/plugin.zip instead of downloading the supplied zip. Such URLs previously worked through the generic zip-url path, so require the release form to end after the tag (and apply the same fix to the mirrored legacy parser).
Useful? React with 👍 / 👎.
Related Issue
No linked issue.
Problem
For necessary organizational, security, or distribution constraints, some plugins can only be hosted on internal GitLab instances. Kimi Code CLI currently resolves repository URLs only for GitHub, so those plugins require a separate manual download and extraction workflow.
Supporting GitLab directly through
/plugins install <url>keeps plugin documentation and installation instructions consistent across hosting providers and makes internal plugins substantially more convenient to install.What changed
.git./-/tree/<ref>,/-/releases/<tag>, and/-/commit/<sha>forms. Bare URLs select the latest release and fall back to the default branch when no release exists./api/v4endpoints in both plugin engines./pluginsCustom-tab hint and the mirrored English/Chinese plugin documentation.Authentication behavior is unchanged: the GitLab instance and repository must already be reachable from the CLI. This PR does not add token or credential storage.
Validation
vitesttargeted plugin/TUI suite: 7 files, 126 tests passed.@moonshot-ai/agent-core,@moonshot-ai/agent-core-v2, and@moonshot-ai/kimi-code.pnpm lintcompleted with 0 errors (the repository currently reports existing warnings outside this change).ES2024target warnings.200 application/ziprepository archive response withref_type=tags.pnpm testis blocked only in 25 existing MCP stdio tests across two files: this checkout path contains a space, and those tests incorrectly resolve fixture paths with%20. The suite completed after excluding those two path-sensitive files.Checklist
gen-changesetsskill, or this PR needs no changeset.gen-docsskill, or this PR needs no doc update.The
gen-docsworkflow's referenceddocs/scripts/sync-changelog.mjsprerequisite is not present in this checkout. No changelog files were changed; the bilingual page pair was reviewed directly and the docs site was built successfully.