Skip to content

Commit d15fea0

Browse files
authored
Merge pull request #1896 from python-gitlab/jlvillal/ci_lint
feat: add Project CI Lint support
2 parents 88a1535 + b213dd3 commit d15fea0

File tree

3 files changed

+112
-0
lines changed

3 files changed

+112
-0
lines changed

docs/gl_objects/projects.rst

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -819,6 +819,49 @@ Get total fetches in last 30 days of a project::
819819

820820
total_fetches = project.additionalstatistics.get().fetches['total']
821821

822+
Project CI Lint
823+
=============================
824+
825+
Reference
826+
---------
827+
828+
* v4 API:
829+
830+
+ :class:`gitlab.v4.objects.ProjectCiLint`
831+
+ :class:`gitlab.v4.objects.ProjectCiLintManager`
832+
+ :attr:`gitlab.v4.objects.Project.ci_lint`
833+
834+
* GitLab API: https://docs.gitlab.com/ee/api/lint.html
835+
836+
Examples
837+
---------
838+
839+
Validate a project's CI configuration::
840+
841+
lint_result = project.ci_lint.get()
842+
assert lint_result.valid is True # Test that the .gitlab-ci.yml is valid
843+
print(lint_result.merged_yaml) # Print the merged YAML file
844+
845+
Validate a CI YAML configuration with a namespace::
846+
847+
gitlab_ci_yml = """.api_test:
848+
rules:
849+
- if: $CI_PIPELINE_SOURCE=="merge_request_event"
850+
changes:
851+
- src/api/*
852+
deploy:
853+
extends:
854+
- .api_test
855+
rules:
856+
- when: manual
857+
allow_failure: true
858+
script:
859+
- echo "hello world"
860+
"""
861+
lint_result = project.ci_lint.create({"content": gitlab_ci_yml})
862+
assert lint_result.valid is True # Test that the .gitlab-ci.yml is valid
863+
print(lint_result.merged_yaml) # Print the merged YAML file
864+
822865
Project storage
823866
=============================
824867

gitlab/v4/objects/projects.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
"""
2+
GitLab API:
3+
https://docs.gitlab.com/ee/api/projects.html
4+
https://docs.gitlab.com/ee/api/lint.html
5+
"""
16
from typing import (
27
Any,
38
Callable,
@@ -97,6 +102,8 @@
97102
"ProjectRemoteMirrorManager",
98103
"ProjectStorage",
99104
"ProjectStorageManager",
105+
"ProjectCiLint",
106+
"ProjectCiLintManager",
100107
]
101108

102109

@@ -158,6 +165,7 @@ class Project(RefreshMixin, SaveMixin, ObjectDeleteMixin, RepositoryMixin, RESTO
158165
badges: ProjectBadgeManager
159166
boards: ProjectBoardManager
160167
branches: ProjectBranchManager
168+
ci_lint: "ProjectCiLintManager"
161169
clusters: ProjectClusterManager
162170
commits: ProjectCommitManager
163171
customattributes: ProjectCustomAttributeManager
@@ -1055,3 +1063,18 @@ class ProjectStorageManager(GetWithoutIdMixin, RESTManager):
10551063

10561064
def get(self, **kwargs: Any) -> ProjectStorage:
10571065
return cast(ProjectStorage, super().get(**kwargs))
1066+
1067+
1068+
class ProjectCiLint(RESTObject):
1069+
pass
1070+
1071+
1072+
class ProjectCiLintManager(GetWithoutIdMixin, CreateMixin, RESTManager):
1073+
"""GitLab API: https://docs.gitlab.com/ee/api/lint.html"""
1074+
1075+
_path = "/projects/{project_id}/ci/lint"
1076+
_obj_cls = ProjectCiLint
1077+
_from_parent_attrs = {"project_id": "id"}
1078+
1079+
def get(self, **kwargs: Any) -> ProjectCiLint:
1080+
return cast(ProjectCiLint, super().get(**kwargs))

tests/unit/objects/test_projects.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@
8282
"status": "created",
8383
"source": "trigger",
8484
}
85+
ci_lint_get_content = {
86+
"valid": True,
87+
"merged_yaml": "---\n:test_job:\n :script: echo 1\n",
88+
"errors": [],
89+
"warnings": [],
90+
}
8591

8692

8793
@pytest.fixture
@@ -541,6 +547,32 @@ def resp_artifact():
541547
yield rsps
542548

543549

550+
@pytest.fixture
551+
def resp_get_ci_lint():
552+
with responses.RequestsMock() as rsps:
553+
rsps.add(
554+
method=responses.GET,
555+
url="http://localhost/api/v4/projects/1/ci/lint",
556+
json=ci_lint_get_content,
557+
content_type="application/json",
558+
status=200,
559+
)
560+
yield rsps
561+
562+
563+
@pytest.fixture
564+
def resp_create_ci_lint():
565+
with responses.RequestsMock() as rsps:
566+
rsps.add(
567+
method=responses.POST,
568+
url="http://localhost/api/v4/projects/1/ci/lint",
569+
json=ci_lint_get_content,
570+
content_type="application/json",
571+
status=200,
572+
)
573+
yield rsps
574+
575+
544576
def test_get_project(gl, resp_get_project):
545577
data = gl.projects.get(1)
546578
assert isinstance(data, Project)
@@ -756,3 +788,17 @@ def test_project_pull_mirror(project, resp_start_pull_mirroring_project):
756788
def test_project_snapshot(project, resp_snapshot_project):
757789
tar_file = project.snapshot()
758790
assert isinstance(tar_file, bytes)
791+
792+
793+
def test_project_ci_lint_get(project, resp_get_ci_lint):
794+
lint_result = project.ci_lint.get()
795+
assert lint_result.valid is True
796+
797+
798+
def test_project_ci_lint_create(project, resp_create_ci_lint):
799+
gitlab_ci_yml = """---
800+
:test_job:
801+
:script: echo 1
802+
"""
803+
lint_result = project.ci_lint.create({"content": gitlab_ci_yml})
804+
assert lint_result.valid is True

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy