runspec-gitlab 0.1.0__tar.gz → 0.2.0__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.0/CHANGELOG.md +44 -0
- {runspec_gitlab-0.1.0 → runspec_gitlab-0.2.0}/PKG-INFO +2 -2
- {runspec_gitlab-0.1.0 → runspec_gitlab-0.2.0}/pyproject.toml +2 -2
- {runspec_gitlab-0.1.0 → runspec_gitlab-0.2.0}/runspec_gitlab/__init__.py +10 -2
- {runspec_gitlab-0.1.0 → runspec_gitlab-0.2.0}/runspec_gitlab/cli.py +40 -3
- {runspec_gitlab-0.1.0 → runspec_gitlab-0.2.0}/runspec_gitlab/runspec.toml +43 -3
- {runspec_gitlab-0.1.0 → runspec_gitlab-0.2.0}/tests/test_cli.py +12 -0
- runspec_gitlab-0.1.0/CHANGELOG.md +0 -21
- {runspec_gitlab-0.1.0 → runspec_gitlab-0.2.0}/.gitignore +0 -0
- {runspec_gitlab-0.1.0 → runspec_gitlab-0.2.0}/tests/__init__.py +0 -0
- {runspec_gitlab-0.1.0 → runspec_gitlab-0.2.0}/tests/test_spec.py +0 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `runspec-gitlab` are documented here.
|
|
4
|
+
|
|
5
|
+
## [0.2.0]
|
|
6
|
+
|
|
7
|
+
Reconciled the verb set against GitLab's canonical MCP tool registry
|
|
8
|
+
(`app/services/mcp/tools/manager.rb` + the `route_setting :mcp` declarations in
|
|
9
|
+
the REST route files) and closed the gaps. **17 read verbs** now (all
|
|
10
|
+
`autonomy = "autonomous"`).
|
|
11
|
+
|
|
12
|
+
- **Renamed** `get-merge-request-changes` → `get-merge-request-diffs` — matches
|
|
13
|
+
the canonical `get_merge_request_diffs` tool and its `/diffs` endpoint; the old
|
|
14
|
+
verb used the deprecated `/changes` endpoint. **Breaking** vs 0.1.0.
|
|
15
|
+
- **Added** canonical MR read tools: `get-merge-request-commits`,
|
|
16
|
+
`get-merge-request-notes`, `get-merge-request-pipelines`,
|
|
17
|
+
`get-merge-request-conflicts`.
|
|
18
|
+
- `get-merge-request-conflicts` is **best-effort** — GitLab has no public
|
|
19
|
+
`/api/v4` conflicts endpoint (its MCP server calls an internal service in
|
|
20
|
+
process), so it relays the non-public web-UI endpoint
|
|
21
|
+
(`/{project}/-/merge_requests/:iid/conflicts`) and returns its JSON verbatim;
|
|
22
|
+
the shape is GitLab's and may change between versions.
|
|
23
|
+
- Docs now separate the canonical 1:1 ports from the extra REST reads GitLab has
|
|
24
|
+
no MCP tool for (`whoami`, `get-project`, `search-projects`, `list-issues`,
|
|
25
|
+
`get-file`, `list-tree`).
|
|
26
|
+
- Requires `runspec-gitlab-core >= 0.2.0`.
|
|
27
|
+
|
|
28
|
+
## [0.1.0]
|
|
29
|
+
|
|
30
|
+
Initial release. One runnable, `gitlab`, with a read verb per tool of the
|
|
31
|
+
[GitLab MCP server](https://docs.gitlab.com/user/model_context_protocol/mcp_server/),
|
|
32
|
+
**ported onto the GitLab REST API (v4)** rather than proxying that server — so it
|
|
33
|
+
runs over the runspec-console SSH fleet with no extra process. `runspec serve`
|
|
34
|
+
re-expands each verb into its own leaf MCP tool (`gitlab_get-issue`,
|
|
35
|
+
`gitlab_list-pipelines`, …).
|
|
36
|
+
|
|
37
|
+
- Verbs (all `autonomy = "autonomous"` — read-only): `whoami`, `get-project`,
|
|
38
|
+
`search-projects`, `list-issues`, `get-issue`, `list-merge-requests`,
|
|
39
|
+
`get-merge-request`, `get-merge-request-changes`, `list-pipelines`,
|
|
40
|
+
`get-pipeline-jobs`, `get-job-log`, `get-file`, `list-tree`.
|
|
41
|
+
- Global `--env` (dev/uat/prod) selects `GITLAB_<ENV>_*` env vars; auth is
|
|
42
|
+
`GITLAB_URL` (default `https://gitlab.com`) + `GITLAB_TOKEN`. `project` accepts
|
|
43
|
+
a numeric id or `group/project` path and falls back to `GITLAB_PROJECT`.
|
|
44
|
+
- Thin wrapper over `runspec-gitlab-core` (the dependency-free REST logic core).
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: runspec-gitlab
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: GitLab read runnable for runspec — issues, merge requests, pipelines, and repository reads, ported from the GitLab MCP server
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
|
-
Requires-Dist: runspec-gitlab-core>=0.
|
|
6
|
+
Requires-Dist: runspec-gitlab-core>=0.2.0
|
|
7
7
|
Requires-Dist: runspec>=0.44.0
|
|
8
8
|
Provides-Extra: dev
|
|
9
9
|
Requires-Dist: mypy; extra == 'dev'
|
|
@@ -4,14 +4,14 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "runspec-gitlab"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.2.0"
|
|
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
10
|
dependencies = [
|
|
11
11
|
# 0.44.0 surfaces inherited args (the global --env) on each leaf subcommand's
|
|
12
12
|
# MCP tool via `runspec serve`.
|
|
13
13
|
"runspec>=0.44.0",
|
|
14
|
-
"runspec-gitlab-core>=0.
|
|
14
|
+
"runspec-gitlab-core>=0.2.0",
|
|
15
15
|
]
|
|
16
16
|
|
|
17
17
|
[project.scripts]
|
|
@@ -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).
|
|
File without changes
|
|
File without changes
|
|
File without changes
|