runspec-gitlab 0.1.0__tar.gz → 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- runspec_gitlab-0.2.1/CHANGELOG.md +54 -0
- runspec_gitlab-0.2.1/PKG-INFO +90 -0
- runspec_gitlab-0.2.1/README.md +72 -0
- {runspec_gitlab-0.1.0 → runspec_gitlab-0.2.1}/pyproject.toml +10 -2
- {runspec_gitlab-0.1.0 → runspec_gitlab-0.2.1}/runspec_gitlab/__init__.py +10 -2
- {runspec_gitlab-0.1.0 → runspec_gitlab-0.2.1}/runspec_gitlab/cli.py +40 -3
- {runspec_gitlab-0.1.0 → runspec_gitlab-0.2.1}/runspec_gitlab/runspec.toml +43 -3
- {runspec_gitlab-0.1.0 → runspec_gitlab-0.2.1}/tests/test_cli.py +12 -0
- runspec_gitlab-0.1.0/CHANGELOG.md +0 -21
- runspec_gitlab-0.1.0/PKG-INFO +0 -11
- {runspec_gitlab-0.1.0 → runspec_gitlab-0.2.1}/.gitignore +0 -0
- {runspec_gitlab-0.1.0 → runspec_gitlab-0.2.1}/tests/__init__.py +0 -0
- {runspec_gitlab-0.1.0 → runspec_gitlab-0.2.1}/tests/test_spec.py +0 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `runspec-gitlab` are documented here.
|
|
4
|
+
|
|
5
|
+
## [0.2.1] — 2026-09-20
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- **Packaging metadata for the runspec-console Marketplace.** Add a README
|
|
10
|
+
(rendered in the Marketplace card's Details view) and publish `keywords`
|
|
11
|
+
and Documentation / Source / Changelog / Issues project URLs, so the
|
|
12
|
+
package's Marketplace card shows a description, tags and links. No
|
|
13
|
+
runnable, argument, or behaviour changes.
|
|
14
|
+
|
|
15
|
+
## [0.2.0]
|
|
16
|
+
|
|
17
|
+
Reconciled the verb set against GitLab's canonical MCP tool registry
|
|
18
|
+
(`app/services/mcp/tools/manager.rb` + the `route_setting :mcp` declarations in
|
|
19
|
+
the REST route files) and closed the gaps. **17 read verbs** now (all
|
|
20
|
+
`autonomy = "autonomous"`).
|
|
21
|
+
|
|
22
|
+
- **Renamed** `get-merge-request-changes` → `get-merge-request-diffs` — matches
|
|
23
|
+
the canonical `get_merge_request_diffs` tool and its `/diffs` endpoint; the old
|
|
24
|
+
verb used the deprecated `/changes` endpoint. **Breaking** vs 0.1.0.
|
|
25
|
+
- **Added** canonical MR read tools: `get-merge-request-commits`,
|
|
26
|
+
`get-merge-request-notes`, `get-merge-request-pipelines`,
|
|
27
|
+
`get-merge-request-conflicts`.
|
|
28
|
+
- `get-merge-request-conflicts` is **best-effort** — GitLab has no public
|
|
29
|
+
`/api/v4` conflicts endpoint (its MCP server calls an internal service in
|
|
30
|
+
process), so it relays the non-public web-UI endpoint
|
|
31
|
+
(`/{project}/-/merge_requests/:iid/conflicts`) and returns its JSON verbatim;
|
|
32
|
+
the shape is GitLab's and may change between versions.
|
|
33
|
+
- Docs now separate the canonical 1:1 ports from the extra REST reads GitLab has
|
|
34
|
+
no MCP tool for (`whoami`, `get-project`, `search-projects`, `list-issues`,
|
|
35
|
+
`get-file`, `list-tree`).
|
|
36
|
+
- Requires `runspec-gitlab-core >= 0.2.0`.
|
|
37
|
+
|
|
38
|
+
## [0.1.0]
|
|
39
|
+
|
|
40
|
+
Initial release. One runnable, `gitlab`, with a read verb per tool of the
|
|
41
|
+
[GitLab MCP server](https://docs.gitlab.com/user/model_context_protocol/mcp_server/),
|
|
42
|
+
**ported onto the GitLab REST API (v4)** rather than proxying that server — so it
|
|
43
|
+
runs over the runspec-console SSH fleet with no extra process. `runspec serve`
|
|
44
|
+
re-expands each verb into its own leaf MCP tool (`gitlab_get-issue`,
|
|
45
|
+
`gitlab_list-pipelines`, …).
|
|
46
|
+
|
|
47
|
+
- Verbs (all `autonomy = "autonomous"` — read-only): `whoami`, `get-project`,
|
|
48
|
+
`search-projects`, `list-issues`, `get-issue`, `list-merge-requests`,
|
|
49
|
+
`get-merge-request`, `get-merge-request-changes`, `list-pipelines`,
|
|
50
|
+
`get-pipeline-jobs`, `get-job-log`, `get-file`, `list-tree`.
|
|
51
|
+
- Global `--env` (dev/uat/prod) selects `GITLAB_<ENV>_*` env vars; auth is
|
|
52
|
+
`GITLAB_URL` (default `https://gitlab.com`) + `GITLAB_TOKEN`. `project` accepts
|
|
53
|
+
a numeric id or `group/project` path and falls back to `GITLAB_PROJECT`.
|
|
54
|
+
- Thin wrapper over `runspec-gitlab-core` (the dependency-free REST logic core).
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: runspec-gitlab
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: GitLab read runnable for runspec — issues, merge requests, pipelines, and repository reads, ported from the GitLab MCP server
|
|
5
|
+
Project-URL: Documentation, https://runspec.app/
|
|
6
|
+
Project-URL: Source, https://github.com/jasonfinestone/runspec/tree/main/packages/python/runspec-gitlab
|
|
7
|
+
Project-URL: Changelog, https://github.com/jasonfinestone/runspec/blob/main/packages/python/runspec-gitlab/CHANGELOG.md
|
|
8
|
+
Project-URL: Issues, https://github.com/jasonfinestone/runspec/issues
|
|
9
|
+
Keywords: gitlab,mcp,runnable,runspec
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Requires-Dist: runspec-gitlab-core>=0.2.0
|
|
12
|
+
Requires-Dist: runspec>=0.44.0
|
|
13
|
+
Provides-Extra: dev
|
|
14
|
+
Requires-Dist: mypy; extra == 'dev'
|
|
15
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
16
|
+
Requires-Dist: ruff==0.15.20; extra == 'dev'
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# runspec-gitlab
|
|
20
|
+
|
|
21
|
+
A read-only GitLab runnable for [runspec](https://pypi.org/project/runspec/),
|
|
22
|
+
ported onto the GitLab REST API (v4) — **not** proxying GitLab's hosted MCP
|
|
23
|
+
server. One runnable — **`gitlab`** — with 17 read verbs, all `autonomy =
|
|
24
|
+
"autonomous"`. `pip install` it into a venv and it's discoverable by `runspec
|
|
25
|
+
local`, `runspec serve` (MCP), and runspec-console.
|
|
26
|
+
|
|
27
|
+
`runspec serve` flattens each verb into a leaf MCP tool (`gitlab_get-issue`, …).
|
|
28
|
+
Verb names are reconciled against GitLab's canonical MCP tool registry; the
|
|
29
|
+
extras (`whoami`, `get-project`, `search-projects`, `list-issues`, `get-file`,
|
|
30
|
+
`list-tree`) are useful REST reads GitLab has no MCP tool for.
|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
pip install runspec-gitlab
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Verbs
|
|
39
|
+
|
|
40
|
+
| Area | Verbs |
|
|
41
|
+
|---|---|
|
|
42
|
+
| Account / project | `whoami`, `get-project`, `search-projects`, `get-file`, `list-tree` |
|
|
43
|
+
| Issues | `list-issues`, `get-issue` |
|
|
44
|
+
| Merge requests | `list-merge-requests`, `get-merge-request`, `get-merge-request-diffs`, `get-merge-request-commits`, `get-merge-request-notes`, `get-merge-request-pipelines`, `get-merge-request-conflicts` |
|
|
45
|
+
| Pipelines | `list-pipelines`, `get-pipeline-jobs`, `get-job-log` |
|
|
46
|
+
|
|
47
|
+
`get-merge-request-conflicts` is best-effort — GitLab has no public `/api/v4`
|
|
48
|
+
conflicts endpoint, so it relays the web-UI endpoint and returns its JSON
|
|
49
|
+
verbatim.
|
|
50
|
+
|
|
51
|
+
## Auth — env vars only
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
export GITLAB_URL=https://gitlab.example.com # default https://gitlab.com
|
|
55
|
+
export GITLAB_TOKEN=<personal-or-project-access-token>
|
|
56
|
+
export GITLAB_PROJECT=group/subgroup/project # optional default
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Every verb takes `--env`, selecting `GITLAB_<ENV>_*` prefixed vars. `--project`
|
|
60
|
+
accepts a numeric id or a `group/project` path and falls back to
|
|
61
|
+
`GITLAB_PROJECT`.
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
gitlab whoami
|
|
65
|
+
gitlab list-merge-requests --project group/app --state opened
|
|
66
|
+
gitlab get-job-log --project group/app --job-id 998877
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Public Python API
|
|
70
|
+
|
|
71
|
+
The pure logic is in **[`runspec-gitlab-core`](../runspec-gitlab-core)** —
|
|
72
|
+
`httpx`-only, no `runspec` dependency, no entry points. Every op takes a
|
|
73
|
+
`GitlabConfig` first arg and raises `GitlabError`, so a private wrapper can
|
|
74
|
+
inject credentials and bake in a default `project` / `env`:
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from runspec_gitlab_core import config_from_env, get_issue, list_merge_requests
|
|
78
|
+
|
|
79
|
+
cfg = config_from_env(env="prod")
|
|
80
|
+
mrs = list_merge_requests(cfg, state="opened")
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Development
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
python -m venv .venv && . .venv/bin/activate
|
|
87
|
+
pip install -e ".[dev]"
|
|
88
|
+
ruff check . && ruff format --check .
|
|
89
|
+
pytest
|
|
90
|
+
```
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# runspec-gitlab
|
|
2
|
+
|
|
3
|
+
A read-only GitLab runnable for [runspec](https://pypi.org/project/runspec/),
|
|
4
|
+
ported onto the GitLab REST API (v4) — **not** proxying GitLab's hosted MCP
|
|
5
|
+
server. One runnable — **`gitlab`** — with 17 read verbs, all `autonomy =
|
|
6
|
+
"autonomous"`. `pip install` it into a venv and it's discoverable by `runspec
|
|
7
|
+
local`, `runspec serve` (MCP), and runspec-console.
|
|
8
|
+
|
|
9
|
+
`runspec serve` flattens each verb into a leaf MCP tool (`gitlab_get-issue`, …).
|
|
10
|
+
Verb names are reconciled against GitLab's canonical MCP tool registry; the
|
|
11
|
+
extras (`whoami`, `get-project`, `search-projects`, `list-issues`, `get-file`,
|
|
12
|
+
`list-tree`) are useful REST reads GitLab has no MCP tool for.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
pip install runspec-gitlab
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Verbs
|
|
21
|
+
|
|
22
|
+
| Area | Verbs |
|
|
23
|
+
|---|---|
|
|
24
|
+
| Account / project | `whoami`, `get-project`, `search-projects`, `get-file`, `list-tree` |
|
|
25
|
+
| Issues | `list-issues`, `get-issue` |
|
|
26
|
+
| Merge requests | `list-merge-requests`, `get-merge-request`, `get-merge-request-diffs`, `get-merge-request-commits`, `get-merge-request-notes`, `get-merge-request-pipelines`, `get-merge-request-conflicts` |
|
|
27
|
+
| Pipelines | `list-pipelines`, `get-pipeline-jobs`, `get-job-log` |
|
|
28
|
+
|
|
29
|
+
`get-merge-request-conflicts` is best-effort — GitLab has no public `/api/v4`
|
|
30
|
+
conflicts endpoint, so it relays the web-UI endpoint and returns its JSON
|
|
31
|
+
verbatim.
|
|
32
|
+
|
|
33
|
+
## Auth — env vars only
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
export GITLAB_URL=https://gitlab.example.com # default https://gitlab.com
|
|
37
|
+
export GITLAB_TOKEN=<personal-or-project-access-token>
|
|
38
|
+
export GITLAB_PROJECT=group/subgroup/project # optional default
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Every verb takes `--env`, selecting `GITLAB_<ENV>_*` prefixed vars. `--project`
|
|
42
|
+
accepts a numeric id or a `group/project` path and falls back to
|
|
43
|
+
`GITLAB_PROJECT`.
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
gitlab whoami
|
|
47
|
+
gitlab list-merge-requests --project group/app --state opened
|
|
48
|
+
gitlab get-job-log --project group/app --job-id 998877
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Public Python API
|
|
52
|
+
|
|
53
|
+
The pure logic is in **[`runspec-gitlab-core`](../runspec-gitlab-core)** —
|
|
54
|
+
`httpx`-only, no `runspec` dependency, no entry points. Every op takes a
|
|
55
|
+
`GitlabConfig` first arg and raises `GitlabError`, so a private wrapper can
|
|
56
|
+
inject credentials and bake in a default `project` / `env`:
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from runspec_gitlab_core import config_from_env, get_issue, list_merge_requests
|
|
60
|
+
|
|
61
|
+
cfg = config_from_env(env="prod")
|
|
62
|
+
mrs = list_merge_requests(cfg, state="opened")
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Development
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
python -m venv .venv && . .venv/bin/activate
|
|
69
|
+
pip install -e ".[dev]"
|
|
70
|
+
ruff check . && ruff format --check .
|
|
71
|
+
pytest
|
|
72
|
+
```
|
|
@@ -4,16 +4,24 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "runspec-gitlab"
|
|
7
|
-
version = "0.1
|
|
7
|
+
version = "0.2.1"
|
|
8
8
|
requires-python = ">=3.10"
|
|
9
9
|
description = "GitLab read runnable for runspec — issues, merge requests, pipelines, and repository reads, ported from the GitLab MCP server"
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
keywords = ["runspec", "runnable", "gitlab", "mcp"]
|
|
10
12
|
dependencies = [
|
|
11
13
|
# 0.44.0 surfaces inherited args (the global --env) on each leaf subcommand's
|
|
12
14
|
# MCP tool via `runspec serve`.
|
|
13
15
|
"runspec>=0.44.0",
|
|
14
|
-
"runspec-gitlab-core>=0.
|
|
16
|
+
"runspec-gitlab-core>=0.2.0",
|
|
15
17
|
]
|
|
16
18
|
|
|
19
|
+
[project.urls]
|
|
20
|
+
Documentation = "https://runspec.app/"
|
|
21
|
+
Source = "https://github.com/jasonfinestone/runspec/tree/main/packages/python/runspec-gitlab"
|
|
22
|
+
Changelog = "https://github.com/jasonfinestone/runspec/blob/main/packages/python/runspec-gitlab/CHANGELOG.md"
|
|
23
|
+
Issues = "https://github.com/jasonfinestone/runspec/issues"
|
|
24
|
+
|
|
17
25
|
[project.scripts]
|
|
18
26
|
# Entry point name MUST match the runnable name in runspec.toml (`gitlab`).
|
|
19
27
|
gitlab = "runspec_gitlab.cli:main"
|
|
@@ -20,7 +20,11 @@ from runspec_gitlab_core import (
|
|
|
20
20
|
get_issue,
|
|
21
21
|
get_job_log,
|
|
22
22
|
get_merge_request,
|
|
23
|
-
|
|
23
|
+
get_merge_request_commits,
|
|
24
|
+
get_merge_request_conflicts,
|
|
25
|
+
get_merge_request_diffs,
|
|
26
|
+
get_merge_request_notes,
|
|
27
|
+
get_merge_request_pipelines,
|
|
24
28
|
get_pipeline_jobs,
|
|
25
29
|
get_project,
|
|
26
30
|
gitlab_request,
|
|
@@ -45,7 +49,11 @@ __all__ = [
|
|
|
45
49
|
"get_issue",
|
|
46
50
|
"list_merge_requests",
|
|
47
51
|
"get_merge_request",
|
|
48
|
-
"
|
|
52
|
+
"get_merge_request_diffs",
|
|
53
|
+
"get_merge_request_commits",
|
|
54
|
+
"get_merge_request_notes",
|
|
55
|
+
"get_merge_request_pipelines",
|
|
56
|
+
"get_merge_request_conflicts",
|
|
49
57
|
"list_pipelines",
|
|
50
58
|
"get_pipeline_jobs",
|
|
51
59
|
"get_job_log",
|
|
@@ -24,7 +24,11 @@ from runspec_gitlab_core import (
|
|
|
24
24
|
get_issue,
|
|
25
25
|
get_job_log,
|
|
26
26
|
get_merge_request,
|
|
27
|
-
|
|
27
|
+
get_merge_request_commits,
|
|
28
|
+
get_merge_request_conflicts,
|
|
29
|
+
get_merge_request_diffs,
|
|
30
|
+
get_merge_request_notes,
|
|
31
|
+
get_merge_request_pipelines,
|
|
28
32
|
get_pipeline_jobs,
|
|
29
33
|
get_project,
|
|
30
34
|
list_issues,
|
|
@@ -79,8 +83,41 @@ def _dispatch(spec: rs.RunSpec, config: GitlabConfig) -> Any:
|
|
|
79
83
|
)
|
|
80
84
|
if command == "get-merge-request":
|
|
81
85
|
return get_merge_request(config, mr_iid=spec.mr_iid.value, project=spec.project.value)
|
|
82
|
-
if command == "get-merge-request-
|
|
83
|
-
return
|
|
86
|
+
if command == "get-merge-request-diffs":
|
|
87
|
+
return get_merge_request_diffs(
|
|
88
|
+
config,
|
|
89
|
+
mr_iid=spec.mr_iid.value,
|
|
90
|
+
project=spec.project.value,
|
|
91
|
+
per_page=spec.per_page.value,
|
|
92
|
+
page=spec.page.value,
|
|
93
|
+
)
|
|
94
|
+
if command == "get-merge-request-commits":
|
|
95
|
+
return get_merge_request_commits(
|
|
96
|
+
config,
|
|
97
|
+
mr_iid=spec.mr_iid.value,
|
|
98
|
+
project=spec.project.value,
|
|
99
|
+
per_page=spec.per_page.value,
|
|
100
|
+
page=spec.page.value,
|
|
101
|
+
)
|
|
102
|
+
if command == "get-merge-request-notes":
|
|
103
|
+
return get_merge_request_notes(
|
|
104
|
+
config,
|
|
105
|
+
mr_iid=spec.mr_iid.value,
|
|
106
|
+
project=spec.project.value,
|
|
107
|
+
sort=spec.sort.value,
|
|
108
|
+
per_page=spec.per_page.value,
|
|
109
|
+
page=spec.page.value,
|
|
110
|
+
)
|
|
111
|
+
if command == "get-merge-request-pipelines":
|
|
112
|
+
return get_merge_request_pipelines(
|
|
113
|
+
config,
|
|
114
|
+
mr_iid=spec.mr_iid.value,
|
|
115
|
+
project=spec.project.value,
|
|
116
|
+
per_page=spec.per_page.value,
|
|
117
|
+
page=spec.page.value,
|
|
118
|
+
)
|
|
119
|
+
if command == "get-merge-request-conflicts":
|
|
120
|
+
return get_merge_request_conflicts(config, mr_iid=spec.mr_iid.value, project=spec.project.value)
|
|
84
121
|
if command == "list-pipelines":
|
|
85
122
|
return list_pipelines(
|
|
86
123
|
config,
|
|
@@ -98,10 +98,50 @@ autonomy = "autonomous"
|
|
|
98
98
|
project = {type = "str", short = "-p", required = false, description = "Project id or path (falls back to GITLAB_PROJECT)"}
|
|
99
99
|
mr-iid = {type = "int", required = true, description = "Merge request internal id (the !N shown in the UI)"}
|
|
100
100
|
|
|
101
|
-
[gitlab.commands.get-merge-request-
|
|
102
|
-
description = "
|
|
101
|
+
[gitlab.commands.get-merge-request-diffs]
|
|
102
|
+
description = "List a merge request's file diffs (the canonical get_merge_request_diffs tool)"
|
|
103
103
|
autonomy = "autonomous"
|
|
104
|
-
[gitlab.commands.get-merge-request-
|
|
104
|
+
[gitlab.commands.get-merge-request-diffs.args]
|
|
105
|
+
project = {type = "str", short = "-p", required = false, description = "Project id or path (falls back to GITLAB_PROJECT)"}
|
|
106
|
+
mr-iid = {type = "int", required = true, description = "Merge request internal id (the !N shown in the UI)"}
|
|
107
|
+
per-page = {type = "int", default = 20, description = "Results per page (GitLab caps at 100)"}
|
|
108
|
+
page = {type = "int", default = 1, description = "Page number"}
|
|
109
|
+
|
|
110
|
+
[gitlab.commands.get-merge-request-commits]
|
|
111
|
+
description = "List the commits on a merge request (canonical get_merge_request_commits)"
|
|
112
|
+
autonomy = "autonomous"
|
|
113
|
+
[gitlab.commands.get-merge-request-commits.args]
|
|
114
|
+
project = {type = "str", short = "-p", required = false, description = "Project id or path (falls back to GITLAB_PROJECT)"}
|
|
115
|
+
mr-iid = {type = "int", required = true, description = "Merge request internal id (the !N shown in the UI)"}
|
|
116
|
+
per-page = {type = "int", default = 20, description = "Results per page (GitLab caps at 100)"}
|
|
117
|
+
page = {type = "int", default = 1, description = "Page number"}
|
|
118
|
+
|
|
119
|
+
[gitlab.commands.get-merge-request-notes]
|
|
120
|
+
description = "List a merge request's notes/comments (canonical get_merge_request_notes)"
|
|
121
|
+
autonomy = "autonomous"
|
|
122
|
+
[gitlab.commands.get-merge-request-notes.args]
|
|
123
|
+
project = {type = "str", short = "-p", required = false, description = "Project id or path (falls back to GITLAB_PROJECT)"}
|
|
124
|
+
mr-iid = {type = "int", required = true, description = "Merge request internal id (the !N shown in the UI)"}
|
|
125
|
+
sort = {type = "choice", options = ["asc", "desc"], required = false, description = "Sort order by created date"}
|
|
126
|
+
per-page = {type = "int", default = 20, description = "Results per page (GitLab caps at 100)"}
|
|
127
|
+
page = {type = "int", default = 1, description = "Page number"}
|
|
128
|
+
|
|
129
|
+
[gitlab.commands.get-merge-request-pipelines]
|
|
130
|
+
description = "List the pipelines run for a merge request (canonical get_merge_request_pipelines)"
|
|
131
|
+
autonomy = "autonomous"
|
|
132
|
+
[gitlab.commands.get-merge-request-pipelines.args]
|
|
133
|
+
project = {type = "str", short = "-p", required = false, description = "Project id or path (falls back to GITLAB_PROJECT)"}
|
|
134
|
+
mr-iid = {type = "int", required = true, description = "Merge request internal id (the !N shown in the UI)"}
|
|
135
|
+
per-page = {type = "int", default = 20, description = "Results per page (GitLab caps at 100)"}
|
|
136
|
+
page = {type = "int", default = 1, description = "Page number"}
|
|
137
|
+
|
|
138
|
+
[gitlab.commands.get-merge-request-conflicts]
|
|
139
|
+
# Best-effort: GitLab has no public /api/v4 conflicts endpoint — this relays
|
|
140
|
+
# the non-public web-UI endpoint (/{project}/-/merge_requests/:iid/conflicts),
|
|
141
|
+
# so the response shape is GitLab's and may change between versions.
|
|
142
|
+
description = "Fetch a merge request's conflict files (best-effort; relays GitLab's non-public web conflicts endpoint)"
|
|
143
|
+
autonomy = "autonomous"
|
|
144
|
+
[gitlab.commands.get-merge-request-conflicts.args]
|
|
105
145
|
project = {type = "str", short = "-p", required = false, description = "Project id or path (falls back to GITLAB_PROJECT)"}
|
|
106
146
|
mr-iid = {type = "int", required = true, description = "Merge request internal id (the !N shown in the UI)"}
|
|
107
147
|
|
|
@@ -64,6 +64,18 @@ def test_dispatch_get_job_log_tail(monkeypatch) -> None:
|
|
|
64
64
|
assert result["truncated"] is True
|
|
65
65
|
|
|
66
66
|
|
|
67
|
+
def test_dispatch_mr_conflicts_uses_web_endpoint(monkeypatch) -> None:
|
|
68
|
+
seen = _capture(monkeypatch, httpx.Response(200, json={"files": []}))
|
|
69
|
+
h = _harness()
|
|
70
|
+
spec = h.expect_ok(["get-merge-request-conflicts", "--project", "grp/app", "--mr-iid", "3"])
|
|
71
|
+
result = cli._dispatch(spec, CFG)
|
|
72
|
+
h.close()
|
|
73
|
+
assert result == {"files": []}
|
|
74
|
+
# non-public web endpoint, not /api/v4
|
|
75
|
+
assert seen["path"] == "/grp/app/-/merge_requests/3/conflicts"
|
|
76
|
+
assert "/api/v4" not in seen["path"]
|
|
77
|
+
|
|
78
|
+
|
|
67
79
|
def test_dispatch_whoami(monkeypatch) -> None:
|
|
68
80
|
seen = _capture(monkeypatch, httpx.Response(200, json={"username": "alice"}))
|
|
69
81
|
h = _harness()
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
All notable changes to `runspec-gitlab` are documented here.
|
|
4
|
-
|
|
5
|
-
## [0.1.0]
|
|
6
|
-
|
|
7
|
-
Initial release. One runnable, `gitlab`, with a read verb per tool of the
|
|
8
|
-
[GitLab MCP server](https://docs.gitlab.com/user/model_context_protocol/mcp_server/),
|
|
9
|
-
**ported onto the GitLab REST API (v4)** rather than proxying that server — so it
|
|
10
|
-
runs over the runspec-console SSH fleet with no extra process. `runspec serve`
|
|
11
|
-
re-expands each verb into its own leaf MCP tool (`gitlab_get-issue`,
|
|
12
|
-
`gitlab_list-pipelines`, …).
|
|
13
|
-
|
|
14
|
-
- Verbs (all `autonomy = "autonomous"` — read-only): `whoami`, `get-project`,
|
|
15
|
-
`search-projects`, `list-issues`, `get-issue`, `list-merge-requests`,
|
|
16
|
-
`get-merge-request`, `get-merge-request-changes`, `list-pipelines`,
|
|
17
|
-
`get-pipeline-jobs`, `get-job-log`, `get-file`, `list-tree`.
|
|
18
|
-
- Global `--env` (dev/uat/prod) selects `GITLAB_<ENV>_*` env vars; auth is
|
|
19
|
-
`GITLAB_URL` (default `https://gitlab.com`) + `GITLAB_TOKEN`. `project` accepts
|
|
20
|
-
a numeric id or `group/project` path and falls back to `GITLAB_PROJECT`.
|
|
21
|
-
- Thin wrapper over `runspec-gitlab-core` (the dependency-free REST logic core).
|
runspec_gitlab-0.1.0/PKG-INFO
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: runspec-gitlab
|
|
3
|
-
Version: 0.1.0
|
|
4
|
-
Summary: GitLab read runnable for runspec — issues, merge requests, pipelines, and repository reads, ported from the GitLab MCP server
|
|
5
|
-
Requires-Python: >=3.10
|
|
6
|
-
Requires-Dist: runspec-gitlab-core>=0.1.0
|
|
7
|
-
Requires-Dist: runspec>=0.44.0
|
|
8
|
-
Provides-Extra: dev
|
|
9
|
-
Requires-Dist: mypy; extra == 'dev'
|
|
10
|
-
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
11
|
-
Requires-Dist: ruff==0.15.20; extra == 'dev'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|