diff --git a/docs/api-objects.rst b/docs/api-objects.rst index e313bd8e8..a4e852be9 100644 --- a/docs/api-objects.rst +++ b/docs/api-objects.rst @@ -13,6 +13,7 @@ API examples gl_objects/branches gl_objects/clusters gl_objects/messages + gl_objects/ci_lint gl_objects/commits gl_objects/deploy_keys gl_objects/deploy_tokens diff --git a/docs/cli-examples.rst b/docs/cli-examples.rst index 5f4e0bca9..7f21f0308 100644 --- a/docs/cli-examples.rst +++ b/docs/cli-examples.rst @@ -6,6 +6,48 @@ CLI examples For a complete list of objects and actions available, see :doc:`/cli-objects`. +CI Lint +------- + +Lint a CI YAML configuration from a string: + +.. note:: + + To see output, you will need to use the ``-v``/``--verbose`` flag. + +.. code-block:: console + + $ gitlab --verbose ci-lint create --content \ + "--- + test: + script: + - echo hello + " + +Lint a CI YAML configuration from a file (see :ref:`cli_from_files`): + +.. code-block:: console + + $ gitlab --verbose ci-lint create --content @.gitlab-ci.yml + +Lint a project's CI YAML configuration: + +.. code-block:: console + + $ gitlab --verbose project-ci-lint create --project-id group/my-project --content @.gitlab-ci.yml + +Lint a project's current CI YAML configuration: + +.. code-block:: console + + $ gitlab --verbose project-ci-lint get --project-id group/my-project + +Lint a project's current CI YAML configuration on a specific branch: + +.. code-block:: console + + $ gitlab --verbose project-ci-lint get --project-id group/my-project --ref my-branch + Projects -------- diff --git a/docs/cli-usage.rst b/docs/cli-usage.rst index 6546f6674..5091ccba1 100644 --- a/docs/cli-usage.rst +++ b/docs/cli-usage.rst @@ -288,6 +288,8 @@ Example: $ gitlab -o yaml -f id,permissions -g elsewhere -c /tmp/gl.cfg project list +.. _cli_from_files: + Reading values from files ------------------------- diff --git a/docs/gl_objects/ci_lint.rst b/docs/gl_objects/ci_lint.rst new file mode 100644 index 000000000..6533db310 --- /dev/null +++ b/docs/gl_objects/ci_lint.rst @@ -0,0 +1,53 @@ +####### +CI Lint +####### + +Reference +--------- + +* v4 API: + + + :class:`gitlab.v4.objects.CiLint` + + :class:`gitlab.v4.objects.CiLintManager` + + :attr:`gitlab.Gitlab.ci_lint` + + :class:`gitlab.v4.objects.ProjectCiLint` + + :class:`gitlab.v4.objects.ProjectCiLintManager` + + :attr:`gitlab.v4.objects.Project.ci_lint` + +* GitLab API: https://docs.gitlab.com/ee/api/lint.html + +Examples +--------- + +Validate a CI YAML configuration:: + + gitlab_ci_yml = """.api_test: + rules: + - if: $CI_PIPELINE_SOURCE=="merge_request_event" + changes: + - src/api/* + deploy: + extends: + - .api_test + rules: + - when: manual + allow_failure: true + script: + - echo "hello world" + """ + lint_result = gl.ci_lint.create({"content": gitlab_ci_yml}) + + print(lint_result.status) # Print the status of the CI YAML + print(lint_result.merged_yaml) # Print the merged YAML file + +Validate a project's CI configuration:: + + lint_result = project.ci_lint.get() + assert lint_result.valid is True # Test that the .gitlab-ci.yml is valid + print(lint_result.merged_yaml) # Print the merged YAML file + +Validate a CI YAML configuration with a namespace:: + + lint_result = project.ci_lint.create({"content": gitlab_ci_yml}) + assert lint_result.valid is True # Test that the .gitlab-ci.yml is valid + print(lint_result.merged_yaml) # Print the merged YAML file diff --git a/docs/gl_objects/projects.rst b/docs/gl_objects/projects.rst index a6ed0e638..82ec77f52 100644 --- a/docs/gl_objects/projects.rst +++ b/docs/gl_objects/projects.rst @@ -819,49 +819,6 @@ Get total fetches in last 30 days of a project:: total_fetches = project.additionalstatistics.get().fetches['total'] -Project CI Lint -============================= - -Reference ---------- - -* v4 API: - - + :class:`gitlab.v4.objects.ProjectCiLint` - + :class:`gitlab.v4.objects.ProjectCiLintManager` - + :attr:`gitlab.v4.objects.Project.ci_lint` - -* GitLab API: https://docs.gitlab.com/ee/api/lint.html - -Examples ---------- - -Validate a project's CI configuration:: - - lint_result = project.ci_lint.get() - assert lint_result.valid is True # Test that the .gitlab-ci.yml is valid - print(lint_result.merged_yaml) # Print the merged YAML file - -Validate a CI YAML configuration with a namespace:: - - gitlab_ci_yml = """.api_test: - rules: - - if: $CI_PIPELINE_SOURCE=="merge_request_event" - changes: - - src/api/* - deploy: - extends: - - .api_test - rules: - - when: manual - allow_failure: true - script: - - echo "hello world" - """ - lint_result = project.ci_lint.create({"content": gitlab_ci_yml}) - assert lint_result.valid is True # Test that the .gitlab-ci.yml is valid - print(lint_result.merged_yaml) # Print the merged YAML file - Project storage ============================= diff --git a/gitlab/client.py b/gitlab/client.py index 82eebbc7d..54409c3cd 100644 --- a/gitlab/client.py +++ b/gitlab/client.py @@ -126,6 +126,8 @@ def __init__( self.broadcastmessages = objects.BroadcastMessageManager(self) """See :class:`~gitlab.v4.objects.BroadcastMessageManager`""" + self.ci_lint = objects.CiLintManager(self) + """See :class:`~gitlab.v4.objects.CiLintManager`""" self.deploykeys = objects.DeployKeyManager(self) """See :class:`~gitlab.v4.objects.DeployKeyManager`""" self.deploytokens = objects.DeployTokenManager(self) @@ -397,6 +399,11 @@ def lint(self, content: str, **kwargs: Any) -> Tuple[bool, List[str]]: Returns: (True, []) if the file is valid, (False, errors(list)) otherwise """ + utils.warn( + "`lint()` is deprecated and will be removed in a future version.\n" + "Please use `ci_lint.create()` instead.", + category=DeprecationWarning, + ) post_data = {"content": content} data = self.http_post("/ci/lint", post_data=post_data, **kwargs) if TYPE_CHECKING: diff --git a/gitlab/v4/objects/__init__.py b/gitlab/v4/objects/__init__.py index 40f9bf3fb..a390a4d5e 100644 --- a/gitlab/v4/objects/__init__.py +++ b/gitlab/v4/objects/__init__.py @@ -25,6 +25,7 @@ from .boards import * from .branches import * from .broadcast_messages import * +from .ci_lint import * from .clusters import * from .commits import * from .container_registry import * diff --git a/gitlab/v4/objects/ci_lint.py b/gitlab/v4/objects/ci_lint.py new file mode 100644 index 000000000..73f9d4d9d --- /dev/null +++ b/gitlab/v4/objects/ci_lint.py @@ -0,0 +1,45 @@ +""" +GitLab API: +https://docs.gitlab.com/ee/api/lint.html +""" + +from typing import Any, cast + +from gitlab.base import RESTManager, RESTObject +from gitlab.mixins import CreateMixin, GetWithoutIdMixin +from gitlab.types import RequiredOptional + +__all__ = [ + "CiLint", + "CiLintManager", + "ProjectCiLint", + "ProjectCiLintManager", +] + + +class CiLint(RESTObject): + _id_attr = None + + +class CiLintManager(CreateMixin, RESTManager): + _path = "/ci/lint" + _obj_cls = CiLint + _create_attrs = RequiredOptional( + required=("content",), optional=("include_merged_yaml", "include_jobs") + ) + + +class ProjectCiLint(RESTObject): + pass + + +class ProjectCiLintManager(GetWithoutIdMixin, CreateMixin, RESTManager): + _path = "/projects/{project_id}/ci/lint" + _obj_cls = ProjectCiLint + _from_parent_attrs = {"project_id": "id"} + _create_attrs = RequiredOptional( + required=("content",), optional=("dry_run", "include_jobs", "ref") + ) + + def get(self, **kwargs: Any) -> ProjectCiLint: + return cast(ProjectCiLint, super().get(**kwargs)) diff --git a/gitlab/v4/objects/projects.py b/gitlab/v4/objects/projects.py index 367ab6892..ab3fb2052 100644 --- a/gitlab/v4/objects/projects.py +++ b/gitlab/v4/objects/projects.py @@ -1,7 +1,6 @@ """ GitLab API: https://docs.gitlab.com/ee/api/projects.html -https://docs.gitlab.com/ee/api/lint.html """ from typing import ( Any, @@ -39,6 +38,7 @@ from .badges import ProjectBadgeManager # noqa: F401 from .boards import ProjectBoardManager # noqa: F401 from .branches import ProjectBranchManager, ProjectProtectedBranchManager # noqa: F401 +from .ci_lint import ProjectCiLintManager # noqa: F401 from .clusters import ProjectClusterManager # noqa: F401 from .commits import ProjectCommitManager # noqa: F401 from .container_registry import ProjectRegistryRepositoryManager # noqa: F401 @@ -102,8 +102,6 @@ "ProjectRemoteMirrorManager", "ProjectStorage", "ProjectStorageManager", - "ProjectCiLint", - "ProjectCiLintManager", ] @@ -165,7 +163,7 @@ class Project(RefreshMixin, SaveMixin, ObjectDeleteMixin, RepositoryMixin, RESTO badges: ProjectBadgeManager boards: ProjectBoardManager branches: ProjectBranchManager - ci_lint: "ProjectCiLintManager" + ci_lint: ProjectCiLintManager clusters: ProjectClusterManager commits: ProjectCommitManager customattributes: ProjectCustomAttributeManager @@ -1063,18 +1061,3 @@ class ProjectStorageManager(GetWithoutIdMixin, RESTManager): def get(self, **kwargs: Any) -> ProjectStorage: return cast(ProjectStorage, super().get(**kwargs)) - - -class ProjectCiLint(RESTObject): - pass - - -class ProjectCiLintManager(GetWithoutIdMixin, CreateMixin, RESTManager): - """GitLab API: https://docs.gitlab.com/ee/api/lint.html""" - - _path = "/projects/{project_id}/ci/lint" - _obj_cls = ProjectCiLint - _from_parent_attrs = {"project_id": "id"} - - def get(self, **kwargs: Any) -> ProjectCiLint: - return cast(ProjectCiLint, super().get(**kwargs)) diff --git a/tests/unit/objects/test_ci_lint.py b/tests/unit/objects/test_ci_lint.py new file mode 100644 index 000000000..509a5ed1b --- /dev/null +++ b/tests/unit/objects/test_ci_lint.py @@ -0,0 +1,71 @@ +import pytest +import responses + +gitlab_ci_yml = """--- +:test_job: + :script: echo 1 +""" + +ci_lint_create_content = {"status": "valid", "errors": [], "warnings": []} + + +project_ci_lint_content = { + "valid": True, + "merged_yaml": "---\n:test_job:\n :script: echo 1\n", + "errors": [], + "warnings": [], +} + + +@pytest.fixture +def resp_create_ci_lint(): + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.POST, + url="http://localhost/api/v4/ci/lint", + json=ci_lint_create_content, + content_type="application/json", + status=200, + ) + yield rsps + + +@pytest.fixture +def resp_get_project_ci_lint(): + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.GET, + url="http://localhost/api/v4/projects/1/ci/lint", + json=project_ci_lint_content, + content_type="application/json", + status=200, + ) + yield rsps + + +@pytest.fixture +def resp_create_project_ci_lint(): + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.POST, + url="http://localhost/api/v4/projects/1/ci/lint", + json=project_ci_lint_content, + content_type="application/json", + status=200, + ) + yield rsps + + +def test_ci_lint_create(gl, resp_create_ci_lint): + lint_result = gl.ci_lint.create({"content": gitlab_ci_yml}) + assert lint_result.status == "valid" + + +def test_project_ci_lint_get(project, resp_get_project_ci_lint): + lint_result = project.ci_lint.get() + assert lint_result.valid is True + + +def test_project_ci_lint_create(project, resp_create_project_ci_lint): + lint_result = project.ci_lint.create({"content": gitlab_ci_yml}) + assert lint_result.valid is True diff --git a/tests/unit/objects/test_projects.py b/tests/unit/objects/test_projects.py index 4db88356a..85bae8600 100644 --- a/tests/unit/objects/test_projects.py +++ b/tests/unit/objects/test_projects.py @@ -82,12 +82,6 @@ "status": "created", "source": "trigger", } -ci_lint_get_content = { - "valid": True, - "merged_yaml": "---\n:test_job:\n :script: echo 1\n", - "errors": [], - "warnings": [], -} @pytest.fixture @@ -547,32 +541,6 @@ def resp_artifact(): yield rsps -@pytest.fixture -def resp_get_ci_lint(): - with responses.RequestsMock() as rsps: - rsps.add( - method=responses.GET, - url="http://localhost/api/v4/projects/1/ci/lint", - json=ci_lint_get_content, - content_type="application/json", - status=200, - ) - yield rsps - - -@pytest.fixture -def resp_create_ci_lint(): - with responses.RequestsMock() as rsps: - rsps.add( - method=responses.POST, - url="http://localhost/api/v4/projects/1/ci/lint", - json=ci_lint_get_content, - content_type="application/json", - status=200, - ) - yield rsps - - def test_get_project(gl, resp_get_project): data = gl.projects.get(1) assert isinstance(data, Project) @@ -788,17 +756,3 @@ def test_project_pull_mirror(project, resp_start_pull_mirroring_project): def test_project_snapshot(project, resp_snapshot_project): tar_file = project.snapshot() assert isinstance(tar_file, bytes) - - -def test_project_ci_lint_get(project, resp_get_ci_lint): - lint_result = project.ci_lint.get() - assert lint_result.valid is True - - -def test_project_ci_lint_create(project, resp_create_ci_lint): - gitlab_ci_yml = """--- -:test_job: - :script: echo 1 -""" - lint_result = project.ci_lint.create({"content": gitlab_ci_yml}) - assert lint_result.valid is True
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: