gitlab-api 27.3.13__tar.gz → 27.3.15__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.
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/PKG-INFO +1 -1
- gitlab_api-27.3.15/gitlab_api/ontology/access_contracts.ttl +287 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/ontology/certification.json +1 -1
- gitlab_api-27.3.15/gitlab_api/skills/gitlab-api-development/SKILL.md +132 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api.egg-info/PKG-INFO +1 -1
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api.egg-info/SOURCES.txt +5 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/pyproject.toml +1 -1
- gitlab_api-27.3.15/tests/test_access_contracts.py +75 -0
- gitlab_api-27.3.15/tests/test_gitlab_api_development_skill.py +54 -0
- gitlab_api-27.3.15/tests/test_pages_workflow.py +52 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/LICENSE +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/README.md +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/__init__.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/__main__.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/api/__init__.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/api/api_client_base.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/api/api_client_environments.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/api/api_client_issues.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/api/api_client_merge_requests.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/api/api_client_other.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/api/api_client_pipelines.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/api/api_client_projects.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/api/api_client_repositories.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/api/api_client_system.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/api/api_client_users_groups.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/api/api_client_vulnerabilities.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/api_client.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/auth.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/connector_prep.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/connector_sources.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/gitlab_gql.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/gitlab_input_models.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/gitlab_response_models.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/instances.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/kg_ingest.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/mcp_config.json +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/mcp_server.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/ontology/__init__.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/ontology/fixtures/records.json +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/ontology/gitlab.ttl +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/ontology/mappings/source.yaml +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/ontology/migrations/manifest.json +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/ontology/shapes/connector.shacl.ttl +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/prompts/devops_specialist.json +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/skills/gitlab-api-operations/SKILL.md +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/skills/gitlab-api-operations/agents/openai.yaml +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/skills/gitlab-api-operations/references/catalog.md +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/skills/gitlab-issues/SKILL.md +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/skills/gitlab-issues/WORKFLOW.md +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/skills/gitlab-merge-request-create/SKILL.md +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/skills/gitlab-merge-request-create/WORKFLOW.md +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/skills/gitlab-merge-request-review/SKILL.md +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/skills/gitlab-merge-request-review/WORKFLOW.md +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/skills/gitlab-pipelines/SKILL.md +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/skills/gitlab-pipelines/WORKFLOW.md +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/skills/gitlab-vulnerabilities/SKILL.md +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/skills/gitlab-vulnerabilities/WORKFLOW.md +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api.egg-info/dependency_links.txt +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api.egg-info/entry_points.txt +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api.egg-info/requires.txt +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api.egg-info/top_level.txt +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/scripts/check_import_safety.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/scripts/security_contract.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/setup.cfg +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/tests/__init__.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/tests/conftest.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/tests/test_api_wrapper.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/tests/test_auth.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/tests/test_catalog_edges.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/tests/test_connector_prep.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/tests/test_cq_paths.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/tests/test_fleet_name.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/tests/test_gitlab_a2a_validation.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/tests/test_gitlab_api_brute_force_coverage.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/tests/test_gitlab_gql.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/tests/test_gitlab_mcp_validation.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/tests/test_gitlab_models.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/tests/test_identity_context_scoping.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/tests/test_ingest_sources.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/tests/test_init_dynamics.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/tests/test_instances.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/tests/test_kg_ingest.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/tests/test_mock_coverage.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/tests/test_ontology_parent_link.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/tests/test_startup.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/tests/test_tool_modes.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/tests/test_verify_agent.py +0 -0
- {gitlab_api-27.3.13 → gitlab_api-27.3.15}/tests/verify_a2a_queries.py +0 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
@prefix ac: <https://knuckles-team.github.io/agent-connector-sdk/access#> .
|
|
2
|
+
@prefix gl: <http://knuckles.team/kg#> .
|
|
3
|
+
|
|
4
|
+
# Read contracts for the classes declared by gitlab.ttl. The MCP tools expose
|
|
5
|
+
# both read and write actions, but these mappings are read-only query routes:
|
|
6
|
+
# callers select the listed get action through the operation's action envelope.
|
|
7
|
+
# The source classes stay in GitLab's ontology namespace. No heuristic
|
|
8
|
+
# crosswalk to a hub class is asserted here.
|
|
9
|
+
|
|
10
|
+
gl:gitlabGroupAccess a ac:AccessContract ;
|
|
11
|
+
ac:classIri gl:GitLabGroup ;
|
|
12
|
+
ac:sourceId "gitlab-api" ;
|
|
13
|
+
ac:entity "group" ;
|
|
14
|
+
ac:keyField "id" ;
|
|
15
|
+
ac:accessKind "mcp_tool" ;
|
|
16
|
+
ac:operation "gitlab_groups" ;
|
|
17
|
+
ac:parameter "action", "params_json" ;
|
|
18
|
+
ac:pagination "page" ;
|
|
19
|
+
ac:pushdown "filter", "limit", "sort" ;
|
|
20
|
+
ac:authRef "env://GITLAB_TOKEN" ;
|
|
21
|
+
ac:rateLimitPerMinute 60 ;
|
|
22
|
+
ac:freshnessSeconds 300 ;
|
|
23
|
+
ac:costHint "medium" .
|
|
24
|
+
|
|
25
|
+
gl:gitlabGroupWebUrlBinding a ac:PropertyBinding ;
|
|
26
|
+
ac:contract gl:gitlabGroupAccess ; ac:property gl:webUrl ; ac:field "web_url" .
|
|
27
|
+
|
|
28
|
+
gl:projectAccess a ac:AccessContract ;
|
|
29
|
+
ac:classIri gl:Project ;
|
|
30
|
+
ac:sourceId "gitlab-api" ;
|
|
31
|
+
ac:entity "project" ;
|
|
32
|
+
ac:keyField "id" ;
|
|
33
|
+
ac:accessKind "mcp_tool" ;
|
|
34
|
+
ac:operation "gitlab_projects" ;
|
|
35
|
+
ac:parameter "action", "params_json" ;
|
|
36
|
+
ac:pagination "page" ;
|
|
37
|
+
ac:pushdown "filter", "limit", "sort" ;
|
|
38
|
+
ac:authRef "env://GITLAB_TOKEN" ;
|
|
39
|
+
ac:rateLimitPerMinute 60 ;
|
|
40
|
+
ac:freshnessSeconds 300 ;
|
|
41
|
+
ac:costHint "low" .
|
|
42
|
+
|
|
43
|
+
gl:projectWebUrlBinding a ac:PropertyBinding ;
|
|
44
|
+
ac:contract gl:projectAccess ; ac:property gl:webUrl ; ac:field "web_url" .
|
|
45
|
+
|
|
46
|
+
gl:mergeRequestAccess a ac:AccessContract ;
|
|
47
|
+
ac:classIri gl:MergeRequest ;
|
|
48
|
+
ac:sourceId "gitlab-api" ;
|
|
49
|
+
ac:entity "merge_request" ;
|
|
50
|
+
ac:keyField "id" ;
|
|
51
|
+
ac:accessKind "mcp_tool" ;
|
|
52
|
+
ac:operation "gitlab_merge_requests" ;
|
|
53
|
+
ac:parameter "action", "params_json" ;
|
|
54
|
+
ac:pagination "page" ;
|
|
55
|
+
ac:pushdown "filter", "limit", "sort" ;
|
|
56
|
+
ac:authRef "env://GITLAB_TOKEN" ;
|
|
57
|
+
ac:rateLimitPerMinute 60 ;
|
|
58
|
+
ac:freshnessSeconds 60 ;
|
|
59
|
+
ac:costHint "medium" .
|
|
60
|
+
|
|
61
|
+
gl:mergeRequestIidBinding a ac:PropertyBinding ;
|
|
62
|
+
ac:contract gl:mergeRequestAccess ; ac:property gl:iid ; ac:field "iid" .
|
|
63
|
+
|
|
64
|
+
gl:mergeRequestStateBinding a ac:PropertyBinding ;
|
|
65
|
+
ac:contract gl:mergeRequestAccess ; ac:property gl:state ; ac:field "state" .
|
|
66
|
+
|
|
67
|
+
gl:mergeRequestWebUrlBinding a ac:PropertyBinding ;
|
|
68
|
+
ac:contract gl:mergeRequestAccess ; ac:property gl:webUrl ; ac:field "web_url" .
|
|
69
|
+
|
|
70
|
+
gl:pipelineAccess a ac:AccessContract ;
|
|
71
|
+
ac:classIri gl:Pipeline ;
|
|
72
|
+
ac:sourceId "gitlab-api" ;
|
|
73
|
+
ac:entity "pipeline" ;
|
|
74
|
+
ac:keyField "id" ;
|
|
75
|
+
ac:accessKind "mcp_tool" ;
|
|
76
|
+
ac:operation "gitlab_pipelines" ;
|
|
77
|
+
ac:parameter "action", "params_json" ;
|
|
78
|
+
ac:pagination "page" ;
|
|
79
|
+
ac:pushdown "filter", "limit", "sort" ;
|
|
80
|
+
ac:authRef "env://GITLAB_TOKEN" ;
|
|
81
|
+
ac:rateLimitPerMinute 60 ;
|
|
82
|
+
ac:freshnessSeconds 60 ;
|
|
83
|
+
ac:costHint "medium" .
|
|
84
|
+
|
|
85
|
+
gl:pipelineStatusBinding a ac:PropertyBinding ;
|
|
86
|
+
ac:contract gl:pipelineAccess ; ac:property gl:pipelineStatus ; ac:field "status" .
|
|
87
|
+
|
|
88
|
+
gl:pipelineRefBinding a ac:PropertyBinding ;
|
|
89
|
+
ac:contract gl:pipelineAccess ; ac:property gl:ref ; ac:field "ref" .
|
|
90
|
+
|
|
91
|
+
gl:pipelineShaBinding a ac:PropertyBinding ;
|
|
92
|
+
ac:contract gl:pipelineAccess ; ac:property gl:sha ; ac:field "sha" .
|
|
93
|
+
|
|
94
|
+
gl:pipelineWebUrlBinding a ac:PropertyBinding ;
|
|
95
|
+
ac:contract gl:pipelineAccess ; ac:property gl:webUrl ; ac:field "web_url" .
|
|
96
|
+
|
|
97
|
+
gl:pipelineRunAccess a ac:AccessContract ;
|
|
98
|
+
ac:classIri gl:PipelineRun ;
|
|
99
|
+
ac:sourceId "gitlab-api" ;
|
|
100
|
+
ac:entity "pipeline" ;
|
|
101
|
+
ac:keyField "id" ;
|
|
102
|
+
ac:accessKind "mcp_tool" ;
|
|
103
|
+
ac:operation "gitlab_pipelines" ;
|
|
104
|
+
ac:parameter "action", "params_json" ;
|
|
105
|
+
ac:pagination "page" ;
|
|
106
|
+
ac:pushdown "filter", "limit", "sort" ;
|
|
107
|
+
ac:authRef "env://GITLAB_TOKEN" ;
|
|
108
|
+
ac:rateLimitPerMinute 60 ;
|
|
109
|
+
ac:freshnessSeconds 60 ;
|
|
110
|
+
ac:costHint "medium" .
|
|
111
|
+
|
|
112
|
+
gl:jobAccess a ac:AccessContract ;
|
|
113
|
+
ac:classIri gl:Job ;
|
|
114
|
+
ac:sourceId "gitlab-api" ;
|
|
115
|
+
ac:entity "job" ;
|
|
116
|
+
ac:keyField "id" ;
|
|
117
|
+
ac:accessKind "mcp_tool" ;
|
|
118
|
+
ac:operation "gitlab_jobs" ;
|
|
119
|
+
ac:parameter "action", "params_json" ;
|
|
120
|
+
ac:pagination "page" ;
|
|
121
|
+
ac:pushdown "filter", "limit", "sort" ;
|
|
122
|
+
ac:authRef "env://GITLAB_TOKEN" ;
|
|
123
|
+
ac:rateLimitPerMinute 60 ;
|
|
124
|
+
ac:freshnessSeconds 60 ;
|
|
125
|
+
ac:costHint "medium" .
|
|
126
|
+
|
|
127
|
+
gl:jobStatusBinding a ac:PropertyBinding ;
|
|
128
|
+
ac:contract gl:jobAccess ; ac:property gl:pipelineStatus ; ac:field "status" .
|
|
129
|
+
|
|
130
|
+
gl:jobRefBinding a ac:PropertyBinding ;
|
|
131
|
+
ac:contract gl:jobAccess ; ac:property gl:ref ; ac:field "ref" .
|
|
132
|
+
|
|
133
|
+
gl:jobWebUrlBinding a ac:PropertyBinding ;
|
|
134
|
+
ac:contract gl:jobAccess ; ac:property gl:webUrl ; ac:field "web_url" .
|
|
135
|
+
|
|
136
|
+
gl:checkRunAccess a ac:AccessContract ;
|
|
137
|
+
ac:classIri gl:CheckRun ;
|
|
138
|
+
ac:sourceId "gitlab-api" ;
|
|
139
|
+
ac:entity "job" ;
|
|
140
|
+
ac:keyField "id" ;
|
|
141
|
+
ac:accessKind "mcp_tool" ;
|
|
142
|
+
ac:operation "gitlab_jobs" ;
|
|
143
|
+
ac:parameter "action", "params_json" ;
|
|
144
|
+
ac:pagination "page" ;
|
|
145
|
+
ac:pushdown "filter", "limit", "sort" ;
|
|
146
|
+
ac:authRef "env://GITLAB_TOKEN" ;
|
|
147
|
+
ac:rateLimitPerMinute 60 ;
|
|
148
|
+
ac:freshnessSeconds 60 ;
|
|
149
|
+
ac:costHint "medium" .
|
|
150
|
+
|
|
151
|
+
gl:issueAccess a ac:AccessContract ;
|
|
152
|
+
ac:classIri gl:Issue ;
|
|
153
|
+
ac:sourceId "gitlab-api" ;
|
|
154
|
+
ac:entity "issue" ;
|
|
155
|
+
ac:keyField "id" ;
|
|
156
|
+
ac:accessKind "mcp_tool" ;
|
|
157
|
+
ac:operation "gitlab_issues" ;
|
|
158
|
+
ac:parameter "action", "params_json" ;
|
|
159
|
+
ac:pagination "page" ;
|
|
160
|
+
ac:pushdown "filter", "limit", "sort" ;
|
|
161
|
+
ac:authRef "env://GITLAB_TOKEN" ;
|
|
162
|
+
ac:rateLimitPerMinute 60 ;
|
|
163
|
+
ac:freshnessSeconds 60 ;
|
|
164
|
+
ac:costHint "medium" .
|
|
165
|
+
|
|
166
|
+
gl:issueIidBinding a ac:PropertyBinding ;
|
|
167
|
+
ac:contract gl:issueAccess ; ac:property gl:iid ; ac:field "iid" .
|
|
168
|
+
|
|
169
|
+
gl:issueStateBinding a ac:PropertyBinding ;
|
|
170
|
+
ac:contract gl:issueAccess ; ac:property gl:state ; ac:field "state" .
|
|
171
|
+
|
|
172
|
+
gl:issueWebUrlBinding a ac:PropertyBinding ;
|
|
173
|
+
ac:contract gl:issueAccess ; ac:property gl:webUrl ; ac:field "web_url" .
|
|
174
|
+
|
|
175
|
+
gl:epicAccess a ac:AccessContract ;
|
|
176
|
+
ac:classIri gl:Epic ;
|
|
177
|
+
ac:sourceId "gitlab-api" ;
|
|
178
|
+
ac:entity "epic" ;
|
|
179
|
+
ac:keyField "id" ;
|
|
180
|
+
ac:accessKind "mcp_tool" ;
|
|
181
|
+
ac:operation "gitlab_epics" ;
|
|
182
|
+
ac:parameter "action", "params_json" ;
|
|
183
|
+
ac:pagination "page" ;
|
|
184
|
+
ac:pushdown "filter", "limit", "sort" ;
|
|
185
|
+
ac:authRef "env://GITLAB_TOKEN" ;
|
|
186
|
+
ac:rateLimitPerMinute 60 ;
|
|
187
|
+
ac:freshnessSeconds 300 ;
|
|
188
|
+
ac:costHint "medium" .
|
|
189
|
+
|
|
190
|
+
gl:epicIidBinding a ac:PropertyBinding ;
|
|
191
|
+
ac:contract gl:epicAccess ; ac:property gl:iid ; ac:field "iid" .
|
|
192
|
+
|
|
193
|
+
gl:epicWebUrlBinding a ac:PropertyBinding ;
|
|
194
|
+
ac:contract gl:epicAccess ; ac:property gl:webUrl ; ac:field "web_url" .
|
|
195
|
+
|
|
196
|
+
gl:milestoneAccess a ac:AccessContract ;
|
|
197
|
+
ac:classIri gl:Milestone ;
|
|
198
|
+
ac:sourceId "gitlab-api" ;
|
|
199
|
+
ac:entity "milestone" ;
|
|
200
|
+
ac:keyField "id" ;
|
|
201
|
+
ac:accessKind "mcp_tool" ;
|
|
202
|
+
ac:operation "gitlab_milestones" ;
|
|
203
|
+
ac:parameter "action", "params_json" ;
|
|
204
|
+
ac:pagination "page" ;
|
|
205
|
+
ac:pushdown "filter", "limit", "sort" ;
|
|
206
|
+
ac:authRef "env://GITLAB_TOKEN" ;
|
|
207
|
+
ac:rateLimitPerMinute 60 ;
|
|
208
|
+
ac:freshnessSeconds 300 ;
|
|
209
|
+
ac:costHint "medium" .
|
|
210
|
+
|
|
211
|
+
gl:milestoneStateBinding a ac:PropertyBinding ;
|
|
212
|
+
ac:contract gl:milestoneAccess ; ac:property gl:state ; ac:field "state" .
|
|
213
|
+
|
|
214
|
+
gl:milestoneWebUrlBinding a ac:PropertyBinding ;
|
|
215
|
+
ac:contract gl:milestoneAccess ; ac:property gl:webUrl ; ac:field "web_url" .
|
|
216
|
+
|
|
217
|
+
gl:commitAccess a ac:AccessContract ;
|
|
218
|
+
ac:classIri gl:Commit ;
|
|
219
|
+
ac:sourceId "gitlab-api" ;
|
|
220
|
+
ac:entity "commit" ;
|
|
221
|
+
ac:keyField "id" ;
|
|
222
|
+
ac:accessKind "mcp_tool" ;
|
|
223
|
+
ac:operation "gitlab_commits" ;
|
|
224
|
+
ac:parameter "action", "params_json" ;
|
|
225
|
+
ac:pagination "page" ;
|
|
226
|
+
ac:pushdown "filter", "limit", "sort" ;
|
|
227
|
+
ac:authRef "env://GITLAB_TOKEN" ;
|
|
228
|
+
ac:rateLimitPerMinute 60 ;
|
|
229
|
+
ac:freshnessSeconds 60 ;
|
|
230
|
+
ac:costHint "medium" .
|
|
231
|
+
|
|
232
|
+
gl:commitShaBinding a ac:PropertyBinding ;
|
|
233
|
+
ac:contract gl:commitAccess ; ac:property gl:sha ; ac:field "sha" .
|
|
234
|
+
|
|
235
|
+
gl:commitWebUrlBinding a ac:PropertyBinding ;
|
|
236
|
+
ac:contract gl:commitAccess ; ac:property gl:webUrl ; ac:field "web_url" .
|
|
237
|
+
|
|
238
|
+
gl:branchAccess a ac:AccessContract ;
|
|
239
|
+
ac:classIri gl:Branch ;
|
|
240
|
+
ac:sourceId "gitlab-api" ;
|
|
241
|
+
ac:entity "branch" ;
|
|
242
|
+
ac:keyField "name" ;
|
|
243
|
+
ac:accessKind "mcp_tool" ;
|
|
244
|
+
ac:operation "gitlab_branches" ;
|
|
245
|
+
ac:parameter "action", "params_json" ;
|
|
246
|
+
ac:pagination "page" ;
|
|
247
|
+
ac:pushdown "filter", "limit", "sort" ;
|
|
248
|
+
ac:authRef "env://GITLAB_TOKEN" ;
|
|
249
|
+
ac:rateLimitPerMinute 60 ;
|
|
250
|
+
ac:freshnessSeconds 300 ;
|
|
251
|
+
ac:costHint "low" .
|
|
252
|
+
|
|
253
|
+
gl:branchWebUrlBinding a ac:PropertyBinding ;
|
|
254
|
+
ac:contract gl:branchAccess ; ac:property gl:webUrl ; ac:field "web_url" .
|
|
255
|
+
|
|
256
|
+
gl:releaseAccess a ac:AccessContract ;
|
|
257
|
+
ac:classIri gl:Release ;
|
|
258
|
+
ac:sourceId "gitlab-api" ;
|
|
259
|
+
ac:entity "release" ;
|
|
260
|
+
ac:keyField "tag_name" ;
|
|
261
|
+
ac:accessKind "mcp_tool" ;
|
|
262
|
+
ac:operation "gitlab_releases" ;
|
|
263
|
+
ac:parameter "action", "params_json" ;
|
|
264
|
+
ac:pagination "page" ;
|
|
265
|
+
ac:pushdown "filter", "limit", "sort" ;
|
|
266
|
+
ac:authRef "env://GITLAB_TOKEN" ;
|
|
267
|
+
ac:rateLimitPerMinute 60 ;
|
|
268
|
+
ac:freshnessSeconds 300 ;
|
|
269
|
+
ac:costHint "medium" .
|
|
270
|
+
|
|
271
|
+
gl:runnerAccess a ac:AccessContract ;
|
|
272
|
+
ac:classIri gl:Runner ;
|
|
273
|
+
ac:sourceId "gitlab-api" ;
|
|
274
|
+
ac:entity "runner" ;
|
|
275
|
+
ac:keyField "id" ;
|
|
276
|
+
ac:accessKind "mcp_tool" ;
|
|
277
|
+
ac:operation "gitlab_runners" ;
|
|
278
|
+
ac:parameter "action", "params_json" ;
|
|
279
|
+
ac:pagination "page" ;
|
|
280
|
+
ac:pushdown "filter", "limit", "sort" ;
|
|
281
|
+
ac:authRef "env://GITLAB_TOKEN" ;
|
|
282
|
+
ac:rateLimitPerMinute 60 ;
|
|
283
|
+
ac:freshnessSeconds 300 ;
|
|
284
|
+
ac:costHint "medium" .
|
|
285
|
+
|
|
286
|
+
gl:runnerStateBinding a ac:PropertyBinding ;
|
|
287
|
+
ac:contract gl:runnerAccess ; ac:property gl:state ; ac:field "status" .
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gitlab-api-development
|
|
3
|
+
skill_type: skill
|
|
4
|
+
domain: development
|
|
5
|
+
description: >-
|
|
6
|
+
Develop, test, gate, and release the GitLab REST and GraphQL connector. Use
|
|
7
|
+
this skill when changing API mappings, governed MCP tools, authentication,
|
|
8
|
+
tenant selection, KG ingestion, packaged content, or repository gates.
|
|
9
|
+
license: MIT
|
|
10
|
+
tags: [gitlab, rest, graphql, mcp, development, connector, test, gates]
|
|
11
|
+
tier: repository
|
|
12
|
+
metadata:
|
|
13
|
+
author: Genius
|
|
14
|
+
version: '0.1.0'
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# gitlab-api development
|
|
18
|
+
|
|
19
|
+
This repository owns the GitLab REST and GraphQL API facades, tenant and
|
|
20
|
+
authentication boundary, action-routed MCP tools, GitLab ontology and KG
|
|
21
|
+
ingestion mappings, and packaged connector content. Shared connector
|
|
22
|
+
contracts, transports, TLS profiles, and credential helpers belong to
|
|
23
|
+
`agent-connector-sdk`; admission, serving, and durable graph state belong to
|
|
24
|
+
their owning repositories. Read `AGENTS.md` before changing code and keep
|
|
25
|
+
credentials, private endpoints, tenant data, and customer records out of
|
|
26
|
+
source, logs, fixtures, and skill content.
|
|
27
|
+
|
|
28
|
+
## Rapid delivery: the 5-minute contract
|
|
29
|
+
|
|
30
|
+
Specs are already designed. Deliver small, reviewable slices and cite the
|
|
31
|
+
owning spec row in the commit trailer.
|
|
32
|
+
|
|
33
|
+
- Find the owning row first with `git grep -n "<term>" -- specs` and extend the
|
|
34
|
+
existing spec before adding requirement text.
|
|
35
|
+
- Reuse the single `Connector` in `gitlab_api/__init__.py`, the `Api` facade in
|
|
36
|
+
`gitlab_api/api_client.py`, the modular clients under `gitlab_api/api/`, and
|
|
37
|
+
the action dispatch tables in `gitlab_api/mcp_server.py`. Keep GraphQL
|
|
38
|
+
operations in `gitlab_api/gitlab_gql.py`; do not create a second API, MCP,
|
|
39
|
+
authentication, or transport plane.
|
|
40
|
+
- Preserve the multi-tenant resolution in `gitlab_api/instances.py`. A named
|
|
41
|
+
instance, bare URL, or default selection must retain caller entitlement
|
|
42
|
+
checks and the `GITLAB_INSTANCES` shared setting. The legacy
|
|
43
|
+
`GITLAB_URL`/`GITLAB_TOKEN` fallback remains supported.
|
|
44
|
+
- Preserve `GITLAB_URL`, `GITLAB_TOKEN`, `GITLAB_TLS_PROFILE`, and
|
|
45
|
+
`ENABLE_DELEGATION` handling in `gitlab_api/auth.py`. Delegated RFC 8693
|
|
46
|
+
token exchange and fixed credentials stay behind the SDK identity/TLS
|
|
47
|
+
helpers. Never print tokens or add a TLS verification bypass.
|
|
48
|
+
- Keep the REST resource paths, GraphQL typed-operation boundary, action names,
|
|
49
|
+
pagination, and response models stable. Use `run_blocking` for blocking API
|
|
50
|
+
calls from MCP tools and return typed refusal/error envelopes instead of
|
|
51
|
+
bypassing the shared dispatcher. Keep `MCP_TOOL_MODE` (`condensed`,
|
|
52
|
+
`verbose`, or `both`) and each action group's toggle behavior aligned with
|
|
53
|
+
the generated tool surface.
|
|
54
|
+
- Preserve typed KG ingestion through `gitlab_api/kg_ingest.py`, source
|
|
55
|
+
declarations in `gitlab_api/connector_sources.py`, and neutral ontology
|
|
56
|
+
mappings. Project, issue, merge-request, pipeline, job, runner, group, and
|
|
57
|
+
person records use the existing stable IDs and relationship names.
|
|
58
|
+
- Mock HTTP, GraphQL, and authentication responses in local tests; never call a
|
|
59
|
+
live GitLab instance or use live credentials in a gate. Every delivered
|
|
60
|
+
requirement needs a bound focused test. Generated `specs/*/status.json` files
|
|
61
|
+
come from shared hooks; never hand-edit them.
|
|
62
|
+
- Keep `skills/**`, `ontology/**`, `connectors/**`, prompts, and
|
|
63
|
+
`mcp_config.json` coherent with package data and the
|
|
64
|
+
`agent_connector_sdk.connector` entry point (`gitlab-mcp =
|
|
65
|
+
gitlab_api:connector`). Do not add a second publisher or runtime plane.
|
|
66
|
+
|
|
67
|
+
## Setup and gates
|
|
68
|
+
|
|
69
|
+
Use the pinned environment from `uv sync --locked`, then run the shared gates:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
uv sync --locked
|
|
73
|
+
uvx pre-commit run --all-files
|
|
74
|
+
uvx pre-commit run --all-files --hook-stage pre-push
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
For focused feedback, run the smallest affected tests with the locked
|
|
78
|
+
environment, for example:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
uv run --locked --extra test pytest -q tests/test_gitlab_api_development_skill.py
|
|
82
|
+
uv run --locked --extra test pytest -q tests/test_pages_workflow.py
|
|
83
|
+
uv run --locked --extra test pytest -q tests/test_auth.py tests/test_gitlab_mcp_validation.py
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The canonical `.pre-commit-config.yaml` delegates connector-standard, secrets,
|
|
87
|
+
spec, tests-fast, and dependency-readiness to `Knuckles-Team/pipelines`. Do not
|
|
88
|
+
copy those hooks locally or bypass a failing check.
|
|
89
|
+
|
|
90
|
+
## Connector-specific checks
|
|
91
|
+
|
|
92
|
+
- Keep REST resource paths, GraphQL operation names, action selectors,
|
|
93
|
+
pagination, and Pydantic response normalization stable. Add focused mocked
|
|
94
|
+
request tests for a changed provider path.
|
|
95
|
+
- Keep OIDC token exchange and fixed-token fallback at the existing
|
|
96
|
+
authentication boundary. Resolve TLS policy through the SDK and never bake
|
|
97
|
+
static bearer tokens into the client.
|
|
98
|
+
- Preserve identity-scoped tenant discovery and refusal of an instance the
|
|
99
|
+
caller is not entitled to use. Discovery returns names, URLs, and profile
|
|
100
|
+
state only; it never returns tokens.
|
|
101
|
+
- Keep projects, issues, merge requests, pipelines, jobs, runners, groups, and
|
|
102
|
+
people as the typed records already mapped by `gitlab_api/kg_ingest.py`.
|
|
103
|
+
Ingestion stays best-effort when the graph engine is unavailable.
|
|
104
|
+
- Keep `gitlab_api/skills/`, `gitlab_api/ontology/`,
|
|
105
|
+
`gitlab_api/connectors/`, prompts, and `gitlab_api/mcp_config.json` in package
|
|
106
|
+
data. Keep source metadata tenant-neutral.
|
|
107
|
+
- Documentation is published by the existing Pages workflow and checked-in
|
|
108
|
+
dashboard configuration; source status records under `specs/` are generated
|
|
109
|
+
by shared hooks.
|
|
110
|
+
|
|
111
|
+
## Packaging and publication
|
|
112
|
+
|
|
113
|
+
`pyproject.toml` declares the `agent_connector_sdk.connector` entry point
|
|
114
|
+
`gitlab-mcp = "gitlab_api:connector"` and packages `skills/**`, `ontology/**`,
|
|
115
|
+
`connectors/**`, prompts, and `mcp_config.json`. When packaged content changes,
|
|
116
|
+
use `uv version --bump patch` and let the lockfile update with the project; add
|
|
117
|
+
the release note to `CHANGELOG.md`. Inspect `uv build` output for
|
|
118
|
+
`gitlab_api/skills/gitlab-api-development/SKILL.md`.
|
|
119
|
+
|
|
120
|
+
The canonical release caller delegates publication and connector rendering to
|
|
121
|
+
the shared pipelines. Do not commit generated Docker artifacts or claim
|
|
122
|
+
package, container, or Pages publication from a local build; require exact
|
|
123
|
+
source hosted evidence and the public artifact.
|
|
124
|
+
|
|
125
|
+
## Delivery discipline
|
|
126
|
+
|
|
127
|
+
Work in a real Git worktree based on `origin/main`. Inspect the complete diff,
|
|
128
|
+
stage only reviewed paths, and verify `git status --short` after every gate. Do
|
|
129
|
+
not use `git stash`, `git add -A`, or `git add .`; preserve unrelated working
|
|
130
|
+
tree changes. Keep runtime, ontology, status, and generated artifacts outside
|
|
131
|
+
this bounded skill slice unless the owning requirement explicitly requires
|
|
132
|
+
them.
|
|
@@ -33,6 +33,7 @@ gitlab_api/api/api_client_system.py
|
|
|
33
33
|
gitlab_api/api/api_client_users_groups.py
|
|
34
34
|
gitlab_api/api/api_client_vulnerabilities.py
|
|
35
35
|
gitlab_api/ontology/__init__.py
|
|
36
|
+
gitlab_api/ontology/access_contracts.ttl
|
|
36
37
|
gitlab_api/ontology/certification.json
|
|
37
38
|
gitlab_api/ontology/gitlab.ttl
|
|
38
39
|
gitlab_api/ontology/fixtures/records.json
|
|
@@ -40,6 +41,7 @@ gitlab_api/ontology/mappings/source.yaml
|
|
|
40
41
|
gitlab_api/ontology/migrations/manifest.json
|
|
41
42
|
gitlab_api/ontology/shapes/connector.shacl.ttl
|
|
42
43
|
gitlab_api/prompts/devops_specialist.json
|
|
44
|
+
gitlab_api/skills/gitlab-api-development/SKILL.md
|
|
43
45
|
gitlab_api/skills/gitlab-api-operations/SKILL.md
|
|
44
46
|
gitlab_api/skills/gitlab-api-operations/agents/openai.yaml
|
|
45
47
|
gitlab_api/skills/gitlab-api-operations/references/catalog.md
|
|
@@ -57,6 +59,7 @@ scripts/check_import_safety.py
|
|
|
57
59
|
scripts/security_contract.py
|
|
58
60
|
tests/__init__.py
|
|
59
61
|
tests/conftest.py
|
|
62
|
+
tests/test_access_contracts.py
|
|
60
63
|
tests/test_api_wrapper.py
|
|
61
64
|
tests/test_auth.py
|
|
62
65
|
tests/test_catalog_edges.py
|
|
@@ -65,6 +68,7 @@ tests/test_cq_paths.py
|
|
|
65
68
|
tests/test_fleet_name.py
|
|
66
69
|
tests/test_gitlab_a2a_validation.py
|
|
67
70
|
tests/test_gitlab_api_brute_force_coverage.py
|
|
71
|
+
tests/test_gitlab_api_development_skill.py
|
|
68
72
|
tests/test_gitlab_gql.py
|
|
69
73
|
tests/test_gitlab_mcp_validation.py
|
|
70
74
|
tests/test_gitlab_models.py
|
|
@@ -75,6 +79,7 @@ tests/test_instances.py
|
|
|
75
79
|
tests/test_kg_ingest.py
|
|
76
80
|
tests/test_mock_coverage.py
|
|
77
81
|
tests/test_ontology_parent_link.py
|
|
82
|
+
tests/test_pages_workflow.py
|
|
78
83
|
tests/test_startup.py
|
|
79
84
|
tests/test_tool_modes.py
|
|
80
85
|
tests/test_verify_agent.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "gitlab-api"
|
|
7
|
-
version = "27.3.
|
|
7
|
+
version = "27.3.15"
|
|
8
8
|
description = "GitLab API + MCP Server"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
classifiers = [ "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: MIT License", "Environment :: Console", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3",]
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""GitLab's read-only source access contracts stay source-local."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from agent_connector_sdk.access_contract import parse_access_contracts
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
ROOT = Path(__file__).resolve().parents[1]
|
|
11
|
+
CONTRACTS = ROOT / "gitlab_api" / "ontology" / "access_contracts.ttl"
|
|
12
|
+
KG = "http://knuckles.team/kg#"
|
|
13
|
+
|
|
14
|
+
EXPECTED = {
|
|
15
|
+
"GitLabGroup": ("gitlab_groups", "group", "id"),
|
|
16
|
+
"Project": ("gitlab_projects", "project", "id"),
|
|
17
|
+
"MergeRequest": ("gitlab_merge_requests", "merge_request", "id"),
|
|
18
|
+
"Pipeline": ("gitlab_pipelines", "pipeline", "id"),
|
|
19
|
+
"PipelineRun": ("gitlab_pipelines", "pipeline", "id"),
|
|
20
|
+
"Job": ("gitlab_jobs", "job", "id"),
|
|
21
|
+
"CheckRun": ("gitlab_jobs", "job", "id"),
|
|
22
|
+
"Issue": ("gitlab_issues", "issue", "id"),
|
|
23
|
+
"Epic": ("gitlab_epics", "epic", "id"),
|
|
24
|
+
"Milestone": ("gitlab_milestones", "milestone", "id"),
|
|
25
|
+
"Commit": ("gitlab_commits", "commit", "id"),
|
|
26
|
+
"Branch": ("gitlab_branches", "branch", "name"),
|
|
27
|
+
"Release": ("gitlab_releases", "release", "tag_name"),
|
|
28
|
+
"Runner": ("gitlab_runners", "runner", "id"),
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _contracts():
|
|
33
|
+
return parse_access_contracts(CONTRACTS.read_text(encoding="utf-8"))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_every_declared_gitlab_class_has_a_typed_read_contract() -> None:
|
|
37
|
+
contracts = _contracts()
|
|
38
|
+
by_class = {contract.class_iri.rsplit("#", 1)[-1]: contract for contract in contracts}
|
|
39
|
+
|
|
40
|
+
assert set(by_class) == set(EXPECTED)
|
|
41
|
+
for class_name, (operation, entity, key_field) in EXPECTED.items():
|
|
42
|
+
contract = by_class[class_name]
|
|
43
|
+
assert contract.class_iri == KG + class_name
|
|
44
|
+
assert contract.source_id == "gitlab-api"
|
|
45
|
+
assert contract.access_kind == "mcp_tool"
|
|
46
|
+
assert contract.operation == operation
|
|
47
|
+
assert contract.entity == entity
|
|
48
|
+
assert contract.key_field == key_field
|
|
49
|
+
assert contract.parameters == ("action", "params_json")
|
|
50
|
+
assert contract.auth_ref == "env://GITLAB_TOKEN"
|
|
51
|
+
assert contract.pagination == "page"
|
|
52
|
+
assert contract.pushdown == frozenset({"filter", "limit", "sort"})
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_gitlab_contracts_do_not_promote_draft_crosswalks() -> None:
|
|
56
|
+
contracts = _contracts()
|
|
57
|
+
|
|
58
|
+
assert all(contract.class_iri.startswith(KG) for contract in contracts)
|
|
59
|
+
assert all(
|
|
60
|
+
contract.class_iri not in {KG + "Incident", KG + "SoftwareProject"}
|
|
61
|
+
for contract in contracts
|
|
62
|
+
)
|
|
63
|
+
assert all(contract.source_id == "gitlab-api" for contract in contracts)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_gitlab_contract_artifact_is_packaged_under_ontology() -> None:
|
|
67
|
+
assert CONTRACTS.is_file()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_gitlab_contract_bindings_use_declared_fields() -> None:
|
|
71
|
+
contracts = {contract.class_iri.rsplit("#", 1)[-1]: contract for contract in _contracts()}
|
|
72
|
+
|
|
73
|
+
assert (KG + "webUrl", "web_url") in contracts["Project"].predicates
|
|
74
|
+
assert (KG + "pipelineStatus", "status") in contracts["Pipeline"].predicates
|
|
75
|
+
assert (KG + "sha", "sha") in contracts["Commit"].predicates
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""PIPE-CONNSPEC-R125.4: the GitLab API development skill is packaged."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
import tomllib
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
11
|
+
ROOT = Path(__file__).resolve().parents[1]
|
|
12
|
+
SKILL = ROOT / "gitlab_api" / "skills" / "gitlab-api-development" / "SKILL.md"
|
|
13
|
+
|
|
14
|
+
pytestmark = pytest.mark.spec("PIPE-CONNSPEC-R125.4")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_development_skill_has_rapid_contract_and_package_binding() -> None:
|
|
18
|
+
text = SKILL.read_text(encoding="utf-8")
|
|
19
|
+
frontmatter = text.split("---", 2)[1]
|
|
20
|
+
project = tomllib.loads((ROOT / "pyproject.toml").read_text(encoding="utf-8"))
|
|
21
|
+
|
|
22
|
+
assert SKILL.is_file()
|
|
23
|
+
assert re.search(r"^name:\s*gitlab-api-development\s*$", frontmatter, re.M)
|
|
24
|
+
assert re.search(r"^skill_type:\s*skill\s*$", frontmatter, re.M)
|
|
25
|
+
assert "## Rapid delivery: the 5-minute contract" in text
|
|
26
|
+
for term in (
|
|
27
|
+
"gitlab_api/__init__.py",
|
|
28
|
+
"gitlab_api/api_client.py",
|
|
29
|
+
"gitlab_api/api/",
|
|
30
|
+
"gitlab_api/auth.py",
|
|
31
|
+
"gitlab_api/instances.py",
|
|
32
|
+
"gitlab_api/mcp_server.py",
|
|
33
|
+
"gitlab_api/gitlab_gql.py",
|
|
34
|
+
"gitlab_api/kg_ingest.py",
|
|
35
|
+
"gitlab_api/connector_sources.py",
|
|
36
|
+
"GITLAB_URL",
|
|
37
|
+
"GITLAB_TOKEN",
|
|
38
|
+
"GITLAB_TLS_PROFILE",
|
|
39
|
+
"GITLAB_INSTANCES",
|
|
40
|
+
"ENABLE_DELEGATION",
|
|
41
|
+
"MCP_TOOL_MODE",
|
|
42
|
+
"skills/**",
|
|
43
|
+
"ontology/**",
|
|
44
|
+
"uv run --locked --extra test pytest",
|
|
45
|
+
):
|
|
46
|
+
assert term in text
|
|
47
|
+
|
|
48
|
+
assert "verify=False" not in text
|
|
49
|
+
assert "--no-verify" not in text
|
|
50
|
+
package_data = project["tool"]["setuptools"]["package-data"]["gitlab_api"]
|
|
51
|
+
assert "skills/**" in package_data
|
|
52
|
+
assert project["project"]["entry-points"]["agent_connector_sdk.connector"] == {
|
|
53
|
+
"gitlab-mcp": "gitlab_api:connector"
|
|
54
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""PIPE-CONNSPEC-R125.3: GitLab API Pages and spec-dashboard contract."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import re
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
11
|
+
ROOT = Path(__file__).resolve().parents[1]
|
|
12
|
+
WORKFLOW = ROOT / ".github/workflows/pages.yml"
|
|
13
|
+
DASHBOARD = ROOT / ".github/spec-dashboard.json"
|
|
14
|
+
|
|
15
|
+
pytestmark = pytest.mark.spec("PIPE-CONNSPEC-R125.3")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_pages_caller_enables_dashboard_and_rebuilds_for_spec_changes() -> None:
|
|
19
|
+
assert not (ROOT / ".github/workflows/docs.yml").exists()
|
|
20
|
+
|
|
21
|
+
workflow = WORKFLOW.read_text(encoding="utf-8")
|
|
22
|
+
|
|
23
|
+
assert "uses: Knuckles-Team/pipelines/.github/workflows/pages_pipeline.yml@main" in workflow
|
|
24
|
+
assert re.search(r"^ content_source: docs$", workflow, re.MULTILINE)
|
|
25
|
+
assert re.search(r"^ spec_dashboard_enabled: true$", workflow, re.MULTILINE)
|
|
26
|
+
assert re.search(
|
|
27
|
+
r"^ spec_dashboard_config: \.github/spec-dashboard\.json$",
|
|
28
|
+
workflow,
|
|
29
|
+
re.MULTILINE,
|
|
30
|
+
)
|
|
31
|
+
assert re.search(r"^ workflow_dispatch: \{\}$", workflow, re.MULTILINE)
|
|
32
|
+
for path in (
|
|
33
|
+
"'docs/**'",
|
|
34
|
+
"'mkdocs.yml'",
|
|
35
|
+
"'README.md'",
|
|
36
|
+
"'specs/**'",
|
|
37
|
+
"'.github/spec-dashboard.json'",
|
|
38
|
+
"'.github/workflows/pages.yml'",
|
|
39
|
+
):
|
|
40
|
+
assert path in workflow
|
|
41
|
+
|
|
42
|
+
assert re.search(r"(?m)^ pages: write$", workflow)
|
|
43
|
+
assert re.search(r"(?m)^ id-token: write$", workflow)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_dashboard_reads_only_canonical_spec_status_files() -> None:
|
|
47
|
+
config = json.loads(DASHBOARD.read_text(encoding="utf-8"))
|
|
48
|
+
|
|
49
|
+
assert config == {
|
|
50
|
+
"version": 1,
|
|
51
|
+
"sources": [{"glob": "specs/*/status.json", "kind": "spec"}],
|
|
52
|
+
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/skills/gitlab-api-operations/agents/openai.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/skills/gitlab-merge-request-create/SKILL.md
RENAMED
|
File without changes
|
{gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/skills/gitlab-merge-request-create/WORKFLOW.md
RENAMED
|
File without changes
|
{gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/skills/gitlab-merge-request-review/SKILL.md
RENAMED
|
File without changes
|
{gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/skills/gitlab-merge-request-review/WORKFLOW.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gitlab_api-27.3.13 → gitlab_api-27.3.15}/gitlab_api/skills/gitlab-vulnerabilities/WORKFLOW.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|