diff --git a/docs/api-objects.rst b/docs/api-objects.rst index 5d5949702..8221f63b8 100644 --- a/docs/api-objects.rst +++ b/docs/api-objects.rst @@ -48,5 +48,6 @@ API examples gl_objects/templates gl_objects/todos gl_objects/users + gl_objects/variables gl_objects/sidekiq gl_objects/wikis diff --git a/docs/gl_objects/pipelines_and_jobs.rst b/docs/gl_objects/pipelines_and_jobs.rst index eb9e23a00..cc4db538f 100644 --- a/docs/gl_objects/pipelines_and_jobs.rst +++ b/docs/gl_objects/pipelines_and_jobs.rst @@ -184,58 +184,6 @@ Delete a schedule variable:: var.delete() -Projects and groups variables -============================= - -You can associate variables to projects and groups to modify the build/job -scripts behavior. - -Reference ---------- - -* v4 API - - + :class:`gitlab.v4.objects.ProjectVariable` - + :class:`gitlab.v4.objects.ProjectVariableManager` - + :attr:`gitlab.v4.objects.Project.variables` - + :class:`gitlab.v4.objects.GroupVariable` - + :class:`gitlab.v4.objects.GroupVariableManager` - + :attr:`gitlab.v4.objects.Group.variables` - -* GitLab API - - + https://docs.gitlab.com/ce/api/project_level_variables.html - + https://docs.gitlab.com/ce/api/group_level_variables.html - -Examples --------- - -List variables:: - - p_variables = project.variables.list() - g_variables = group.variables.list() - -Get a variable:: - - p_var = project.variables.get('key_name') - g_var = group.variables.get('key_name') - -Create a variable:: - - var = project.variables.create({'key': 'key1', 'value': 'value1'}) - var = group.variables.create({'key': 'key1', 'value': 'value1'}) - -Update a variable value:: - - var.value = 'new_value' - var.save() - -Remove a variable:: - - project.variables.delete('key_name') - group.variables.delete('key_name') - # or - var.delete() Jobs ==== diff --git a/docs/gl_objects/variables.rst b/docs/gl_objects/variables.rst new file mode 100644 index 000000000..e6ae4ba98 --- /dev/null +++ b/docs/gl_objects/variables.rst @@ -0,0 +1,102 @@ +############### +CI/CD Variables +############### + +You can configure variables at the instance-level (admin only), or associate +variables to projects and groups, to modify pipeline/job scripts behavior. + + +Instance-level variables +======================== + +This endpoint requires admin access. + +Reference +--------- + +* v4 API + + + :class:`gitlab.v4.objects.Variable` + + :class:`gitlab.v4.objects.VariableManager` + + :attr:`gitlab.Gitlab.variables` + +* GitLab API + + + https://docs.gitlab.com/ce/api/instance_level_ci_variables.html + +Examples +-------- + +List all instance variables:: + + variables = gl.variables.list() + +Get an instance variable by key:: + + variable = gl.variables.get('key_name') + +Create an instance variable:: + + variable = gl.variables.create({'key': 'key1', 'value': 'value1'}) + +Update a variable value:: + + variable.value = 'new_value' + variable.save() + +Remove a variable:: + + gl.variables.delete('key_name') + # or + variable.delete() + +Projects and groups variables +============================= + +Reference +--------- + +* v4 API + + + :class:`gitlab.v4.objects.ProjectVariable` + + :class:`gitlab.v4.objects.ProjectVariableManager` + + :attr:`gitlab.v4.objects.Project.variables` + + :class:`gitlab.v4.objects.GroupVariable` + + :class:`gitlab.v4.objects.GroupVariableManager` + + :attr:`gitlab.v4.objects.Group.variables` + +* GitLab API + + + https://docs.gitlab.com/ce/api/instance_level_ci_variables.html + + https://docs.gitlab.com/ce/api/project_level_variables.html + + https://docs.gitlab.com/ce/api/group_level_variables.html + +Examples +-------- + +List variables:: + + p_variables = project.variables.list() + g_variables = group.variables.list() + +Get a variable:: + + p_var = project.variables.get('key_name') + g_var = group.variables.get('key_name') + +Create a variable:: + + var = project.variables.create({'key': 'key1', 'value': 'value1'}) + var = group.variables.create({'key': 'key1', 'value': 'value1'}) + +Update a variable value:: + + var.value = 'new_value' + var.save() + +Remove a variable:: + + project.variables.delete('key_name') + group.variables.delete('key_name') + # or + var.delete() diff --git a/gitlab/__init__.py b/gitlab/__init__.py index 1959adcee..a1327e218 100644 --- a/gitlab/__init__.py +++ b/gitlab/__init__.py @@ -139,6 +139,7 @@ def __init__( self.pagesdomains = objects.PagesDomainManager(self) self.user_activities = objects.UserActivitiesManager(self) self.applications = objects.ApplicationManager(self) + self.variables = objects.VariableManager(self) def __enter__(self): return self diff --git a/gitlab/tests/objects/test_variables.py b/gitlab/tests/objects/test_variables.py new file mode 100644 index 000000000..d79bf96c3 --- /dev/null +++ b/gitlab/tests/objects/test_variables.py @@ -0,0 +1,193 @@ +""" +GitLab API: +https://docs.gitlab.com/ee/api/instance_level_ci_variables.html +https://docs.gitlab.com/ee/api/project_level_variables.html +https://docs.gitlab.com/ee/api/group_level_variables.html +""" + +import re + +import pytest +import responses + +from gitlab.v4.objects import GroupVariable, ProjectVariable, Variable + + +key = "TEST_VARIABLE_1" +value = "TEST_1" +new_value = "TEST_2" + +variable_content = { + "key": key, + "variable_type": "env_var", + "value": value, + "protected": False, + "masked": True, +} +variables_url = re.compile( + r"http://localhost/api/v4/(((groups|projects)/1)|(admin/ci))/variables" +) +variables_key_url = re.compile( + rf"http://localhost/api/v4/(((groups|projects)/1)|(admin/ci))/variables/{key}" +) + + +@pytest.fixture +def resp_list_variables(): + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.GET, + url=variables_url, + json=[variable_content], + content_type="application/json", + status=200, + ) + yield rsps + + +@pytest.fixture +def resp_get_variable(): + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.GET, + url=variables_key_url, + json=variable_content, + content_type="application/json", + status=200, + ) + yield rsps + + +@pytest.fixture +def resp_create_variable(): + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.POST, + url=variables_url, + json=variable_content, + content_type="application/json", + status=200, + ) + yield rsps + + +@pytest.fixture +def resp_update_variable(): + updated_content = dict(variable_content) + updated_content["value"] = new_value + + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.PUT, + url=variables_key_url, + json=updated_content, + content_type="application/json", + status=200, + ) + yield rsps + + +@pytest.fixture +def resp_delete_variable(no_content): + with responses.RequestsMock() as rsps: + rsps.add( + method=responses.DELETE, + url=variables_key_url, + json=no_content, + content_type="application/json", + status=204, + ) + yield rsps + + +def test_list_instance_variables(gl, resp_list_variables): + variables = gl.variables.list() + assert isinstance(variables, list) + assert isinstance(variables[0], Variable) + assert variables[0].value == value + + +def test_get_instance_variable(gl, resp_get_variable): + variable = gl.variables.get(key) + assert isinstance(variable, Variable) + assert variable.value == value + + +def test_create_instance_variable(gl, resp_create_variable): + variable = gl.variables.create({"key": key, "value": value}) + assert isinstance(variable, Variable) + assert variable.value == value + + +def test_update_instance_variable(gl, resp_update_variable): + variable = gl.variables.get(key, lazy=True) + variable.value = new_value + variable.save() + assert variable.value == new_value + + +def test_delete_instance_variable(gl, resp_delete_variable): + variable = gl.variables.get(key, lazy=True) + variable.delete() + + +def test_list_project_variables(project, resp_list_variables): + variables = project.variables.list() + assert isinstance(variables, list) + assert isinstance(variables[0], ProjectVariable) + assert variables[0].value == value + + +def test_get_project_variable(project, resp_get_variable): + variable = project.variables.get(key) + assert isinstance(variable, ProjectVariable) + assert variable.value == value + + +def test_create_project_variable(project, resp_create_variable): + variable = project.variables.create({"key": key, "value": value}) + assert isinstance(variable, ProjectVariable) + assert variable.value == value + + +def test_update_project_variable(project, resp_update_variable): + variable = project.variables.get(key, lazy=True) + variable.value = new_value + variable.save() + assert variable.value == new_value + + +def test_delete_project_variable(project, resp_delete_variable): + variable = project.variables.get(key, lazy=True) + variable.delete() + + +def test_list_group_variables(group, resp_list_variables): + variables = group.variables.list() + assert isinstance(variables, list) + assert isinstance(variables[0], GroupVariable) + assert variables[0].value == value + + +def test_get_group_variable(group, resp_get_variable): + variable = group.variables.get(key) + assert isinstance(variable, GroupVariable) + assert variable.value == value + + +def test_create_group_variable(group, resp_create_variable): + variable = group.variables.create({"key": key, "value": value}) + assert isinstance(variable, GroupVariable) + assert variable.value == value + + +def test_update_group_variable(group, resp_update_variable): + variable = group.variables.get(key, lazy=True) + variable.value = new_value + variable.save() + assert variable.value == new_value + + +def test_delete_group_variable(group, resp_delete_variable): + variable = group.variables.get(key, lazy=True) + variable.delete() diff --git a/gitlab/v4/objects.py b/gitlab/v4/objects/__init__.py similarity index 99% rename from gitlab/v4/objects.py rename to gitlab/v4/objects/__init__.py index eaf1cd05b..f9a2c25e6 100644 --- a/gitlab/v4/objects.py +++ b/gitlab/v4/objects/__init__.py @@ -23,6 +23,8 @@ from gitlab.mixins import * # noqa from gitlab import types from gitlab import utils +from gitlab.v4.objects.variables import * + VISIBILITY_PRIVATE = "private" VISIBILITY_INTERNAL = "internal" @@ -1366,18 +1368,6 @@ class GroupSubgroupManager(ListMixin, RESTManager): ) -class GroupVariable(SaveMixin, ObjectDeleteMixin, RESTObject): - _id_attr = "key" - - -class GroupVariableManager(CRUDMixin, RESTManager): - _path = "/groups/%(group_id)s/variables" - _obj_cls = GroupVariable - _from_parent_attrs = {"group_id": "id"} - _create_attrs = (("key", "value"), ("protected", "variable_type", "masked")) - _update_attrs = (("key", "value"), ("protected", "variable_type", "masked")) - - class Group(SaveMixin, ObjectDeleteMixin, RESTObject): _short_print_attr = "name" _managers = ( @@ -4116,18 +4106,6 @@ class ProjectUserManager(ListMixin, RESTManager): _list_filters = ("search",) -class ProjectVariable(SaveMixin, ObjectDeleteMixin, RESTObject): - _id_attr = "key" - - -class ProjectVariableManager(CRUDMixin, RESTManager): - _path = "/projects/%(project_id)s/variables" - _obj_cls = ProjectVariable - _from_parent_attrs = {"project_id": "id"} - _create_attrs = (("key", "value"), ("protected", "variable_type", "masked")) - _update_attrs = (("key", "value"), ("protected", "variable_type", "masked")) - - class ProjectService(SaveMixin, ObjectDeleteMixin, RESTObject): pass diff --git a/gitlab/v4/objects/variables.py b/gitlab/v4/objects/variables.py new file mode 100644 index 000000000..c8de80f81 --- /dev/null +++ b/gitlab/v4/objects/variables.py @@ -0,0 +1,49 @@ +""" +GitLab API: +https://docs.gitlab.com/ee/api/instance_level_ci_variables.html +https://docs.gitlab.com/ee/api/project_level_variables.html +https://docs.gitlab.com/ee/api/group_level_variables.html +""" +from gitlab.base import * # noqa +from gitlab.mixins import * # noqa + + +class Variable(SaveMixin, ObjectDeleteMixin, RESTObject): + _id_attr = "key" + + +class VariableManager(CRUDMixin, RESTManager): + _path = "/admin/ci/variables" + _obj_cls = Variable + _create_attrs = (("key", "value"), ("protected", "variable_type", "masked")) + _update_attrs = (("key", "value"), ("protected", "variable_type", "masked")) + + +class GroupVariable(SaveMixin, ObjectDeleteMixin, RESTObject): + _id_attr = "key" + + +class GroupVariableManager(CRUDMixin, RESTManager): + _path = "/groups/%(group_id)s/variables" + _obj_cls = GroupVariable + _from_parent_attrs = {"group_id": "id"} + _create_attrs = (("key", "value"), ("protected", "variable_type", "masked")) + _update_attrs = (("key", "value"), ("protected", "variable_type", "masked")) + + +class ProjectVariable(SaveMixin, ObjectDeleteMixin, RESTObject): + _id_attr = "key" + + +class ProjectVariableManager(CRUDMixin, RESTManager): + _path = "/projects/%(project_id)s/variables" + _obj_cls = ProjectVariable + _from_parent_attrs = {"project_id": "id"} + _create_attrs = ( + ("key", "value"), + ("protected", "variable_type", "masked", "environment_scope"), + ) + _update_attrs = ( + ("key", "value"), + ("protected", "variable_type", "masked", "environment_scope"), + ) diff --git a/tools/functional/api/test_variables.py b/tools/functional/api/test_variables.py new file mode 100644 index 000000000..d20ebba27 --- /dev/null +++ b/tools/functional/api/test_variables.py @@ -0,0 +1,48 @@ +""" +GitLab API: +https://docs.gitlab.com/ee/api/instance_level_ci_variables.html +https://docs.gitlab.com/ee/api/project_level_variables.html +https://docs.gitlab.com/ee/api/group_level_variables.html +""" + + +def test_instance_variables(gl): + variable = gl.variables.create({"key": "key1", "value": "value1"}) + assert variable.value == "value1" + assert len(gl.variables.list()) == 1 + + variable.value = "new_value1" + variable.save() + variable = gl.variables.get(variable.key) + assert variable.value == "new_value1" + + variable.delete() + assert len(gl.variables.list()) == 0 + + +def test_group_variables(group): + variable = group.variables.create({"key": "key1", "value": "value1"}) + assert variable.value == "value1" + assert len(group.variables.list()) == 1 + + variable.value = "new_value1" + variable.save() + variable = group.variables.get(variable.key) + assert variable.value == "new_value1" + + variable.delete() + assert len(group.variables.list()) == 0 + + +def test_project_variables(project): + variable = project.variables.create({"key": "key1", "value": "value1"}) + assert variable.value == "value1" + assert len(project.variables.list()) == 1 + + variable.value = "new_value1" + variable.save() + variable = project.variables.get(variable.key) + assert variable.value == "new_value1" + + variable.delete() + assert len(project.variables.list()) == 0 diff --git a/tools/functional/cli/conftest.py b/tools/functional/cli/conftest.py new file mode 100644 index 000000000..13c30962e --- /dev/null +++ b/tools/functional/cli/conftest.py @@ -0,0 +1,21 @@ +import pytest + + +@pytest.fixture +def gitlab_cli(script_runner, CONFIG): + """Wrapper fixture to help make test cases less verbose.""" + + def _gitlab_cli(subcommands): + """ + Return a script_runner.run method that takes a default gitlab + command, and subcommands passed as arguments inside test cases. + """ + command = ["gitlab", "--config-file", CONFIG] + + for subcommand in subcommands: + # ensure we get strings (e.g from IDs) + command.append(str(subcommand)) + + return script_runner.run(*command) + + return _gitlab_cli diff --git a/tools/functional/cli/test_cli_variables.py b/tools/functional/cli/test_cli_variables.py new file mode 100644 index 000000000..9b1b16d0c --- /dev/null +++ b/tools/functional/cli/test_cli_variables.py @@ -0,0 +1,19 @@ +def test_list_instance_variables(gitlab_cli, gl): + cmd = ["variable", "list"] + ret = gitlab_cli(cmd) + + assert ret.success + + +def test_list_group_variables(gitlab_cli, group): + cmd = ["group-variable", "list", "--group-id", group.id] + ret = gitlab_cli(cmd) + + assert ret.success + + +def test_list_project_variables(gitlab_cli, project): + cmd = ["project-variable", "list", "--project-id", project.id] + ret = gitlab_cli(cmd) + + assert ret.success diff --git a/tools/functional/conftest.py b/tools/functional/conftest.py index bd99fa9ab..e60fa3915 100644 --- a/tools/functional/conftest.py +++ b/tools/functional/conftest.py @@ -1,3 +1,5 @@ +import tempfile +from pathlib import Path from random import randint import pytest @@ -5,6 +7,9 @@ import gitlab +TEMP_DIR = tempfile.gettempdir() + + def random_id(): """ Helper to ensure new resource creation does not clash with @@ -17,27 +22,7 @@ def random_id(): @pytest.fixture(scope="session") def CONFIG(): - return "/tmp/python-gitlab.cfg" - - -@pytest.fixture -def gitlab_cli(script_runner, CONFIG): - """Wrapper fixture to help make test cases less verbose.""" - - def _gitlab_cli(subcommands): - """ - Return a script_runner.run method that takes a default gitlab - command, and subcommands passed as arguments inside test cases. - """ - command = ["gitlab", "--config-file", CONFIG] - - for subcommand in subcommands: - # ensure we get strings (e.g from IDs) - command.append(str(subcommand)) - - return script_runner.run(*command) - - return _gitlab_cli + return Path(TEMP_DIR) / "python-gitlab.cfg" @pytest.fixture(scope="session") diff --git a/tools/functional_tests.sh b/tools/functional_tests.sh index 87907c52d..9b91f0f72 100755 --- a/tools/functional_tests.sh +++ b/tools/functional_tests.sh @@ -18,4 +18,4 @@ setenv_script=$(dirname "$0")/build_test_env.sh || exit 1 BUILD_TEST_ENV_AUTO_CLEANUP=true . "$setenv_script" "$@" || exit 1 -pytest "$(dirname "$0")/functional/cli" +pytest --script-launch-mode=subprocess "$(dirname "$0")/functional/cli" diff --git a/tools/python_test_v4.py b/tools/python_test_v4.py index 21faf9e64..7ff97b67f 100644 --- a/tools/python_test_v4.py +++ b/tools/python_test_v4.py @@ -367,17 +367,6 @@ assert len(gm1.issues()) == 0 assert len(gm1.merge_requests()) == 0 -# group variables -group1.variables.create({"key": "foo", "value": "bar"}) -g_v = group1.variables.get("foo") -assert g_v.value == "bar" -g_v.value = "baz" -g_v.save() -g_v = group1.variables.get("foo") -assert g_v.value == "baz" -assert len(group1.variables.list()) == 1 -g_v.delete() -assert len(group1.variables.list()) == 0 # group labels # group1.labels.create({"name": "foo", "description": "bar", "color": "#112233"}) @@ -856,14 +845,6 @@ assert len(admin_project.triggers.list()) == 1 tr1.delete() -# variables -v1 = admin_project.variables.create({"key": "key1", "value": "value1"}) -assert len(admin_project.variables.list()) == 1 -v1.value = "new_value1" -v1.save() -v1 = admin_project.variables.get(v1.key) -assert v1.value == "new_value1" -v1.delete() # branches and merges to_merge = admin_project.branches.create({"branch": "branch1", "ref": "master"}) 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