summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.commitlintrc.json3
-rw-r--r--.dockerignore5
-rw-r--r--.github/ISSUE_TEMPLATE.md14
-rw-r--r--.github/workflows/docs.yml43
-rw-r--r--.github/workflows/lint.yml36
-rw-r--r--.github/workflows/release.yml20
-rw-r--r--.github/workflows/test.yml89
-rw-r--r--.gitignore26
-rw-r--r--.gitlab-ci.yml27
-rw-r--r--.pre-commit-config.yaml29
-rw-r--r--.readthedocs.yml13
-rw-r--r--.renovaterc.json33
-rw-r--r--AUTHORS17
-rw-r--r--CHANGELOG.md1058
-rw-r--r--CONTRIBUTING.rst160
-rw-r--r--COPYING165
-rw-r--r--Dockerfile17
-rw-r--r--MANIFEST.in4
-rw-r--r--README.rst97
-rw-r--r--codecov.yml15
-rwxr-xr-xdocker-entrypoint.sh22
-rw-r--r--docs/Makefile177
-rw-r--r--docs/__init__.py0
-rw-r--r--docs/_templates/breadcrumbs.html24
-rw-r--r--docs/api-objects.rst58
-rw-r--r--docs/api-usage.rst458
-rw-r--r--docs/api/gitlab.rst87
-rw-r--r--docs/api/gitlab.v4.rst22
-rw-r--r--docs/changelog.md2
-rw-r--r--docs/cli-objects.rst17
-rw-r--r--docs/cli-usage.rst484
-rw-r--r--docs/conf.py295
-rw-r--r--docs/ext/__init__.py0
-rw-r--r--docs/ext/docstrings.py56
-rw-r--r--docs/ext/manager_tmpl.j238
-rw-r--r--docs/faq.rst38
-rw-r--r--docs/gl_objects/access_requests.rst53
-rw-r--r--docs/gl_objects/appearance.rst26
-rw-r--r--docs/gl_objects/applications.rst31
-rw-r--r--docs/gl_objects/badges.rst52
-rw-r--r--docs/gl_objects/boards.rst104
-rw-r--r--docs/gl_objects/branches.rst42
-rw-r--r--docs/gl_objects/clusters.rst82
-rw-r--r--docs/gl_objects/commits.rst147
-rw-r--r--docs/gl_objects/deploy_keys.rst70
-rw-r--r--docs/gl_objects/deploy_tokens.rst137
-rw-r--r--docs/gl_objects/deployments.rst63
-rw-r--r--docs/gl_objects/discussions.rst107
-rw-r--r--docs/gl_objects/emojis.rst45
-rw-r--r--docs/gl_objects/environments.rst44
-rw-r--r--docs/gl_objects/epics.rst79
-rw-r--r--docs/gl_objects/events.rst83
-rw-r--r--docs/gl_objects/features.rst32
-rw-r--r--docs/gl_objects/geo_nodes.rst43
-rw-r--r--docs/gl_objects/groups.rst378
-rw-r--r--docs/gl_objects/issues.rst279
-rw-r--r--docs/gl_objects/keys.rst28
-rw-r--r--docs/gl_objects/labels.rst89
-rw-r--r--docs/gl_objects/messages.rst48
-rw-r--r--docs/gl_objects/milestones.rst105
-rw-r--r--docs/gl_objects/mr_approvals.rst86
-rw-r--r--docs/gl_objects/mrs.rst217
-rw-r--r--docs/gl_objects/namespaces.rst25
-rw-r--r--docs/gl_objects/notes.rst63
-rw-r--r--docs/gl_objects/notifications.rst59
-rw-r--r--docs/gl_objects/packages.rst131
-rw-r--r--docs/gl_objects/pagesdomains.rst65
-rw-r--r--docs/gl_objects/personal_access_tokens.rst54
-rw-r--r--docs/gl_objects/pipelines_and_jobs.rst355
-rw-r--r--docs/gl_objects/project_access_tokens.rst34
-rw-r--r--docs/gl_objects/projects.rst768
-rw-r--r--docs/gl_objects/protected_branches.rst51
-rw-r--r--docs/gl_objects/releases.rst83
-rw-r--r--docs/gl_objects/remote_mirrors.rst34
-rw-r--r--docs/gl_objects/repositories.rst28
-rw-r--r--docs/gl_objects/repository_tags.rst47
-rw-r--r--docs/gl_objects/runners.rst137
-rw-r--r--docs/gl_objects/search.rst77
-rw-r--r--docs/gl_objects/settings.rst26
-rw-r--r--docs/gl_objects/sidekiq.rst23
-rw-r--r--docs/gl_objects/snippets.rst66
-rw-r--r--docs/gl_objects/system_hooks.rst35
-rw-r--r--docs/gl_objects/templates.rst114
-rw-r--r--docs/gl_objects/todos.rst44
-rw-r--r--docs/gl_objects/users.rst404
-rw-r--r--docs/gl_objects/variables.rst104
-rw-r--r--docs/gl_objects/wikis.rst56
-rw-r--r--docs/index.rst30
-rw-r--r--docs/install.rst26
-rw-r--r--docs/make.bat242
-rw-r--r--docs/release-notes.rst221
-rw-r--r--gitlab/__init__.py34
-rw-r--r--gitlab/__main__.py4
-rw-r--r--gitlab/__version__.py6
-rw-r--r--gitlab/base.py331
-rw-r--r--gitlab/cli.py260
-rw-r--r--gitlab/client.py1011
-rw-r--r--gitlab/config.py249
-rw-r--r--gitlab/const.py58
-rw-r--r--gitlab/exceptions.py310
-rw-r--r--gitlab/mixins.py928
-rw-r--r--gitlab/py.typed0
-rw-r--r--gitlab/types.py64
-rw-r--r--gitlab/utils.py70
-rw-r--r--gitlab/v4/__init__.py0
-rw-r--r--gitlab/v4/cli.py500
-rw-r--r--gitlab/v4/objects/__init__.py77
-rw-r--r--gitlab/v4/objects/access_requests.py35
-rw-r--r--gitlab/v4/objects/appearance.py52
-rw-r--r--gitlab/v4/objects/applications.py20
-rw-r--r--gitlab/v4/objects/audit_events.py57
-rw-r--r--gitlab/v4/objects/award_emojis.py103
-rw-r--r--gitlab/v4/objects/badges.py33
-rw-r--r--gitlab/v4/objects/boards.py59
-rw-r--r--gitlab/v4/objects/branches.py42
-rw-r--r--gitlab/v4/objects/broadcast_messages.py23
-rw-r--r--gitlab/v4/objects/clusters.py98
-rw-r--r--gitlab/v4/objects/commits.py200
-rw-r--r--gitlab/v4/objects/container_registry.py58
-rw-r--r--gitlab/v4/objects/custom_attributes.py41
-rw-r--r--gitlab/v4/objects/deploy_keys.py48
-rw-r--r--gitlab/v4/objects/deploy_tokens.py63
-rw-r--r--gitlab/v4/objects/deployments.py30
-rw-r--r--gitlab/v4/objects/discussions.py69
-rw-r--r--gitlab/v4/objects/environments.py43
-rw-r--r--gitlab/v4/objects/epics.py104
-rw-r--r--gitlab/v4/objects/events.py130
-rw-r--r--gitlab/v4/objects/export_import.py54
-rw-r--r--gitlab/v4/objects/features.py59
-rw-r--r--gitlab/v4/objects/files.py228
-rw-r--r--gitlab/v4/objects/geo_nodes.py93
-rw-r--r--gitlab/v4/objects/groups.py334
-rw-r--r--gitlab/v4/objects/hooks.py114
-rw-r--r--gitlab/v4/objects/issues.py256
-rw-r--r--gitlab/v4/objects/jobs.py190
-rw-r--r--gitlab/v4/objects/keys.py26
-rw-r--r--gitlab/v4/objects/labels.py149
-rw-r--r--gitlab/v4/objects/ldap.py51
-rw-r--r--gitlab/v4/objects/members.py92
-rw-r--r--gitlab/v4/objects/merge_request_approvals.py206
-rw-r--r--gitlab/v4/objects/merge_requests.py439
-rw-r--r--gitlab/v4/objects/milestones.py164
-rw-r--r--gitlab/v4/objects/namespaces.py17
-rw-r--r--gitlab/v4/objects/notes.py169
-rw-r--r--gitlab/v4/objects/notification_settings.py57
-rw-r--r--gitlab/v4/objects/packages.py168
-rw-r--r--gitlab/v4/objects/pages.py32
-rw-r--r--gitlab/v4/objects/personal_access_tokens.py32
-rw-r--r--gitlab/v4/objects/pipelines.py227
-rw-r--r--gitlab/v4/objects/project_access_tokens.py17
-rw-r--r--gitlab/v4/objects/projects.py1047
-rw-r--r--gitlab/v4/objects/push_rules.py50
-rw-r--r--gitlab/v4/objects/releases.py41
-rw-r--r--gitlab/v4/objects/repositories.py207
-rw-r--r--gitlab/v4/objects/runners.py140
-rw-r--r--gitlab/v4/objects/services.py303
-rw-r--r--gitlab/v4/objects/settings.py109
-rw-r--r--gitlab/v4/objects/sidekiq.py83
-rw-r--r--gitlab/v4/objects/snippets.py123
-rw-r--r--gitlab/v4/objects/statistics.py52
-rw-r--r--gitlab/v4/objects/tags.py37
-rw-r--r--gitlab/v4/objects/templates.py51
-rw-r--r--gitlab/v4/objects/todos.py50
-rw-r--r--gitlab/v4/objects/triggers.py19
-rw-r--r--gitlab/v4/objects/users.py514
-rw-r--r--gitlab/v4/objects/variables.py66
-rw-r--r--gitlab/v4/objects/wikis.py41
-rw-r--r--pyproject.toml34
-rw-r--r--requirements-docker.txt5
-rw-r--r--requirements-docs.txt6
-rw-r--r--requirements-lint.txt6
-rw-r--r--requirements-test.txt6
-rw-r--r--requirements.txt2
-rw-r--r--setup.cfg3
-rw-r--r--setup.py54
-rw-r--r--tests/__init__.py0
-rw-r--r--tests/functional/__init__.py0
-rw-r--r--tests/functional/api/__init__.py0
-rw-r--r--tests/functional/api/test_clusters.py46
-rw-r--r--tests/functional/api/test_current_user.py42
-rw-r--r--tests/functional/api/test_deploy_keys.py12
-rw-r--r--tests/functional/api/test_deploy_tokens.py36
-rw-r--r--tests/functional/api/test_gitlab.py183
-rw-r--r--tests/functional/api/test_groups.py223
-rw-r--r--tests/functional/api/test_import_export.py66
-rw-r--r--tests/functional/api/test_issues.py93
-rw-r--r--tests/functional/api/test_keys.py42
-rw-r--r--tests/functional/api/test_merge_requests.py205
-rw-r--r--tests/functional/api/test_packages.py62
-rw-r--r--tests/functional/api/test_projects.py268
-rw-r--r--tests/functional/api/test_releases.py63
-rw-r--r--tests/functional/api/test_repository.py126
-rw-r--r--tests/functional/api/test_snippets.py74
-rw-r--r--tests/functional/api/test_users.py170
-rw-r--r--tests/functional/api/test_variables.py48
-rw-r--r--tests/functional/cli/__init__.py0
-rw-r--r--tests/functional/cli/conftest.py21
-rw-r--r--tests/functional/cli/test_cli_artifacts.py49
-rw-r--r--tests/functional/cli/test_cli_packages.py60
-rw-r--r--tests/functional/cli/test_cli_v4.py715
-rw-r--r--tests/functional/cli/test_cli_variables.py19
-rw-r--r--tests/functional/conftest.py489
-rwxr-xr-xtests/functional/ee-test.py158
-rw-r--r--tests/functional/fixtures/.env2
-rw-r--r--tests/functional/fixtures/avatar.pngbin592 -> 0 bytes
-rw-r--r--tests/functional/fixtures/docker-compose.yml46
-rw-r--r--tests/functional/fixtures/set_token.rb9
-rw-r--r--tests/smoke/__init__.py0
-rw-r--r--tests/smoke/test_dists.py33
-rw-r--r--tests/unit/__init__.py0
-rw-r--r--tests/unit/conftest.py84
-rw-r--r--tests/unit/data/todo.json75
-rw-r--r--tests/unit/mixins/__init__.py0
-rw-r--r--tests/unit/mixins/test_meta_mixins.py58
-rw-r--r--tests/unit/mixins/test_mixin_methods.py300
-rw-r--r--tests/unit/mixins/test_object_mixins_attributes.py79
-rw-r--r--tests/unit/objects/__init__.py0
-rw-r--r--tests/unit/objects/conftest.py70
-rw-r--r--tests/unit/objects/test_appearance.py65
-rw-r--r--tests/unit/objects/test_applications.py44
-rw-r--r--tests/unit/objects/test_audit_events.py109
-rw-r--r--tests/unit/objects/test_badges.py210
-rw-r--r--tests/unit/objects/test_bridges.py109
-rw-r--r--tests/unit/objects/test_commits.py115
-rw-r--r--tests/unit/objects/test_deploy_tokens.py45
-rw-r--r--tests/unit/objects/test_deployments.py50
-rw-r--r--tests/unit/objects/test_environments.py30
-rw-r--r--tests/unit/objects/test_groups.py155
-rw-r--r--tests/unit/objects/test_hooks.py209
-rw-r--r--tests/unit/objects/test_issues.py88
-rw-r--r--tests/unit/objects/test_job_artifacts.py30
-rw-r--r--tests/unit/objects/test_jobs.py96
-rw-r--r--tests/unit/objects/test_keys.py54
-rw-r--r--tests/unit/objects/test_members.py58
-rw-r--r--tests/unit/objects/test_merge_request_pipelines.py53
-rw-r--r--tests/unit/objects/test_merge_requests.py56
-rw-r--r--tests/unit/objects/test_mro.py122
-rw-r--r--tests/unit/objects/test_packages.py252
-rw-r--r--tests/unit/objects/test_personal_access_tokens.py94
-rw-r--r--tests/unit/objects/test_pipeline_schedules.py62
-rw-r--r--tests/unit/objects/test_pipelines.py146
-rw-r--r--tests/unit/objects/test_project_access_tokens.py113
-rw-r--r--tests/unit/objects/test_project_import_export.py112
-rw-r--r--tests/unit/objects/test_project_merge_request_approvals.py317
-rw-r--r--tests/unit/objects/test_project_statistics.py28
-rw-r--r--tests/unit/objects/test_projects.py237
-rw-r--r--tests/unit/objects/test_releases.py170
-rw-r--r--tests/unit/objects/test_remote_mirrors.py72
-rw-r--r--tests/unit/objects/test_repositories.py49
-rw-r--r--tests/unit/objects/test_resource_label_events.py105
-rw-r--r--tests/unit/objects/test_resource_milestone_events.py73
-rw-r--r--tests/unit/objects/test_resource_state_events.py104
-rw-r--r--tests/unit/objects/test_runners.py282
-rw-r--r--tests/unit/objects/test_services.py93
-rw-r--r--tests/unit/objects/test_snippets.py89
-rw-r--r--tests/unit/objects/test_submodules.py46
-rw-r--r--tests/unit/objects/test_todos.py62
-rw-r--r--tests/unit/objects/test_users.py217
-rw-r--r--tests/unit/objects/test_variables.py192
-rw-r--r--tests/unit/test_base.py179
-rw-r--r--tests/unit/test_cli.py157
-rw-r--r--tests/unit/test_config.py317
-rw-r--r--tests/unit/test_exceptions.py18
-rw-r--r--tests/unit/test_gitlab.py196
-rw-r--r--tests/unit/test_gitlab_auth.py85
-rw-r--r--tests/unit/test_gitlab_http_methods.py406
-rw-r--r--tests/unit/test_types.py74
-rw-r--r--tests/unit/test_utils.py42
-rw-r--r--tox.ini102
269 files changed, 1 insertions, 31168 deletions
diff --git a/.commitlintrc.json b/.commitlintrc.json
deleted file mode 100644
index c30e5a9..0000000
--- a/.commitlintrc.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "extends": ["@commitlint/config-conventional"]
-}
diff --git a/.dockerignore b/.dockerignore
deleted file mode 100644
index 204be74..0000000
--- a/.dockerignore
+++ /dev/null
@@ -1,5 +0,0 @@
-venv/
-dist/
-build/
-*.egg-info
-.github/
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
deleted file mode 100644
index 8622f94..0000000
--- a/.github/ISSUE_TEMPLATE.md
+++ /dev/null
@@ -1,14 +0,0 @@
-## Description of the problem, including code/CLI snippet
-
-
-## Expected Behavior
-
-
-## Actual Behavior
-
-
-## Specifications
-
- - python-gitlab version:
- - API version you are using (v3/v4):
- - Gitlab server version (or gitlab.com):
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
deleted file mode 100644
index b5a413d..0000000
--- a/.github/workflows/docs.yml
+++ /dev/null
@@ -1,43 +0,0 @@
-name: Docs
-
-on:
- push:
- branches:
- - master
- pull_request:
- branches:
- - master
-
-env:
- PY_COLORS: 1
-
-jobs:
- sphinx:
- runs-on: ubuntu-20.04
- steps:
- - uses: actions/checkout@v2
- - name: Set up Python
- uses: actions/setup-python@v2
- with:
- python-version: "3.10"
- - name: Install dependencies
- run: pip install tox
- - name: Build docs
- env:
- TOXENV: docs
- run: tox
-
- twine-check:
- runs-on: ubuntu-20.04
- steps:
- - uses: actions/checkout@v2
- - name: Set up Python
- uses: actions/setup-python@v2
- with:
- python-version: "3.10"
- - name: Install dependencies
- run: pip install tox twine wheel
- - name: Check twine readme rendering
- env:
- TOXENV: twine-check
- run: tox
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
deleted file mode 100644
index 4f04e7b..0000000
--- a/.github/workflows/lint.yml
+++ /dev/null
@@ -1,36 +0,0 @@
-name: Lint
-
-on:
- push:
- branches:
- - master
- pull_request:
- branches:
- - master
-
-env:
- PY_COLORS: 1
-
-jobs:
- commitlint:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
- with:
- fetch-depth: 0
- - uses: wagoid/commitlint-github-action@v4
-
- linters:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-python@v2
- - run: pip install --upgrade tox
- - name: Run black code formatter (https://black.readthedocs.io/en/stable/)
- run: tox -e black -- --check
- - name: Run flake8 (https://flake8.pycqa.org/en/latest/)
- run: tox -e pep8
- - name: Run mypy static typing checker (http://mypy-lang.org/)
- run: tox -e mypy
- - name: Run isort import order checker (https://pycqa.github.io/isort/)
- run: tox -e isort -- --check
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
deleted file mode 100644
index ade71ef..0000000
--- a/.github/workflows/release.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-name: Release
-
-on:
- schedule:
- - cron: '0 0 28 * *' # Monthly auto-release
- workflow_dispatch: # Manual trigger for quick fixes
-
-jobs:
- release:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
- with:
- fetch-depth: 0
- token: ${{ secrets.RELEASE_GITHUB_TOKEN }}
- - name: Python Semantic Release
- uses: relekang/python-semantic-release@master
- with:
- github_token: ${{ secrets.RELEASE_GITHUB_TOKEN }}
- pypi_token: ${{ secrets.PYPI_TOKEN }}
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
deleted file mode 100644
index 43ea68a..0000000
--- a/.github/workflows/test.yml
+++ /dev/null
@@ -1,89 +0,0 @@
-name: Test
-
-on:
- push:
- branches:
- - master
- pull_request:
- branches:
- - master
-
-env:
- PY_COLORS: 1
-
-jobs:
- unit:
- runs-on: ubuntu-20.04
- strategy:
- matrix:
- include:
- - python-version: 3.6
- toxenv: py36
- - python-version: 3.7
- toxenv: py37
- - python-version: 3.8
- toxenv: py38
- - python-version: 3.9
- toxenv: py39
- - python-version: "3.10"
- toxenv: py310
- - python-version: "3.10"
- toxenv: smoke
- steps:
- - uses: actions/checkout@v2
- - name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v2
- with:
- python-version: ${{ matrix.python-version }}
- - name: Install dependencies
- run: pip install tox pytest-github-actions-annotate-failures
- - name: Run tests
- env:
- TOXENV: ${{ matrix.toxenv }}
- run: tox
-
- functional:
- runs-on: ubuntu-20.04
- strategy:
- matrix:
- toxenv: [py_func_v4, cli_func_v4]
- steps:
- - uses: actions/checkout@v2
- - name: Set up Python
- uses: actions/setup-python@v2
- with:
- python-version: "3.10"
- - name: Install dependencies
- run: pip install tox pytest-github-actions-annotate-failures
- - name: Run tests
- env:
- TOXENV: ${{ matrix.toxenv }}
- run: tox
- - name: Upload codecov coverage
- uses: codecov/codecov-action@v2
- with:
- files: ./coverage.xml
- flags: ${{ matrix.toxenv }}
- fail_ci_if_error: true
-
- coverage:
- runs-on: ubuntu-20.04
- steps:
- - uses: actions/checkout@v2
- - name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v2
- with:
- python-version: "3.10"
- - name: Install dependencies
- run: pip install tox pytest-github-actions-annotate-failures
- - name: Run tests
- env:
- PY_COLORS: 1
- TOXENV: cover
- run: tox
- - name: Upload codecov coverage
- uses: codecov/codecov-action@v2
- with:
- files: ./coverage.xml
- flags: unit
- fail_ci_if_error: true
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 46c189f..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,26 +0,0 @@
-*.pyc
-build/
-dist/
-htmlcov/
-MANIFEST
-.*.swp
-*.egg-info
-.idea/
-coverage.xml
-docs/_build
-.coverage
-.python-version
-.tox
-.venv/
-venv/
-
-# Include tracked hidden files and directories in search and diff tools
-!.commitlintrc.json
-!.dockerignore
-!.github/
-!.gitignore
-!.gitlab-ci.yml
-!.mypy.ini
-!.pre-commit-config.yaml
-!.readthedocs.yml
-!.renovaterc.json
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
deleted file mode 100644
index d628e5b..0000000
--- a/.gitlab-ci.yml
+++ /dev/null
@@ -1,27 +0,0 @@
-image: python:3.10
-
-stages:
- - deploy
- - deploy-latest
-
-deploy_image:
- stage: deploy
- image:
- name: gcr.io/kaniko-project/executor:debug
- entrypoint: [""]
- script:
- - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
- - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
- only:
- - tags
-
-deploy-latest:
- stage: deploy-latest
- image:
- name: gcr.io/go-containerregistry/crane:debug
- entrypoint: [""]
- script:
- - mkdir /root/.docker && echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /root/.docker/config.json
- - /ko-app/crane cp $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG $CI_REGISTRY_IMAGE:latest
- only:
- - tags
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
deleted file mode 100644
index 5731e69..0000000
--- a/.pre-commit-config.yaml
+++ /dev/null
@@ -1,29 +0,0 @@
-default_language_version:
- python: python3
-
-repos:
- - repo: https://github.com/psf/black
- rev: 20.8b1
- hooks:
- - id: black
- - repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook
- rev: v5.0.0
- hooks:
- - id: commitlint
- additional_dependencies: ['@commitlint/config-conventional']
- stages: [commit-msg]
- - repo: https://github.com/pycqa/flake8
- rev: 3.9.2
- hooks:
- - id: flake8
- - repo: https://github.com/pycqa/isort
- rev: 5.9.3
- hooks:
- - id: isort
- - repo: https://github.com/pre-commit/mirrors-mypy
- rev: v0.910
- hooks:
- - id: mypy
- additional_dependencies:
- - types-PyYAML==5.4.10
- - types-requests==2.25.9
diff --git a/.readthedocs.yml b/.readthedocs.yml
deleted file mode 100644
index 1439594..0000000
--- a/.readthedocs.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-version: 2
-
-sphinx:
- configuration: docs/conf.py
-
-formats:
- - pdf
- - epub
-
-python:
- version: 3.8
- install:
- - requirements: requirements-docs.txt
diff --git a/.renovaterc.json b/.renovaterc.json
deleted file mode 100644
index df0650f..0000000
--- a/.renovaterc.json
+++ /dev/null
@@ -1,33 +0,0 @@
-{
- "extends": [
- "config:base"
- ],
- "pip_requirements": {
- "fileMatch": ["^requirements(-[\\w]*)?\\.txt$"]
- },
- "regexManagers": [
- {
- "fileMatch": ["^tests/functional/fixtures/.env$"],
- "matchStrings": ["GITLAB_TAG=(?<currentValue>.*?)\n"],
- "depNameTemplate": "gitlab/gitlab-ce",
- "datasourceTemplate": "docker",
- "versioningTemplate": "loose"
- },
- {
- "fileMatch": ["^.pre-commit-config.yaml$"],
- "matchStrings": ["- (?<depName>.*?)==(?<currentValue>.*?)\n"],
- "datasourceTemplate": "pypi",
- "versioningTemplate": "pep440"
- }
- ],
- "packageRules": [
- {
- "packagePatterns": ["^gitlab\/gitlab-.+$"],
- "automerge": true
- },
- {
- "matchPackagePrefixes": ["types-"],
- "groupName": "typing dependencies"
- }
- ]
-}
diff --git a/AUTHORS b/AUTHORS
deleted file mode 100644
index 8af0c13..0000000
--- a/AUTHORS
+++ /dev/null
@@ -1,17 +0,0 @@
-Authors / Maintainers
----------------------
-
-Original creator, no longer active
-==================================
-Gauvain Pocentek <gauvainpocentek@gmail.com>
-
-Current
-=======
-Nejc Habjan <hab.nejc@gmail.com>
-Max Wittig <max.wittig@siemens.com>
-Roger Meier <r.meier@siemens.com>
-
-Contributors
-------------
-
-See ``git log`` for a full list of contributors.
diff --git a/CHANGELOG.md b/CHANGELOG.md
deleted file mode 100644
index a6fb8cc..0000000
--- a/CHANGELOG.md
+++ /dev/null
@@ -1,1058 +0,0 @@
-# Changelog
-
-<!--next-version-placeholder-->
-
-## v2.10.1 (2021-08-28)
-### Fix
-* **mixins:** Improve deprecation warning ([`57e0187`](https://github.com/python-gitlab/python-gitlab/commit/57e018772492a8522b37d438d722c643594cf580))
-* **deps:** Upgrade requests to 2.25.0 (see CVE-2021-33503) ([`ce995b2`](https://github.com/python-gitlab/python-gitlab/commit/ce995b256423a0c5619e2a6c0d88e917aad315ba))
-
-### Documentation
-* **mergequests:** Gl.mergequests.list documentation was missleading ([`5b5a7bc`](https://github.com/python-gitlab/python-gitlab/commit/5b5a7bcc70a4ddd621cbd59e134e7004ad2d9ab9))
-
-## v2.10.0 (2021-07-28)
-### Feature
-* **api:** Add merge_ref for merge requests ([`1e24ab2`](https://github.com/python-gitlab/python-gitlab/commit/1e24ab247cc783ae240e94f6cb379fef1e743a52))
-* **api:** Add `name_regex_keep` attribute in `delete_in_bulk()` ([`e49ff3f`](https://github.com/python-gitlab/python-gitlab/commit/e49ff3f868cbab7ff81115f458840b5f6d27d96c))
-
-### Fix
-* **api:** Do not require Release name for creation ([`98cd03b`](https://github.com/python-gitlab/python-gitlab/commit/98cd03b7a3085356b5f0f4fcdb7dc729b682f481))
-
-### Documentation
-* **readme:** Move contributing docs to CONTRIBUTING.rst ([`edf49a3`](https://github.com/python-gitlab/python-gitlab/commit/edf49a3d855b1ce4e2bd8a7038b7444ff0ab5fdc))
-* Add example for mr.merge_ref ([`b30b8ac`](https://github.com/python-gitlab/python-gitlab/commit/b30b8ac27d98ed0a45a13775645d77b76e828f95))
-* **project:** Add example on getting a single project using name with namespace ([`ef16a97`](https://github.com/python-gitlab/python-gitlab/commit/ef16a979031a77155907f4160e4f5e159d839737))
-
-## v2.9.0 (2021-06-28)
-### Feature
-* **release:** Allow to update release ([`b4c4787`](https://github.com/python-gitlab/python-gitlab/commit/b4c4787af54d9db6c1f9e61154be5db9d46de3dd))
-* **api:** Add group hooks ([`4a7e9b8`](https://github.com/python-gitlab/python-gitlab/commit/4a7e9b86aa348b72925bce3af1e5d988b8ce3439))
-* **api:** Remove responsibility for API inconsistencies for MR reviewers ([`3d985ee`](https://github.com/python-gitlab/python-gitlab/commit/3d985ee8cdd5d27585678f8fbb3eb549818a78eb))
-* **api:** Add MR pipeline manager and deprecate pipelines() method ([`954357c`](https://github.com/python-gitlab/python-gitlab/commit/954357c49963ef51945c81c41fd4345002f9fb98))
-* **api:** Add support for creating/editing reviewers in project merge requests ([`676d1f6`](https://github.com/python-gitlab/python-gitlab/commit/676d1f6565617a28ee84eae20e945f23aaf3d86f))
-
-### Documentation
-* **tags:** Remove deprecated functions ([`1b1a827`](https://github.com/python-gitlab/python-gitlab/commit/1b1a827dd40b489fdacdf0a15b0e17a1a117df40))
-* **release:** Add update example ([`6254a5f`](https://github.com/python-gitlab/python-gitlab/commit/6254a5ff6f43bd7d0a26dead304465adf1bd0886))
-* Make Gitlab class usable for intersphinx ([`8753add`](https://github.com/python-gitlab/python-gitlab/commit/8753add72061ea01c508a42d16a27388b1d92677))
-
-## v2.8.0 (2021-06-10)
-### Feature
-* Add keys endpoint ([`a81525a`](https://github.com/python-gitlab/python-gitlab/commit/a81525a2377aaed797af0706b00be7f5d8616d22))
-* **objects:** Add support for Group wikis ([#1484](https://github.com/python-gitlab/python-gitlab/issues/1484)) ([`74f5e62`](https://github.com/python-gitlab/python-gitlab/commit/74f5e62ef5bfffc7ba21494d05dbead60b59ecf0))
-* **objects:** Add support for generic packages API ([`79d88bd`](https://github.com/python-gitlab/python-gitlab/commit/79d88bde9e5e6c33029e4a9f26c97404e6a7a874))
-* **api:** Add deployment mergerequests interface ([`fbbc0d4`](https://github.com/python-gitlab/python-gitlab/commit/fbbc0d400015d7366952a66e4401215adff709f0))
-* **objects:** Support all issues statistics endpoints ([`f731707`](https://github.com/python-gitlab/python-gitlab/commit/f731707f076264ebea65afc814e4aca798970953))
-* **objects:** Add support for descendant groups API ([`1b70580`](https://github.com/python-gitlab/python-gitlab/commit/1b70580020825adf2d1f8c37803bc4655a97be41))
-* **objects:** Add pipeline test report support ([`ee9f96e`](https://github.com/python-gitlab/python-gitlab/commit/ee9f96e61ab5da0ecf469c21cccaafc89130a896))
-* **objects:** Add support for billable members ([`fb0b083`](https://github.com/python-gitlab/python-gitlab/commit/fb0b083a0e536a6abab25c9ad377770cc4290fe9))
-* Add feature to get inherited member for project/group ([`e444b39`](https://github.com/python-gitlab/python-gitlab/commit/e444b39f9423b4a4c85cdb199afbad987df026f1))
-* Add code owner approval as attribute ([`fdc46ba`](https://github.com/python-gitlab/python-gitlab/commit/fdc46baca447e042d3b0a4542970f9758c62e7b7))
-* Indicate that we are a typed package ([`e4421ca`](https://github.com/python-gitlab/python-gitlab/commit/e4421caafeeb0236df19fe7b9233300727e1933b))
-* Add support for lists of integers to ListAttribute ([`115938b`](https://github.com/python-gitlab/python-gitlab/commit/115938b3e5adf9a2fb5ecbfb34d9c92bf788035e))
-
-### Fix
-* Catch invalid type used to initialize RESTObject ([`c7bcc25`](https://github.com/python-gitlab/python-gitlab/commit/c7bcc25a361f9df440f9c972672e5eec3b057625))
-* Functional project service test ([#1500](https://github.com/python-gitlab/python-gitlab/issues/1500)) ([`093db9d`](https://github.com/python-gitlab/python-gitlab/commit/093db9d129e0a113995501755ab57a04e461c745))
-* Ensure kwargs are passed appropriately for ObjectDeleteMixin ([`4e690c2`](https://github.com/python-gitlab/python-gitlab/commit/4e690c256fc091ddf1649e48dbbf0b40cc5e6b95))
-* **cli:** Add missing list filter for jobs ([`b3d1c26`](https://github.com/python-gitlab/python-gitlab/commit/b3d1c267cbe6885ee41b3c688d82890bb2e27316))
-* Change mr.merge() to use 'post_data' ([`cb6a3c6`](https://github.com/python-gitlab/python-gitlab/commit/cb6a3c672b9b162f7320c532410713576fbd1cdc))
-* **cli:** Fix parsing CLI objects to classnames ([`4252070`](https://github.com/python-gitlab/python-gitlab/commit/42520705a97289ac895a6b110d34d6c115e45500))
-* **objects:** Return server data in cancel/retry methods ([`9fed061`](https://github.com/python-gitlab/python-gitlab/commit/9fed06116bfe5df79e6ac5be86ae61017f9a2f57))
-* **objects:** Add missing group attributes ([`d20ff4f`](https://github.com/python-gitlab/python-gitlab/commit/d20ff4ff7427519c8abccf53e3213e8929905441))
-* **objects:** Allow lists for filters for in all objects ([`603a351`](https://github.com/python-gitlab/python-gitlab/commit/603a351c71196a7f516367fbf90519f9452f3c55))
-* Iids not working as a list in projects.issues.list() ([`45f806c`](https://github.com/python-gitlab/python-gitlab/commit/45f806c7a7354592befe58a76b7e33a6d5d0fe6e))
-* Add a check to ensure the MRO is correct ([`565d548`](https://github.com/python-gitlab/python-gitlab/commit/565d5488b779de19a720d7a904c6fc14c394a4b9))
-
-### Documentation
-* Fix typo in http_delete docstring ([`5226f09`](https://github.com/python-gitlab/python-gitlab/commit/5226f095c39985d04c34e7703d60814e74be96f8))
-* **api:** Add behavior in local attributes when updating objects ([`38f65e8`](https://github.com/python-gitlab/python-gitlab/commit/38f65e8e9994f58bdc74fe2e0e9b971fc3edf723))
-* Fail on warnings during sphinx build ([`cbd4d52`](https://github.com/python-gitlab/python-gitlab/commit/cbd4d52b11150594ec29b1ce52348c1086a778c8))
-
-## v2.7.1 (2021-04-26)
-
-* fix(files): do not url-encode file paths twice
-
-## v2.7.0 (2021-04-25)
-
-### Bug Fixes
-
-* update user's bool data and avatar (3ba27ffb)
-* argument type was not a tuple as expected (062f8f6a)
-* correct some type-hints in gitlab/mixins.py (8bd31240)
-* only append kwargs as query parameters (b9ecc9a8)
-* only add query_parameters to GitlabList once (1386)
-* checking if RESTManager._from_parent_attrs is set (8224b406)
-* handling config value in _get_values_from_helper (9dfb4cd9)
-* let the homedir be expanded in path of helper (fc7387a0)
-* make secret helper more user friendly (fc2798fc)
-* linting issues and test (b04dd2c0)
-* handle tags like debian/2%2.6-21 as identifiers (b4dac5ce)
-* remove duplicate class definitions in v4/objects/users.py (7c4e6259)
-* wrong variable name (15ec41ca)
-* tox pep8 target, so that it can run (f518e87b)
-* undefined name errors (48ec9e0f)
-* extend wait timeout for test_delete_user() (19fde8ed)
-* test_update_group() dependency on ordering (e78a8d63)
-* honor parameter value passed (c2f8f0e7)
-* **objects:** add single get endpoint for instance audit events (c3f0a6f1)
-* **types:** prevent __dir__ from producing duplicates (5bf7525d)
-
-### Features
-
-* add ProjectPackageFile (#1372)
-* add option to add a helper to lookup token (8ecf5592)
-* add project audit endpoint (6660dbef)
-* add personal access token API (2bb16fac)
-* add import from bitbucket server (ff3013a2)
-* **api,cli:** make user agent configurable (4bb201b9)
-* **issues:** add missing get verb to IssueManager (f78ebe06)
-* **objects:**
- * add support for resource state events API (d4799c40)
- * add support for group audit events API (2a0fbdf9)
- * add Release Links API support (28d75181)
-* **projects:** add project access token api (1becef02)
-* **users:** add follow/unfollow API (e456869d)
-
-### Documentation
-* correct ProjectFile.decode() documentation (b180bafd)
-* update doc for token helper (3ac6fa12)
-* better real life token lookup example (9ef83118)
-
-## v2.6.0 (2021-01-29)
-
-### Features
-
-* support multipart uploads (2fa3004d)
-* add MINIMAL_ACCESS constant (49eb3ca7)
-* unit tests added (f37ebf5f)
-* added support for pipeline bridges (05cbdc22)
-* adds support for project merge request approval rules (#1199) (c6fbf399)
-* **api:**
- * added wip filter param for merge requests (d6078f80)
- * added wip filter param for merge requests (aa6e80d5)
- * add support for user identity provider deletion (e78e1215)
-* **tests:** test label getter (a41af902)
-
-### Bug Fixes
-
-* docs changed using the consts (650b65c3)
-* typo (9baa9053)
-* **api:**
- * use RetrieveMixin for ProjectLabelManager (1a143952)
- * add missing runner access_level param (92669f2e)
-* **base:** really refresh object (e1e0d8cb), closes (#1155)
-* **cli:**
- * write binary data to stdout buffer (0733ec6c)
- * add missing args for project lists (c73e2374)
-
-## v2.5.0 (2020-09-01)
-
-### Features
-
-* add support to resource milestone events (88f8cc78), closes #1154
-* add share/unshare group with group (7c6e541d)
-* add support for instance variables (4492fc42)
-* add support for Packages API (71495d12)
-* add endpoint for latest ref artifacts (b7a07fca)
-
-### Bug Fixes
-
-* wrong reconfirmation parameter when updating user's email (b5c267e1)
-* tests fail when using REUSE_CONTAINER option ([0078f899](https://github.com/python-gitlab/python-gitlab/commit/0078f8993c38df4f02da9aaa3f7616d1c8b97095), closes #1146
-* implement Gitlab's behavior change for owned=True (99777991)
-
-## v2.4.0 (2020-07-09)
-
-### Bug Fixes
-
-* do not check if kwargs is none (a349b90e)
-* make query kwargs consistent between call in init and next (72ffa016)
-* pass kwargs to subsequent queries in gitlab list (1d011ac7)
-* **merge:** parse arguments as query_data (878098b7)
-
-### Features
-
-* add NO_ACCESS const (dab4d0a1)
-* add masked parameter for variables command (b6339bf8)
-
-## v2.3.1 (2020-06-09)
-
-* revert keyset pagination by default
-
-## v2.3.0 (2020-06-08)
-
-### Features
-
-* add group runners api (49439916)
-* add play command to project pipeline schedules (07b99881)
-* allow an environment variable to specify config location (401e702a)
-* **api:** added support in the GroupManager to upload Group avatars (28eb7eab)
-* **services:** add project service list API (fc522218)
-* **types:** add __dir__ to RESTObject to expose attributes (cad134c0)
-
-### Bug Fixes
-
-* use keyset pagination by default for /projects > 50000 (f86ef3bb)
-* **config:** fix duplicate code (ee2df6f1), closes (#1094)
-* **project:** add missing project parameters (ad8c67d6)
-
-## v2.2.0 (2020-04-07)
-
-### Bug Fixes
-
-* add missing import_project param (9b16614b)
-* **types:** do not split single value string in ListAttribute (a26e5858)
-
-### Features
-
-* add commit GPG signature API (da7a8097)
-* add create from template args to ProjectManager (f493b73e)
-* add remote mirrors API (#1056) (4cfaa2fd)
-* add Gitlab Deploy Token API (01de524c)
-* add Group Import/Export API (#1037) (6cb9d923)
-
-## v2.1.2 (2020-03-09)
-
-### Bug Fixes
-
-* Fix regression, when using keyset pagination with merge requests. Related to https://github.com/python-gitlab/python-gitlab/issues/1044
-
-## v2.1.1 (2020-03-09)
-
-### Bug Fixes
-
-**users**: update user attributes
-
-This change was made to migate an issue in Gitlab (again). Fix available in: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/26792
-
-## v2.1.0 (2020-03-08)
-
-### Bug Fixes
-
-* do not require empty data dict for create() (99d959f7)
-* remove trailing slashes from base URL (#913) (2e396e4a)
-* return response with commit data (b77b945c)
-* remove null values from features POST data, because it fails with HTTP 500 (1ec1816d)
-* **docs:**
- * fix typo in user memberships example (33889bcb)
- * update to new set approvers call for # of approvers (8e0c5262)
- * update docs and tests for set_approvers (2cf12c79)
-* **objects:**
- * add default name data and use http post (70c0cfb6)
- * update set_approvers function call (65ecadcf)
- * update to new gitlab api for path, and args (e512cddd)
-
-### Features
-
-* add support for user memberships API (#1009) (c313c2b0)
-* add support for commit revert API (#991) (5298964e)
-* add capability to control GitLab features per project or group (7f192b4f)
-* use keyset pagination by default for `all=True` (99b4484d)
-* add support for GitLab OAuth Applications API (4e12356d)
-
-## v2.0.1 (2020-02-05)
-
-### Changes
-
-* **users:** update user attributes
-
-This change was made to migate an issue in Gitlab. See: https://gitlab.com/gitlab-org/gitlab/issues/202070
-
-## v2.0.0 (2020-01-26)
-
-### This releases drops support for python < 3.6
-
-### Bug Fixes
-
-* **projects:** adjust snippets to match the API (e104e213)
-
-### Features
-
-* add global order_by option to ease pagination (d1879253)
-* support keyset pagination globally (0b71ba4d)
-* add appearance API (4c4ac5ca)
-* add autocompletion support (973cb8b9)
-
-## v1.15.0 (2019-12-16)
-
-### Bug Fixes
-
-* ignore all parameter, when as_list=True 137d72b3, closes #962
-
-### Features
-
-* allow cfg timeout to be overrided via kwargs e9a8289a
-* add support for /import/github aa4d41b7
-* nicer stacktrace 697cda24
-* retry transient HTTP errors 59fe2714, closes #970
-* access project's issues statistics 482e57ba, closes #966
-* adding project stats db0b00a9, closes #967
-* add variable_type/protected to projects ci variables 4724c50e
-* add variable_type to groups ci variables 0986c931
-
-## v1.14.0 (2019-12-07)
-
-### Bug Fixes
-
-* added missing attributes for project approvals 460ed63c
-* **labels:** ensure label.save() works 727f5361
-* **project-fork:**
- * copy create fix from ProjectPipelineManager 516307f1
- * correct path computation for project-fork list 44a7c278
-
-### Features
-
-* add audit endpoint 2534020b
-* add project and group clusters ebd053e7
-* add support for include_subgroups filter adbcd83f
-
-
-## v1.13.0 (2019-11-02)
-
-### Features
-
-* add users activate, deactivate functionality (32ad6692)
-* send python-gitlab version as user-agent (c22d49d0)
-* add deployment creation (ca256a07), closes [#917]
-* **auth:** remove deprecated session auth (b751cdf4)
-* **doc:** remove refs to api v3 in docs (6beeaa99)
-* **test:** unused unittest2, type -> isinstance (33b18012)
-
-### Bug Fixes
-
-* **projects:** support `approval_rules` endpoint for projects (2cef2bb4)
-
-## v1.12.1 (2019-10-07)
-
-### Bug Fixes
-
-fix: fix not working without auth provided
-
-## v1.12.0 (2019-10-06)
-
-### Features
-
-* add support for job token
-* **project:**
- * implement update_submodule
- * add file blame api
-* **user:** add status api
-
-### Bug Fixes
-
-* **cli:** fix cli command user-project list
-* **labels:** don't mangle label name on update
-* **todo:** mark_all_as_done doesn't return anything
-
-## v1.11.0 (2019-08-31)
-
-### Features
-
-* add methods to retrieve an individual project environment
-* group labels with subscriptable mixin
-
-### Bug Fixes
-
-* projects: avatar uploading for projects
-* remove empty list default arguments
-* remove empty dict default arguments
-* add project and group label update without id to fix cli
-
-## v1.10.0 (2019-07-22)
-
-### Features
-
-* add mr rebase method bc4280c2
-* get artifact by ref and job cda11745
-* add support for board update 908d79fa, closes #801
-* add support for issue.related_merge_requests 90a36315, closes #794
-
-### Bug Fixes
-
-* improve pickle support b4b5decb
-* **cli:**
- * allow --recursive parameter in repository tree 7969a78c, closes #718, #731
- * don't fail when the short print attr value is None 8d1552a0, closes #717, #727
- * fix update value for key not working b7662039
-
-
-## v1.9.0 (2019-06-19)
-
-### Features
-
-* implement artifacts deletion
-* add endpoint to get the variables of a pipeline
-* delete ProjectPipeline
-* implement __eq__ and __hash__ methods
-* Allow runpy invocation of CLI tool (python -m gitlab)
-* add project releases api
-* merged new release & registry apis
-
-### Bug Fixes
-
-* convert # to %23 in URLs
-* pep8 errors
-* use python2 compatible syntax for super
-* Make MemberManager.all() return a list of objects
-* %d replaced by %s
-* Re-enable command specific help messages
-* dont ask for id attr if this is \*Manager originating custom action
-* fix -/_ replacament for \*Manager custom actions
-* fix repository_id marshaling in cli
-* register cli action for delete_in_bulk
-
-## v1.8.0 (2019-02-22)
-
-* docs(setup): use proper readme on PyPI
-* docs(readme): provide commit message guidelines
-* fix(api): make reset_time_estimate() work again
-* fix: handle empty 'Retry-After' header from GitLab
-* fix: remove decode() on error_message string
-* chore: release tags to PyPI automatically
-* fix(api): avoid parameter conflicts with python and gitlab
-* fix(api): Don't try to parse raw downloads
-* feat: Added approve & unapprove method for Mergerequests
-* fix all kwarg behaviour
-
-## v1.7.0 (2018-12-09)
-
-* **docs:** Fix the owned/starred usage documentation
-* **docs:** Add a warning about http to https redirects
-* Fix the https redirection test
-* **docs:** Add a note about GroupProject limited API
-* Add missing comma in ProjectIssueManager _create_attrs
-* More flexible docker image
-* Add project protected tags management
-* **cli:** Print help and usage without config file
-* Rename MASTER_ACCESS to MAINTAINER_ACCESS
-* **docs:** Add docs build information
-* Use docker image with current sources
-* **docs:** Add PyYAML requirement notice
-* Add Gitter badge to README
-* **docs:** Add an example of pipeline schedule vars listing
-* **cli:** Exit on config parse error, instead of crashing
-* Add support for resource label events
-* **docs:** Fix the milestone filetring doc (iid -> iids)
-* **docs:** Fix typo in custom attributes example
-* Improve error message handling in exceptions
-* Add support for members all() method
-* Add access control options to protected branch creation
-
-## v1.6.0 (2018-08-25)
-
-* **docs:** Don't use hardcoded values for ids
-* **docs:** Improve the snippets examples
-* **cli:** Output: handle bytes in API responses
-* **cli:** Fix the case where we have nothing to print
-* Project import: fix the override_params parameter
-* Support group and global MR listing
-* Implement MR.pipelines()
-* MR: add the squash attribute for create/update
-* Added support for listing forks of a project
-* **docs:** Add/update notes about read-only objects
-* Raise an exception on https redirects for PUT/POST
-* **docs:** Add a FAQ
-* **cli:** Fix the project-export download
-
-## v1.5.1 (2018-06-23)
-
-* Fix the ProjectPipelineJob base class (regression)
-
-## v1.5.0 (2018-06-22)
-
-* Drop API v3 support
-* Drop GetFromListMixin
-* Update the sphinx extension for v4 objects
-* Add support for user avatar upload
-* Add support for project import/export
-* Add support for the search API
-* Add a global per_page config option
-* Add support for the discussions API
-* Add support for merged branches deletion
-* Add support for Project badges
-* Implement user_agent_detail for snippets
-* Implement commit.refs()
-* Add commit.merge_requests() support
-* Deployment: add list filters
-* Deploy key: add missing attributes
-* Add support for environment stop()
-* Add feature flags deletion support
-* Update some group attributes
-* Issues: add missing attributes and methods
-* Fix the participants() decorator
-* Add support for group boards
-* Implement the markdown rendering API
-* Update MR attributes
-* Add pipeline listing filters
-* Add missing project attributes
-* Implement runner jobs listing
-* Runners can be created (registered)
-* Implement runner token validation
-* Update the settings attributes
-* Add support for the gitlab CI lint API
-* Add support for group badges
-* Fix the IssueManager path to avoid redirections
-* time_stats(): use an existing attribute if available
-* Make ProjectCommitStatus.create work with CLI
-* Tests: default to python 3
-* ProjectPipelineJob was defined twice
-* Silence logs/warnings in unittests
-* Add support for MR approval configuration (EE)
-* Change post_data default value to None
-* Add geo nodes API support (EE)
-* Add support for issue links (EE)
-* Add support for LDAP groups (EE)
-* Add support for board creation/deletion (EE)
-* Add support for Project.pull_mirror (EE)
-* Add project push rules configuration (EE)
-* Add support for the EE license API
-* Add support for the LDAP groups API (EE)
-* Add support for epics API (EE)
-* Fix the non-verbose output of ProjectCommitComment
-
-## v1.4.0 (2018-05-19)
-
-* Require requests>=2.4.2
-* ProjectKeys can be updated
-* Add support for unsharing projects (v3/v4)
-* **cli:** fix listing for json and yaml output
-* Fix typos in documentation
-* Introduce RefreshMixin
-* **docs:** Fix the time tracking examples
-* **docs:** Commits: add an example of binary file creation
-* **cli:** Allow to read args from files
-* Add support for recursive tree listing
-* **cli:** Restore the --help option behavior
-* Add basic unit tests for v4 CLI
-* **cli:** Fix listing of strings
-* Support downloading a single artifact file
-* Update docs copyright years
-* Implement attribute types to handle special cases
-* **docs:** fix GitLab reference for notes
-* Expose additional properties for Gitlab objects
-* Fix the impersonation token deletion example
-* feat: obey the rate limit
-* Fix URL encoding on branch methods
-* **docs:** add a code example for listing commits of a MR
-* **docs:** update service.available() example for API v4
-* **tests:** fix functional tests for python3
-* api-usage: bit more detail for listing with `all`
-* More efficient .get() for group members
-* Add docs for the `files` arg in http_*
-* Deprecate GetFromListMixin
-
-## v1.3.0 (2018-02-18)
-
-* Add support for pipeline schedules and schedule variables
-* Clarify information about supported python version
-* Add manager for jobs within a pipeline
-* Fix wrong tag example
-* Update the groups documentation
-* Add support for MR participants API
-* Add support for getting list of user projects
-* Add Gitlab and User events support
-* Make trigger_pipeline return the pipeline
-* Config: support api_version in the global section
-* Gitlab can be used as context manager
-* Default to API v4
-* Add a simplified example for streamed artifacts
-* Add documentation about labels update
-
-## v1.2.0 (2018-01-01)
-
-* Add mattermost service support
-* Add users custom attributes support
-* **doc:** Fix project.triggers.create example with v4 API
-* Oauth token support
-* Remove deprecated objects/methods
-* Rework authentication args handling
-* Add support for oauth and anonymous auth in config/CLI
-* Add support for impersonation tokens API
-* Add support for user activities
-* Update user docs with gitlab URLs
-* **docs:** Bad arguments in projects file documentation
-* Add support for user_agent_detail (issues)
-* Add a SetMixin
-* Add support for project housekeeping
-* Expected HTTP response for subscribe is 201
-* Update pagination docs for ProjectCommit
-* Add doc to get issue from iid
-* Make todo() raise GitlabTodoError on error
-* Add support for award emojis
-* Update project services docs for v4
-* Avoid sending empty update data to issue.save
-* **docstrings:** Explicitly document pagination arguments
-* **docs:** Add a note about password auth being removed from GitLab
-* Submanagers: allow having undefined parameters
-* ProjectFile.create(): don't modify the input data
-* Update testing tools for /session removal
-* Update groups tests
-* Allow per_page to be used with generators
-* Add groups listing attributes
-* Add support for subgroups listing
-* Add supported python versions in setup.py
-* Add support for pagesdomains
-* Add support for features flags
-* Add support for project and group custom variables
-* Add support for user/group/project filter by custom attribute
-* Respect content of REQUESTS_CA_BUNDLE and \*_proxy envvars
-
-## v1.1.0 (2017-11-03)
-
-* Fix trigger variables in v4 API
-* Make the delete() method handle / in ids
-* **docs:** update the file upload samples
-* Tags release description: support / in tag names
-* **docs:** improve the labels usage documentation
-* Add support for listing project users
-* ProjectFileManager.create: handle / in file paths
-* Change ProjectUser and GroupProject base class
-* **docs:** document `get_create_attrs` in the API tutorial
-* Document the Gitlab session parameter
-* ProjectFileManager: custom update() method
-* Project: add support for printing_merge_request_link_enabled attr
-* Update the ssl_verify docstring
-* Add support for group milestones
-* Add support for GPG keys
-* Add support for wiki pages
-* Update the repository_blob documentation
-* Fix the CLI for objects without ID (API v4)
-* Add a contributed Dockerfile
-* Pagination generators: expose more information
-* Module's base objects serialization
-* **doc:** Add sample code for client-side certificates
-
-## v1.0.2 (2017-09-29)
-
-* **docs:** remove example usage of submanagers
-* Properly handle the labels attribute in ProjectMergeRequest
-* ProjectFile: handle / in path for delete() and save()
-
-## v1.0.1 (2017-09-21)
-
-* Tags can be retrieved by ID
-* Add the server response in GitlabError exceptions
-* Add support for project file upload
-* Minor typo fix in "Switching to v4" documentation
-* Fix password authentication for v4
-* Fix the labels attrs on MR and issues
-* Exceptions: use a proper error message
-* Fix http_get method in get artifacts and job trace
-* CommitStatus: `sha` is parent attribute
-* Fix a couple listing calls to allow proper pagination
-* Add missing doc file
-
-## v1.0.0 (2017-09-08)
-
-* Support for API v4. See
- http://python-gitlab.readthedocs.io/en/master/switching-to-v4.html
-* Support SSL verification via internal CA bundle
-* Docs: Add link to gitlab docs on obtaining a token
-* Added dependency injection support for Session
-* Fixed repository_compare examples
-* Fix changelog and release notes inclusion in sdist
-* Missing expires_at in GroupMembers update
-* Add lower-level methods for Gitlab()
-
-## v0.21.2 (2017-06-11)
-
-* Install doc: use sudo for system commands
-* **v4:** Make MR work properly
-* Remove extra_attrs argument from `_raw_list`
-* **v4:** Make project issues work properly
-* Fix urlencode() usage (python 2/3) (#268)
-* Fixed spelling mistake (#269)
-* Add new event types to ProjectHook
-
-## v0.21.1 (2017-05-25)
-
-* Fix the manager name for jobs in the Project class
-* Fix the docs
-
-## v0.21 (2017-05-24)
-
-* Add time_stats to ProjectMergeRequest
-* Update User options for creation and update (#246)
-* Add milestone.merge_requests() API
-* Fix docs typo (s/correspnding/corresponding/)
-* Support milestone start date (#251)
-* Add support for priority attribute in labels (#256)
-* Add support for nested groups (#257)
-* Make GroupProjectManager a subclass of ProjectManager (#255)
-* Available services: return a list instead of JSON (#258)
-* MR: add support for time tracking features (#248)
-* Fixed repository_tree and repository_blob path encoding (#265)
-* Add 'search' attribute to projects.list()
-* Initial gitlab API v4 support
-* Reorganise the code to handle v3 and v4 objects
-* Allow 202 as delete return code
-* Deprecate parameter related methods in gitlab.Gitlab
-
-## v0.20 (2017-03-25)
-
-* Add time tracking support (#222)
-* Improve changelog (#229, #230)
-* Make sure that manager objects are never overwritten (#209)
-* Include chanlog and release notes in docs
-* Add DeployKey{,Manager} classes (#212)
-* Add support for merge request notes deletion (#227)
-* Properly handle extra args when listing with all=True (#233)
-* Implement pipeline creation API (#237)
-* Fix spent_time methods
-* Add 'delete source branch' option when creating MR (#241)
-* Provide API wrapper for cherry picking commits (#236)
-* Stop listing if recursion limit is hit (#234)
-
-## v0.19 (2017-02-21)
-
-* Update project.archive() docs
-* Support the scope attribute in runners.list()
-* Add support for project runners
-* Add support for commit creation
-* Fix install doc
-* Add builds-email and pipelines-email services
-* Deploy keys: rework enable/disable
-* Document the dynamic aspect of objects
-* Add pipeline_events to ProjectHook attrs
-* Add due_date attribute to ProjectIssue
-* Handle settings.domain_whitelist, partly
-* {Project,Group}Member: support expires_at attribute
-
-## v0.18 (2016-12-27)
-
-* Fix JIRA service editing for GitLab 8.14+
-* Add jira_issue_transition_id to the JIRA service optional fields
-* Added support for Snippets (new API in Gitlab 8.15)
-* **docs:** update pagination section
-* **docs:** artifacts example: open file in wb mode
-* **CLI:** ignore empty arguments
-* **CLI:** Fix wrong use of arguments
-* **docs:** Add doc for snippets
-* Fix duplicated data in API docs
-* Update known attributes for projects
-* sudo: always use strings
-
-## v0.17 (2016-12-02)
-
-* README: add badges for pypi and RTD
-* Fix ProjectBuild.play (raised error on success)
-* Pass kwargs to the object factory
-* Add .tox to ignore to respect default tox settings
-* Convert response list to single data source for iid requests
-* Add support for boards API
-* Add support for Gitlab.version()
-* Add support for broadcast messages API
-* Add support for the notification settings API
-* Don't overwrite attributes returned by the server
-* Fix bug when retrieving changes for merge request
-* Feature: enable / disable the deploy key in a project
-* Docs: add a note for python 3.5 for file content update
-* ProjectHook: support the token attribute
-* Rework the API documentation
-* Fix docstring for http_{username,password}
-* Build managers on demand on GitlabObject's
-* API docs: add managers doc in GitlabObject's
-* Sphinx ext: factorize the build methods
-* Implement `__repr__` for gitlab objects
-* Add a 'report a bug' link on doc
-* Remove deprecated methods
-* Implement merge requests diff support
-* Make the manager objects creation more dynamic
-* Add support for templates API
-* Add attr 'created_at' to ProjectIssueNote
-* Add attr 'updated_at' to ProjectIssue
-* CLI: add support for project all --all
-* Add support for triggering a new build
-* Rework requests arguments (support latest requests release)
-* Fix `should_remove_source_branch`
-
-## v0.16 (2016-10-16)
-
-* Add the ability to fork to a specific namespace
-* JIRA service - add api_url to optional attributes
-* Fix bug: Missing coma concatenates array values
-* docs: branch protection notes
-* Create a project in a group
-* Add only_allow_merge_if_build_succeeds option to project objects
-* Add support for --all in CLI
-* Fix examples for file modification
-* Use the plural merge_requests URL everywhere
-* Rework travis and tox setup
-* Workaround gitlab setup failure in tests
-* Add ProjectBuild.erase()
-* Implement ProjectBuild.play()
-
-## v0.15.1 (2016-10-16)
-
-* docs: improve the pagination section
-* Fix and test pagination
-* 'path' is an existing gitlab attr, don't use it as method argument
-
-## v0.15 (2016-08-28)
-
-* Add a basic HTTP debug method
-* Run more tests in travis
-* Fix fork creation documentation
-* Add more API examples in docs
-* Update the ApplicationSettings attributes
-* Implement the todo API
-* Add sidekiq metrics support
-* Move the constants at the gitlab root level
-* Remove methods marked as deprecated 7 months ago
-* Refactor the Gitlab class
-* Remove _get_list_or_object() and its tests
-* Fix canGet attribute (typo)
-* Remove unused ProjectTagReleaseManager class
-* Add support for project services API
-* Add support for project pipelines
-* Add support for access requests
-* Add support for project deployments
-
-## v0.14 (2016-08-07)
-
-* Remove 'next_url' from kwargs before passing it to the cls constructor.
-* List projects under group
-* Add support for subscribe and unsubscribe in issues
-* Project issue: doc and CLI for (un)subscribe
-* Added support for HTTP basic authentication
-* Add support for build artifacts and trace
-* --title is a required argument for ProjectMilestone
-* Commit status: add optional context url
-* Commit status: optional get attrs
-* Add support for commit comments
-* Issues: add optional listing parameters
-* Issues: add missing optional listing parameters
-* Project issue: proper update attributes
-* Add support for project-issue move
-* Update ProjectLabel attributes
-* Milestone: optional listing attrs
-* Add support for namespaces
-* Add support for label (un)subscribe
-* MR: add (un)subscribe support
-* Add `note_events` to project hooks attributes
-* Add code examples for a bunch of resources
-* Implement user emails support
-* Project: add VISIBILITY_* constants
-* Fix the Project.archive call
-* Implement archive/unarchive for a projet
-* Update ProjectSnippet attributes
-* Fix ProjectMember update
-* Implement sharing project with a group
-* Implement CLI for project archive/unarchive/share
-* Implement runners global API
-* Gitlab: add managers for build-related resources
-* Implement ProjectBuild.keep_artifacts
-* Allow to stream the downloads when appropriate
-* Groups can be updated
-* Replace Snippet.Content() with a new content() method
-* CLI: refactor _die()
-* Improve commit statuses and comments
-* Add support from listing group issues
-* Added a new project attribute to enable the container registry.
-* Add a contributing section in README
-* Add support for global deploy key listing
-* Add support for project environments
-* MR: get list of changes and commits
-* Fix the listing of some resources
-* MR: fix updates
-* Handle empty messages from server in exceptions
-* MR (un)subscribe: don't fail if state doesn't change
-* MR merge(): update the object
-
-## v0.13 (2016-05-16)
-
-* Add support for MergeRequest validation
-* MR: add support for cancel_merge_when_build_succeeds
-* MR: add support for closes_issues
-* Add "external" parameter for users
-* Add deletion support for issues and MR
-* Add missing group creation parameters
-* Add a Session instance for all HTTP requests
-* Enable updates on ProjectIssueNotes
-* Add support for Project raw_blob
-* Implement project compare
-* Implement project contributors
-* Drop the next_url attribute when listing
-* Remove unnecessary canUpdate property from ProjectIssuesNote
-* Add new optional attributes for projects
-* Enable deprecation warnings for gitlab only
-* Rework merge requests update
-* Rework the Gitlab.delete method
-* ProjectFile: file_path is required for deletion
-* Rename some methods to better match the API URLs
-* Deprecate the file_* methods in favor of the files manager
-* Implement star/unstar for projects
-* Implement list/get licenses
-* Manage optional parameters for list() and get()
-
-## v0.12.2 (2016-03-19)
-
-* Add new `ProjectHook` attributes
-* Add support for user block/unblock
-* Fix GitlabObject creation in _custom_list
-* Add support for more CLI subcommands
-* Add some unit tests for CLI
-* Add a coverage tox env
-* Define `GitlabObject.as_dict()` to dump object as a dict
-* Define `GitlabObject.__eq__()` and `__ne__()` equivalence methods
-* Define UserManager.search() to search for users
-* Define UserManager.get_by_username() to get a user by username
-* Implement "user search" CLI
-* Improve the doc for UserManager
-* CLI: implement user get-by-username
-* Re-implement _custom_list in the Gitlab class
-* Fix the 'invalid syntax' error on Python 3.2
-* Gitlab.update(): use the proper attributes if defined
-
-## v0.12.1 (2016-02-03)
-
-* Fix a broken upload to pypi
-
-## v0.12 (2016-02-03)
-
-* Improve documentation
-* Improve unit tests
-* Improve test scripts
-* Skip BaseManager attributes when encoding to JSON
-* Fix the json() method for python 3
-* Add Travis CI support
-* Add a decode method for ProjectFile
-* Make connection exceptions more explicit
-* Fix ProjectLabel get and delete
-* Implement ProjectMilestone.issues()
-* ProjectTag supports deletion
-* Implement setting release info on a tag
-* Implement project triggers support
-* Implement project variables support
-* Add support for application settings
-* Fix the 'password' requirement for User creation
-* Add sudo support
-* Fix project update
-* Fix Project.tree()
-* Add support for project builds
-
-## v0.11.1 (2016-01-17)
-
-* Fix discovery of parents object attrs for managers
-* Support setting commit status
-* Support deletion without getting the object first
-* Improve the documentation
-
-## v0.11 (2016-01-09)
-
-* functional_tests.sh: support python 2 and 3
-* Add a get method for GitlabObject
-* CLI: Add the -g short option for --gitlab
-* Provide a create method for GitlabObject's
-* Rename the `_created` attribute `_from_api`
-* More unit tests
-* CLI: fix error when arguments are missing (python 3)
-* Remove deprecated methods
-* Implement managers to get access to resources
-* Documentation improvements
-* Add fork project support
-* Deprecate the "old" Gitlab methods
-* Add support for groups search
-
-## v0.10 (2015-12-29)
-
-* Implement pagination for list() (#63)
-* Fix url when fetching a single MergeRequest
-* Add support to update MergeRequestNotes
-* API: Provide a Gitlab.from_config method
-* setup.py: require requests>=1 (#69)
-* Fix deletion of object not using 'id' as ID (#68)
-* Fix GET/POST for project files
-* Make 'confirm' an optional attribute for user creation
-* Python 3 compatibility fixes
-* Add support for group members update (#73)
-
-## v0.9.2 (2015-07-11)
-
-* CLI: fix the update and delete subcommands (#62)
-
-## v0.9.1 (2015-05-15)
-
-* Fix the setup.py script
-
-## v0.9 (2015-05-15)
-
-* Implement argparse library for parsing argument on CLI
-* Provide unit tests and (a few) functional tests
-* Provide PEP8 tests
-* Use tox to run the tests
-* CLI: provide a --config-file option
-* Turn the gitlab module into a proper package
-* Allow projects to be updated
-* Use more pythonic names for some methods
-* Deprecate some Gitlab object methods:
- * `raw*` methods should never have been exposed; replace them with `_raw_*` methods
- * setCredentials and setToken are replaced with set_credentials and set_token
-* Sphinx: don't hardcode the version in `conf.py`
-
-## v0.8 (2014-10-26)
-
-* Better python 2.6 and python 3 support
-* Timeout support in HTTP requests
-* Gitlab.get() raised GitlabListError instead of GitlabGetError
-* Support api-objects which don't have id in api response
-* Add ProjectLabel and ProjectFile classes
-* Moved url attributes to separate list
-* Added list for delete attributes
-
-## v0.7 (2014-08-21)
-
-* Fix license classifier in `setup.py`
-* Fix encoding error when printing to redirected output
-* Fix encoding error when updating with redirected output
-* Add support for UserKey listing and deletion
-* Add support for branches creation and deletion
-* Support state_event in ProjectMilestone (#30)
-* Support namespace/name for project id (#28)
-* Fix handling of boolean values (#22)
-
-## v0.6 (2014-01-16)
-
-* IDs can be unicode (#15)
-* ProjectMember: constructor should not create a User object
-* Add support for extra parameters when listing all projects (#12)
-* Projects listing: explicitly define arguments for pagination
-
-## v0.5 (2013-12-26)
-
-* Add SSH key for user
-* Fix comments
-* Add support for project events
-* Support creation of projects for users
-* Project: add methods for create/update/delete files
-* Support projects listing: search, all, owned
-* System hooks can't be updated
-* Project.archive(): download tarball of the project
-* Define new optional attributes for user creation
-* Provide constants for access permissions in groups
-
-## v0.4 (2013-09-26)
-
-* Fix strings encoding (Closes #6)
-* Allow to get a project commit (GitLab 6.1)
-* ProjectMergeRequest: fix Note() method
-* Gitlab 6.1 methods: diff, blob (commit), tree, blob (project)
-* Add support for Gitlab 6.1 group members
-
-## v0.3 (2013-08-27)
-
-* Use PRIVATE-TOKEN header for passing the auth token
-* provide an AUTHORS file
-* cli: support ssl_verify config option
-* Add ssl_verify option to Gitlab object. Defaults to True
-* Correct url for merge requests API.
-
-## v0.2 (2013-08-08)
-
-* provide a pip requirements.txt
-* drop some debug statements
-
-## v0.1 (2013-07-08)
-
-* Initial release
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
deleted file mode 100644
index b065886..0000000
--- a/CONTRIBUTING.rst
+++ /dev/null
@@ -1,160 +0,0 @@
-Contributing
-============
-
-You can contribute to the project in multiple ways:
-
-* Write documentation
-* Implement features
-* Fix bugs
-* Add unit and functional tests
-* Everything else you can think of
-
-Development workflow
---------------------
-
-Before contributing, please make sure you have `pre-commit <https://pre-commit.com>`_
-installed and configured. This will help automate adhering to code style and commit
-message guidelines described below:
-
-.. code-block:: bash
-
- cd python-gitlab/
- pip3 install --user pre-commit
- pre-commit install -t pre-commit -t commit-msg --install-hooks
-
-Please provide your patches as GitHub pull requests. Thanks!
-
-Commit message guidelines
--------------------------
-
-We enforce commit messages to be formatted using the `conventional-changelog <https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-commit-message-guidelines>`_.
-This leads to more readable messages that are easy to follow when looking through the project history.
-
-Code-Style
-----------
-
-We use black as code formatter, so you'll need to format your changes using the
-`black code formatter
-<https://github.com/python/black>`_. Pre-commit hooks will validate/format your code
-when committing. You can then stage any changes ``black`` added if the commit failed.
-
-To format your code according to our guidelines before committing, run:
-
-.. code-block:: bash
-
- cd python-gitlab/
- pip3 install --user black
- black .
-
-Running unit tests
-------------------
-
-Before submitting a pull request make sure that the tests and lint checks still succeed with
-your change. Unit tests and functional tests run in GitHub Actions and
-passing checks are mandatory to get merge requests accepted.
-
-Please write new unit tests with pytest and using `responses
-<https://github.com/getsentry/responses/>`_.
-An example can be found in ``tests/unit/objects/test_runner.py``
-
-You need to install ``tox`` (``pip3 install tox``) to run tests and lint checks locally:
-
-.. code-block:: bash
-
- # run unit tests using your installed python3, and all lint checks:
- tox -s
-
- # run unit tests for all supported python3 versions, and all lint checks:
- tox
-
- # run tests in one environment only:
- tox -epy38
-
- # build the documentation, the result will be generated in
- # build/sphinx/html/
- tox -edocs
-
-Running integration tests
--------------------------
-
-Integration tests run against a running gitlab instance, using a docker
-container. You need to have docker installed on the test machine, and your user
-must have the correct permissions to talk to the docker daemon.
-
-To run these tests:
-
-.. code-block:: bash
-
- # run the CLI tests:
- tox -e cli_func_v4
-
- # run the python API tests:
- tox -e py_func_v4
-
-When developing tests it can be a little frustrating to wait for GitLab to spin
-up every run. To prevent the containers from being cleaned up afterwards, pass
-`--keep-containers` to pytest, i.e.:
-
-.. code-block:: bash
-
- tox -e py_func_v4 -- --keep-containers
-
-If you then wish to test against a clean slate, you may perform a manual clean
-up of the containers by running:
-
-.. code-block:: bash
-
- docker-compose -f tests/functional/fixtures/docker-compose.yml -p pytest-python-gitlab down -v
-
-By default, the tests run against the latest version of the ``gitlab/gitlab-ce``
-image. You can override both the image and tag by providing either the
-``GITLAB_IMAGE`` or ``GITLAB_TAG`` environment variables.
-
-This way you can run tests against different versions, such as ``nightly`` for
-features in an upcoming release, or an older release (e.g. ``12.8.0-ce.0``).
-The tag must match an exact tag on Docker Hub:
-
-.. code-block:: bash
-
- # run tests against `nightly` or specific tag
- GITLAB_TAG=nightly tox -e py_func_v4
- GITLAB_TAG=12.8.0-ce.0 tox -e py_func_v4
-
- # run tests against the latest gitlab EE image
- GITLAB_IMAGE=gitlab/gitlab-ee tox -e py_func_v4
-
-A freshly configured gitlab container will be available at
-http://localhost:8080 (login ``root`` / password ``5iveL!fe``). A configuration
-for python-gitlab will be written in ``/tmp/python-gitlab.cfg``.
-
-To cleanup the environment delete the container:
-
-.. code-block:: bash
-
- docker rm -f gitlab-test
- docker rm -f gitlab-runner-test
-
-Releases
---------
-
-A release is automatically published once a month on the 28th if any commits merged
-to the main branch contain commit message types that signal a semantic version bump
-(``fix``, ``feat``, ``BREAKING CHANGE:``).
-
-Additionally, the release workflow can be run manually by maintainers to publish urgent
-fixes, either on GitHub or using the ``gh`` CLI with ``gh workflow run release.yml``.
-
-**Note:** As a maintainer, this means you should carefully review commit messages
-used by contributors in their pull requests. If scopes such as ``fix`` and ``feat``
-are applied to trivial commits not relevant to end users, it's best to squash their
-pull requests and summarize the addition in a single conventional commit.
-This avoids triggering incorrect version bumps and releases without functional changes.
-
-The release workflow uses `python-semantic-release
-<https://python-semantic-release.readthedocs.io>`_ and does the following:
-
-* Bumps the version in ``__version__.py`` and adds an entry in ``CHANGELOG.md``,
-* Commits and tags the changes, then pushes to the main branch as the ``github-actions`` user,
-* Creates a release from the tag and adds the changelog entry to the release notes,
-* Uploads the package as assets to the GitHub release,
-* Uploads the package to PyPI using ``PYPI_TOKEN`` (configured as a secret).
diff --git a/COPYING b/COPYING
deleted file mode 100644
index 65c5ca8..0000000
--- a/COPYING
+++ /dev/null
@@ -1,165 +0,0 @@
- GNU LESSER GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-
- This version of the GNU Lesser General Public License incorporates
-the terms and conditions of version 3 of the GNU General Public
-License, supplemented by the additional permissions listed below.
-
- 0. Additional Definitions.
-
- As used herein, "this License" refers to version 3 of the GNU Lesser
-General Public License, and the "GNU GPL" refers to version 3 of the GNU
-General Public License.
-
- "The Library" refers to a covered work governed by this License,
-other than an Application or a Combined Work as defined below.
-
- An "Application" is any work that makes use of an interface provided
-by the Library, but which is not otherwise based on the Library.
-Defining a subclass of a class defined by the Library is deemed a mode
-of using an interface provided by the Library.
-
- A "Combined Work" is a work produced by combining or linking an
-Application with the Library. The particular version of the Library
-with which the Combined Work was made is also called the "Linked
-Version".
-
- The "Minimal Corresponding Source" for a Combined Work means the
-Corresponding Source for the Combined Work, excluding any source code
-for portions of the Combined Work that, considered in isolation, are
-based on the Application, and not on the Linked Version.
-
- The "Corresponding Application Code" for a Combined Work means the
-object code and/or source code for the Application, including any data
-and utility programs needed for reproducing the Combined Work from the
-Application, but excluding the System Libraries of the Combined Work.
-
- 1. Exception to Section 3 of the GNU GPL.
-
- You may convey a covered work under sections 3 and 4 of this License
-without being bound by section 3 of the GNU GPL.
-
- 2. Conveying Modified Versions.
-
- If you modify a copy of the Library, and, in your modifications, a
-facility refers to a function or data to be supplied by an Application
-that uses the facility (other than as an argument passed when the
-facility is invoked), then you may convey a copy of the modified
-version:
-
- a) under this License, provided that you make a good faith effort to
- ensure that, in the event an Application does not supply the
- function or data, the facility still operates, and performs
- whatever part of its purpose remains meaningful, or
-
- b) under the GNU GPL, with none of the additional permissions of
- this License applicable to that copy.
-
- 3. Object Code Incorporating Material from Library Header Files.
-
- The object code form of an Application may incorporate material from
-a header file that is part of the Library. You may convey such object
-code under terms of your choice, provided that, if the incorporated
-material is not limited to numerical parameters, data structure
-layouts and accessors, or small macros, inline functions and templates
-(ten or fewer lines in length), you do both of the following:
-
- a) Give prominent notice with each copy of the object code that the
- Library is used in it and that the Library and its use are
- covered by this License.
-
- b) Accompany the object code with a copy of the GNU GPL and this license
- document.
-
- 4. Combined Works.
-
- You may convey a Combined Work under terms of your choice that,
-taken together, effectively do not restrict modification of the
-portions of the Library contained in the Combined Work and reverse
-engineering for debugging such modifications, if you also do each of
-the following:
-
- a) Give prominent notice with each copy of the Combined Work that
- the Library is used in it and that the Library and its use are
- covered by this License.
-
- b) Accompany the Combined Work with a copy of the GNU GPL and this license
- document.
-
- c) For a Combined Work that displays copyright notices during
- execution, include the copyright notice for the Library among
- these notices, as well as a reference directing the user to the
- copies of the GNU GPL and this license document.
-
- d) Do one of the following:
-
- 0) Convey the Minimal Corresponding Source under the terms of this
- License, and the Corresponding Application Code in a form
- suitable for, and under terms that permit, the user to
- recombine or relink the Application with a modified version of
- the Linked Version to produce a modified Combined Work, in the
- manner specified by section 6 of the GNU GPL for conveying
- Corresponding Source.
-
- 1) Use a suitable shared library mechanism for linking with the
- Library. A suitable mechanism is one that (a) uses at run time
- a copy of the Library already present on the user's computer
- system, and (b) will operate properly with a modified version
- of the Library that is interface-compatible with the Linked
- Version.
-
- e) Provide Installation Information, but only if you would otherwise
- be required to provide such information under section 6 of the
- GNU GPL, and only to the extent that such information is
- necessary to install and execute a modified version of the
- Combined Work produced by recombining or relinking the
- Application with a modified version of the Linked Version. (If
- you use option 4d0, the Installation Information must accompany
- the Minimal Corresponding Source and Corresponding Application
- Code. If you use option 4d1, you must provide the Installation
- Information in the manner specified by section 6 of the GNU GPL
- for conveying Corresponding Source.)
-
- 5. Combined Libraries.
-
- You may place library facilities that are a work based on the
-Library side by side in a single library together with other library
-facilities that are not Applications and are not covered by this
-License, and convey such a combined library under terms of your
-choice, if you do both of the following:
-
- a) Accompany the combined library with a copy of the same work based
- on the Library, uncombined with any other library facilities,
- conveyed under the terms of this License.
-
- b) Give prominent notice with the combined library that part of it
- is a work based on the Library, and explaining where to find the
- accompanying uncombined form of the same work.
-
- 6. Revised Versions of the GNU Lesser General Public License.
-
- The Free Software Foundation may publish revised and/or new versions
-of the GNU Lesser General Public License from time to time. Such new
-versions will be similar in spirit to the present version, but may
-differ in detail to address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Library as you received it specifies that a certain numbered version
-of the GNU Lesser General Public License "or any later version"
-applies to it, you have the option of following the terms and
-conditions either of that published version or of any later version
-published by the Free Software Foundation. If the Library as you
-received it does not specify a version number of the GNU Lesser
-General Public License, you may choose any version of the GNU Lesser
-General Public License ever published by the Free Software Foundation.
-
- If the Library as you received it specifies that a proxy can decide
-whether future versions of the GNU Lesser General Public License shall
-apply, that proxy's public statement of acceptance of any version is
-permanent authorization for you to choose that version for the
-Library.
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644
index 72f3cfd..0000000
--- a/Dockerfile
+++ /dev/null
@@ -1,17 +0,0 @@
-FROM python:3.10-alpine AS build
-
-WORKDIR /opt/python-gitlab
-COPY . .
-RUN python setup.py bdist_wheel
-
-FROM python:3.10-alpine
-
-WORKDIR /opt/python-gitlab
-COPY --from=build /opt/python-gitlab/dist dist/
-RUN pip install PyYaml
-RUN pip install $(find dist -name *.whl) && \
- rm -rf dist/
-COPY docker-entrypoint.sh /usr/local/bin/
-
-ENTRYPOINT ["docker-entrypoint.sh"]
-CMD ["--version"]
diff --git a/MANIFEST.in b/MANIFEST.in
deleted file mode 100644
index 8c11b80..0000000
--- a/MANIFEST.in
+++ /dev/null
@@ -1,4 +0,0 @@
-include COPYING AUTHORS CHANGELOG.md requirements*.txt
-include tox.ini
-recursive-include tests *
-recursive-include docs *j2 *.py *.rst api/*.rst Makefile make.bat
diff --git a/README.rst b/README.rst
index 2a12f56..4facd13 100644
--- a/README.rst
+++ b/README.rst
@@ -1,99 +1,4 @@
-.. image:: https://github.com/python-gitlab/python-gitlab/workflows/Test/badge.svg
- :target: https://github.com/python-gitlab/python-gitlab/actions
-
-.. image:: https://badge.fury.io/py/python-gitlab.svg
- :target: https://badge.fury.io/py/python-gitlab
-
-.. image:: https://readthedocs.org/projects/python-gitlab/badge/?version=latest
- :target: https://python-gitlab.readthedocs.org/en/latest/?badge=latest
-
-.. image:: https://codecov.io/github/python-gitlab/python-gitlab/coverage.svg?branch=master
- :target: https://codecov.io/github/python-gitlab/python-gitlab?branch=master
-
-.. image:: https://img.shields.io/pypi/pyversions/python-gitlab.svg
- :target: https://pypi.python.org/pypi/python-gitlab
-
-.. image:: https://img.shields.io/gitter/room/python-gitlab/Lobby.svg
- :target: https://gitter.im/python-gitlab/Lobby
-
-.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
- :target: https://github.com/python/black
-
Python GitLab
=============
-``python-gitlab`` is a Python package providing access to the GitLab server API.
-
-It supports the v4 API of GitLab, and provides a CLI tool (``gitlab``).
-
-Installation
-============
-
-Requirements
-------------
-
-python-gitlab depends on:
-
-* `python-requests <https://2.python-requests.org/en/latest/>`_
-
-Install with pip
-----------------
-
-.. code-block:: console
-
- pip install python-gitlab
-
-
-Using the python-gitlab docker image
-====================================
-
-How to build
-------------
-
-``docker build -t python-gitlab:TAG .``
-
-How to use
-----------
-
-``docker run -it --rm -e GITLAB_PRIVATE_TOKEN=<your token> -v /path/to/python-gitlab.cfg:/python-gitlab.cfg python-gitlab <command> ...``
-
-or run it directly from the upstream image:
-
-``docker run -it --rm -e GITLAB_PRIVATE_TOKEN=<your token> -v /path/to/python-gitlab.cfg:/python-gitlab.cfg registry.gitlab.com/python-gitlab/python-gitlab:latest <command> ...``
-
-To change the GitLab URL, use `-e GITLAB_URL=<your url>`
-
-Bring your own config file:
-``docker run -it --rm -v /path/to/python-gitlab.cfg:/python-gitlab.cfg -e GITLAB_CFG=/python-gitlab.cfg python-gitlab <command> ...``
-
-
-Bug reports
-===========
-
-Please report bugs and feature requests at
-https://github.com/python-gitlab/python-gitlab/issues.
-
-Gitter Community Chat
-=====================
-
-There is a `gitter <https://gitter.im/python-gitlab/Lobby>`_ community chat
-available at https://gitter.im/python-gitlab/Lobby
-
-Documentation
-=============
-
-The full documentation for CLI and API is available on `readthedocs
-<http://python-gitlab.readthedocs.org/en/stable/>`_.
-
-Build the docs
---------------
-You can build the documentation using ``sphinx``::
-
- pip install sphinx
- python setup.py build_sphinx
-
-
-Contributing
-============
-
-For guidelines for contributing to ``python-gitlab``, refer to `CONTRIBUTING.rst <https://github.com/python-gitlab/python-gitlab/blob/master/CONTRIBUTING.rst>`_.
+The ``master`` branch is no longer used. Please use the ``main`` branch.
diff --git a/codecov.yml b/codecov.yml
deleted file mode 100644
index 0a82dcd..0000000
--- a/codecov.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-codecov:
- require_ci_to_pass: yes
-
-coverage:
- precision: 2
- round: down
- range: "70...100"
-
-comment:
- layout: "diff,flags,files"
- behavior: default
- require_changes: yes
-
-github_checks:
- annotations: true
diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh
deleted file mode 100755
index 5835acd..0000000
--- a/docker-entrypoint.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/sh
-
-GITLAB_CFG=${GITLAB_CFG:-"/etc/python-gitlab-default.cfg"}
-
-cat << EOF > /etc/python-gitlab-default.cfg
-[global]
-default = gitlab
-ssl_verify = ${GITLAB_SSL_VERIFY:-true}
-timeout = ${GITLAB_TIMEOUT:-5}
-api_version = ${GITLAB_API_VERSION:-4}
-per_page = ${GITLAB_PER_PAGE:-10}
-
-[gitlab]
-url = ${GITLAB_URL:-https://gitlab.com}
-private_token = ${GITLAB_PRIVATE_TOKEN}
-oauth_token = ${GITLAB_OAUTH_TOKEN}
-job_token = ${GITLAB_JOB_TOKEN}
-http_username = ${GITLAB_HTTP_USERNAME}
-http_password = ${GITLAB_HTTP_PASSWORD}
-EOF
-
-exec gitlab --config-file "${GITLAB_CFG}" "$@"
diff --git a/docs/Makefile b/docs/Makefile
deleted file mode 100644
index a59769c..0000000
--- a/docs/Makefile
+++ /dev/null
@@ -1,177 +0,0 @@
-# Makefile for Sphinx documentation
-#
-
-# You can set these variables from the command line.
-SPHINXOPTS =
-SPHINXBUILD = sphinx-build
-PAPER =
-BUILDDIR = _build
-
-# User-friendly check for sphinx-build
-ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
-$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
-endif
-
-# Internal variables.
-PAPEROPT_a4 = -D latex_paper_size=a4
-PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-# the i18n builder cannot share the environment and doctrees with the others
-I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-
-.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
-
-help:
- @echo "Please use \`make <target>' where <target> is one of"
- @echo " html to make standalone HTML files"
- @echo " dirhtml to make HTML files named index.html in directories"
- @echo " singlehtml to make a single large HTML file"
- @echo " pickle to make pickle files"
- @echo " json to make JSON files"
- @echo " htmlhelp to make HTML files and a HTML help project"
- @echo " qthelp to make HTML files and a qthelp project"
- @echo " devhelp to make HTML files and a Devhelp project"
- @echo " epub to make an epub"
- @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
- @echo " latexpdf to make LaTeX files and run them through pdflatex"
- @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
- @echo " text to make text files"
- @echo " man to make manual pages"
- @echo " texinfo to make Texinfo files"
- @echo " info to make Texinfo files and run them through makeinfo"
- @echo " gettext to make PO message catalogs"
- @echo " changes to make an overview of all changed/added/deprecated items"
- @echo " xml to make Docutils-native XML files"
- @echo " pseudoxml to make pseudoxml-XML files for display purposes"
- @echo " linkcheck to check all external links for integrity"
- @echo " doctest to run all doctests embedded in the documentation (if enabled)"
-
-clean:
- rm -rf $(BUILDDIR)/*
-
-html:
- $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
- @echo
- @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
-
-dirhtml:
- $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
- @echo
- @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
-
-singlehtml:
- $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
- @echo
- @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
-
-pickle:
- $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
- @echo
- @echo "Build finished; now you can process the pickle files."
-
-json:
- $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
- @echo
- @echo "Build finished; now you can process the JSON files."
-
-htmlhelp:
- $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
- @echo
- @echo "Build finished; now you can run HTML Help Workshop with the" \
- ".hhp project file in $(BUILDDIR)/htmlhelp."
-
-qthelp:
- $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
- @echo
- @echo "Build finished; now you can run "qcollectiongenerator" with the" \
- ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
- @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/python-gitlab.qhcp"
- @echo "To view the help file:"
- @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/python-gitlab.qhc"
-
-devhelp:
- $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
- @echo
- @echo "Build finished."
- @echo "To view the help file:"
- @echo "# mkdir -p $$HOME/.local/share/devhelp/python-gitlab"
- @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/python-gitlab"
- @echo "# devhelp"
-
-epub:
- $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
- @echo
- @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
-
-latex:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
- @echo
- @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
- @echo "Run \`make' in that directory to run these through (pdf)latex" \
- "(use \`make latexpdf' here to do that automatically)."
-
-latexpdf:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
- @echo "Running LaTeX files through pdflatex..."
- $(MAKE) -C $(BUILDDIR)/latex all-pdf
- @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
-
-latexpdfja:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
- @echo "Running LaTeX files through platex and dvipdfmx..."
- $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
- @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
-
-text:
- $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
- @echo
- @echo "Build finished. The text files are in $(BUILDDIR)/text."
-
-man:
- $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
- @echo
- @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
-
-texinfo:
- $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
- @echo
- @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
- @echo "Run \`make' in that directory to run these through makeinfo" \
- "(use \`make info' here to do that automatically)."
-
-info:
- $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
- @echo "Running Texinfo files through makeinfo..."
- make -C $(BUILDDIR)/texinfo info
- @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
-
-gettext:
- $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
- @echo
- @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
-
-changes:
- $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
- @echo
- @echo "The overview file is in $(BUILDDIR)/changes."
-
-linkcheck:
- $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
- @echo
- @echo "Link check complete; look for any errors in the above output " \
- "or in $(BUILDDIR)/linkcheck/output.txt."
-
-doctest:
- $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
- @echo "Testing of doctests in the sources finished, look at the " \
- "results in $(BUILDDIR)/doctest/output.txt."
-
-xml:
- $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
- @echo
- @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
-
-pseudoxml:
- $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
- @echo
- @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
diff --git a/docs/__init__.py b/docs/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/docs/__init__.py
+++ /dev/null
diff --git a/docs/_templates/breadcrumbs.html b/docs/_templates/breadcrumbs.html
deleted file mode 100644
index 68648fa..0000000
--- a/docs/_templates/breadcrumbs.html
+++ /dev/null
@@ -1,24 +0,0 @@
-{# Support for Sphinx 1.3+ page_source_suffix, but don't break old builds. #}
-
-{% if page_source_suffix %}
-{% set suffix = page_source_suffix %}
-{% else %}
-{% set suffix = source_suffix %}
-{% endif %}
-
-<div role="navigation" aria-label="breadcrumbs navigation">
- <ul class="wy-breadcrumbs">
- <li><a href="{{ pathto(master_doc) }}">Docs</a> &raquo;</li>
- {% for doc in parents %}
- <li><a href="{{ doc.link|e }}">{{ doc.title }}</a> &raquo;</li>
- {% endfor %}
- <li>{{ title }}</li>
- <li class="wy-breadcrumbs-aside">
- {% if pagename != "search" %}
- <a href="https://github.com/python-gitlab/python-gitlab/blob/master/{{ conf_py_path }}{{ pagename }}{{ suffix }}" class="fa fa-github"> Edit on GitHub</a>
- | <a href="https://github.com/python-gitlab/python-gitlab/issues/new?title=Documentation+bug&body=%0A%0A------%0AIn+page:+{{ pagename }}{{ suffix }}">Report a bug</a>
- {% endif %}
- </li>
- </ul>
- <hr/>
-</div>
diff --git a/docs/api-objects.rst b/docs/api-objects.rst
deleted file mode 100644
index 567344f..0000000
--- a/docs/api-objects.rst
+++ /dev/null
@@ -1,58 +0,0 @@
-############
-API examples
-############
-
-.. toctree::
- :maxdepth: 1
-
- gl_objects/access_requests
- gl_objects/appearance
- gl_objects/applications
- gl_objects/emojis
- gl_objects/badges
- gl_objects/branches
- gl_objects/clusters
- gl_objects/messages
- gl_objects/commits
- gl_objects/deploy_keys
- gl_objects/deploy_tokens
- gl_objects/deployments
- gl_objects/discussions
- gl_objects/environments
- gl_objects/events
- gl_objects/epics
- gl_objects/features
- gl_objects/geo_nodes
- gl_objects/groups
- gl_objects/issues
- gl_objects/keys
- gl_objects/boards
- gl_objects/labels
- gl_objects/notifications
- gl_objects/mrs
- gl_objects/mr_approvals
- gl_objects/milestones
- gl_objects/namespaces
- gl_objects/notes
- gl_objects/packages
- gl_objects/pagesdomains
- gl_objects/personal_access_tokens
- gl_objects/pipelines_and_jobs
- gl_objects/projects
- gl_objects/project_access_tokens
- gl_objects/protected_branches
- gl_objects/releases
- gl_objects/runners
- gl_objects/remote_mirrors
- gl_objects/repositories
- gl_objects/repository_tags
- gl_objects/search
- gl_objects/settings
- gl_objects/snippets
- gl_objects/system_hooks
- gl_objects/templates
- gl_objects/todos
- gl_objects/users
- gl_objects/variables
- gl_objects/sidekiq
- gl_objects/wikis
diff --git a/docs/api-usage.rst b/docs/api-usage.rst
deleted file mode 100644
index f30ed03..0000000
--- a/docs/api-usage.rst
+++ /dev/null
@@ -1,458 +0,0 @@
-############################
-Getting started with the API
-############################
-
-python-gitlab only supports GitLab API v4.
-
-``gitlab.Gitlab`` class
-=======================
-
-To connect to GitLab.com or another GitLab instance, create a ``gitlab.Gitlab`` object:
-
-.. code-block:: python
-
- import gitlab
-
- # anonymous read-only access for public resources (GitLab.com)
- gl = gitlab.Gitlab()
-
- # anonymous read-only access for public resources (self-hosted GitLab instance)
- gl = gitlab.Gitlab('https://gitlab.example.com')
-
- # private token or personal token authentication (GitLab.com)
- gl = gitlab.Gitlab(private_token='JVNSESs8EwWRx5yDxM5q')
-
- # private token or personal token authentication (self-hosted GitLab instance)
- gl = gitlab.Gitlab(url='https://gitlab.example.com', private_token='JVNSESs8EwWRx5yDxM5q')
-
- # oauth token authentication
- gl = gitlab.Gitlab('https://gitlab.example.com', oauth_token='my_long_token_here')
-
- # job token authentication (to be used in CI)
- # bear in mind the limitations of the API endpoints it supports:
- # https://docs.gitlab.com/ee/ci/jobs/ci_job_token.html
- import os
- gl = gitlab.Gitlab('https://gitlab.example.com', job_token=os.environ['CI_JOB_TOKEN'])
-
- # Define your own custom user agent for requests
- gl = gitlab.Gitlab('https://gitlab.example.com', user_agent='my-package/1.0.0')
-
- # make an API request to create the gl.user object. This is mandatory if you
- # use the username/password authentication - not required for token authentication,
- # and will not work with job tokens.
- gl.auth()
-
-You can also use configuration files to create ``gitlab.Gitlab`` objects:
-
-.. code-block:: python
-
- gl = gitlab.Gitlab.from_config('somewhere', ['/tmp/gl.cfg'])
-
-See the :ref:`cli_configuration` section for more information about
-configuration files.
-
-.. warning::
-
- Note that a url that results in 301/302 redirects will raise an error,
- so it is highly recommended to use the final destination in the ``url`` field.
- For example, if the GitLab server you are using redirects requests from http
- to https, make sure to use the ``https://`` protocol in the URL definition.
-
- A URL that redirects using 301/302 (rather than 307/308) will most likely
- `cause malformed POST and PUT requests <https://github.com/psf/requests/blob/c45a4dfe6bfc6017d4ea7e9f051d6cc30972b310/requests/sessions.py#L324-L332>`_.
-
- python-gitlab will therefore raise a ``RedirectionError`` when it encounters
- a redirect which it believes will cause such an error, to avoid confusion
- between successful GET and failing POST/PUT requests on the same instance.
-
-Note on password authentication
--------------------------------
-
-The ``/session`` API endpoint used for username/password authentication has
-been removed from GitLab in version 10.2, and is not available on gitlab.com
-anymore. Personal token authentication is the preferred authentication method.
-
-If you need username/password authentication, you can use cookie-based
-authentication. You can use the web UI form to authenticate, retrieve cookies,
-and then use a custom ``requests.Session`` object to connect to the GitLab API.
-The following code snippet demonstrates how to automate this:
-https://gist.github.com/gpocentek/bd4c3fbf8a6ce226ebddc4aad6b46c0a.
-
-See `issue 380 <https://github.com/python-gitlab/python-gitlab/issues/380>`_
-for a detailed discussion.
-
-Managers
-========
-
-The ``gitlab.Gitlab`` class provides managers to access the GitLab resources.
-Each manager provides a set of methods to act on the resources. The available
-methods depend on the resource type.
-
-Examples:
-
-.. code-block:: python
-
- # list all the projects
- projects = gl.projects.list()
- for project in projects:
- print(project)
-
- # get the group with id == 2
- group = gl.groups.get(2)
- for project in group.projects.list():
- print(project)
-
- # create a new user
- user_data = {'email': 'jen@foo.com', 'username': 'jen', 'name': 'Jen'}
- user = gl.users.create(user_data)
- print(user)
-
-You can list the mandatory and optional attributes for object creation and
-update with the manager's ``get_create_attrs()`` and ``get_update_attrs()``
-methods. They return 2 tuples, the first one is the list of mandatory
-attributes, the second one is the list of optional attribute:
-
-.. code-block:: python
-
- # v4 only
- print(gl.projects.get_create_attrs())
- (('name',), ('path', 'namespace_id', ...))
-
-The attributes of objects are defined upon object creation, and depend on the
-GitLab API itself. To list the available information associated with an object
-use the ``attributes`` attribute:
-
-.. code-block:: python
-
- project = gl.projects.get(1)
- print(project.attributes)
-
-Some objects also provide managers to access related GitLab resources:
-
-.. code-block:: python
-
- # list the issues for a project
- project = gl.projects.get(1)
- issues = project.issues.list()
-
-python-gitlab allows to send any data to the GitLab server when making queries.
-In case of invalid or missing arguments python-gitlab will raise an exception
-with the GitLab server error message:
-
-.. code-block:: python
-
- >>> gl.projects.list(sort='invalid value')
- ...
- GitlabListError: 400: sort does not have a valid value
-
-You can use the ``query_parameters`` argument to send arguments that would
-conflict with python or python-gitlab when using them as kwargs:
-
-.. code-block:: python
-
- gl.user_activities.list(from='2019-01-01') ## invalid
-
- gl.user_activities.list(query_parameters={'from': '2019-01-01'}) # OK
-
-Gitlab Objects
-==============
-
-You can update or delete a remote object when it exists locally:
-
-.. code-block:: python
-
- # update the attributes of a resource
- project = gl.projects.get(1)
- project.wall_enabled = False
- # don't forget to apply your changes on the server:
- project.save()
-
- # delete the resource
- project.delete()
-
-Some classes provide additional methods, allowing more actions on the GitLab
-resources. For example:
-
-.. code-block:: python
-
- # star a git repository
- project = gl.projects.get(1)
- project.star()
-
-Base types
-==========
-
-The ``gitlab`` package provides some base types.
-
-* ``gitlab.Gitlab`` is the primary class, handling the HTTP requests. It holds
- the GitLab URL and authentication information.
-* ``gitlab.base.RESTObject`` is the base class for all the GitLab v4 objects.
- These objects provide an abstraction for GitLab resources (projects, groups,
- and so on).
-* ``gitlab.base.RESTManager`` is the base class for v4 objects managers,
- providing the API to manipulate the resources and their attributes.
-
-Lazy objects
-============
-
-To avoid useless API calls to the server you can create lazy objects. These
-objects are created locally using a known ID, and give access to other managers
-and methods.
-
-The following example will only make one API call to the GitLab server to star
-a project (the previous example used 2 API calls):
-
-.. code-block:: python
-
- # star a git repository
- project = gl.projects.get(1, lazy=True) # no API call
- project.star() # API call
-
-Pagination
-==========
-
-You can use pagination to iterate over long lists. All the Gitlab objects
-listing methods support the ``page`` and ``per_page`` parameters:
-
-.. code-block:: python
-
- ten_first_groups = gl.groups.list(page=1, per_page=10)
-
-.. warning::
-
- The first page is page 1, not page 0.
-
-By default GitLab does not return the complete list of items. Use the ``all``
-parameter to get all the items when using listing methods:
-
-.. code-block:: python
-
- all_groups = gl.groups.list(all=True)
- all_owned_projects = gl.projects.list(owned=True, all=True)
-
-You can define the ``per_page`` value globally to avoid passing it to every
-``list()`` method call:
-
-.. code-block:: python
-
- gl = gitlab.Gitlab(url, token, per_page=50)
-
-Gitlab allows to also use keyset pagination. You can supply it to your project listing,
-but you can also do so globally. Be aware that GitLab then also requires you to only use supported
-order options. At the time of writing, only ``order_by="id"`` works.
-
-.. code-block:: python
-
- gl = gitlab.Gitlab(url, token, pagination="keyset", order_by="id", per_page=100)
- gl.projects.list()
-
-Reference:
-https://docs.gitlab.com/ce/api/README.html#keyset-based-pagination
-
-``list()`` methods can also return a generator object which will handle the
-next calls to the API when required. This is the recommended way to iterate
-through a large number of items:
-
-.. code-block:: python
-
- items = gl.groups.list(as_list=False)
- for item in items:
- print(item.attributes)
-
-The generator exposes extra listing information as received from the server:
-
-* ``current_page``: current page number (first page is 1)
-* ``prev_page``: if ``None`` the current page is the first one
-* ``next_page``: if ``None`` the current page is the last one
-* ``per_page``: number of items per page
-* ``total_pages``: total number of pages available
-* ``total``: total number of items in the list
-
-Sudo
-====
-
-If you have the administrator status, you can use ``sudo`` to act as another
-user. For example:
-
-.. code-block:: python
-
- p = gl.projects.create({'name': 'awesome_project'}, sudo='user1')
-
-Advanced HTTP configuration
-===========================
-
-python-gitlab relies on ``requests`` ``Session`` objects to perform all the
-HTTP requests to the Gitlab servers.
-
-You can provide your own ``Session`` object with custom configuration when
-you create a ``Gitlab`` object.
-
-Context manager
----------------
-
-You can use ``Gitlab`` objects as context managers. This makes sure that the
-``requests.Session`` object associated with a ``Gitlab`` instance is always
-properly closed when you exit a ``with`` block:
-
-.. code-block:: python
-
- with gitlab.Gitlab(host, token) as gl:
- gl.projects.list()
-
-.. warning::
-
- The context manager will also close the custom ``Session`` object you might
- have used to build the ``Gitlab`` instance.
-
-Proxy configuration
--------------------
-
-The following sample illustrates how to define a proxy configuration when using
-python-gitlab:
-
-.. code-block:: python
-
- import gitlab
- import requests
-
- session = requests.Session()
- session.proxies = {
- 'https': os.environ.get('https_proxy'),
- 'http': os.environ.get('http_proxy'),
- }
- gl = gitlab.gitlab(url, token, api_version=4, session=session)
-
-Reference:
-https://2.python-requests.org/en/master/user/advanced/#proxies
-
-SSL certificate verification
-----------------------------
-
-python-gitlab relies on the CA certificate bundle in the `certifi` package
-that comes with the requests library.
-
-If you need python-gitlab to use your system CA store instead, you can provide
-the path to the CA bundle in the `REQUESTS_CA_BUNDLE` environment variable.
-
-Reference:
-https://2.python-requests.org/en/master/user/advanced/#ssl-cert-verification
-
-Client side certificate
------------------------
-
-The following sample illustrates how to use a client-side certificate:
-
-.. code-block:: python
-
- import gitlab
- import requests
-
- session = requests.Session()
- session.cert = ('/path/to/client.cert', '/path/to/client.key')
- gl = gitlab.gitlab(url, token, api_version=4, session=session)
-
-Reference:
-https://2.python-requests.org/en/master/user/advanced/#client-side-certificates
-
-Rate limits
------------
-
-python-gitlab obeys the rate limit of the GitLab server by default. On
-receiving a 429 response (Too Many Requests), python-gitlab sleeps for the
-amount of time in the Retry-After header that GitLab sends back. If GitLab
-does not return a response with the Retry-After header, python-gitlab will
-perform an exponential backoff.
-
-If you don't want to wait, you can disable the rate-limiting feature, by
-supplying the ``obey_rate_limit`` argument.
-
-.. code-block:: python
-
- import gitlab
- import requests
-
- gl = gitlab.gitlab(url, token, api_version=4)
- gl.projects.list(all=True, obey_rate_limit=False)
-
-If you do not disable the rate-limiting feature, you can supply a custom value
-for ``max_retries``; by default, this is set to 10. To retry without bound when
-throttled, you can set this parameter to -1. This parameter is ignored if
-``obey_rate_limit`` is set to ``False``.
-
-.. code-block:: python
-
- import gitlab
- import requests
-
- gl = gitlab.gitlab(url, token, api_version=4)
- gl.projects.list(all=True, max_retries=12)
-
-.. warning::
-
- You will get an Exception, if you then go over the rate limit of your GitLab instance.
-
-Transient errors
-----------------
-
-GitLab server can sometimes return a transient HTTP error.
-python-gitlab can automatically retry in such case, when
-``retry_transient_errors`` argument is set to ``True``. When enabled,
-HTTP error codes 500 (Internal Server Error), 502 (502 Bad Gateway),
-503 (Service Unavailable), and 504 (Gateway Timeout) are retried. By
-default an exception is raised for these errors.
-
-.. code-block:: python
-
- import gitlab
- import requests
-
- gl = gitlab.gitlab(url, token, api_version=4)
- gl.projects.list(all=True, retry_transient_errors=True)
-
-The default ``retry_transient_errors`` can also be set on the ``Gitlab`` object
-and overridden by individual API calls.
-
-.. code-block:: python
-
- import gitlab
- import requests
- gl = gitlab.gitlab(url, token, api_version=4, retry_transient_errors=True)
- gl.projects.list(all=True) # retries due to default value
- gl.projects.list(all=True, retry_transient_errors=False) # does not retry
-
-Timeout
--------
-
-python-gitlab will by default use the ``timeout`` option from it's configuration
-for all requests. This is passed downwards to the ``requests`` module at the
-time of making the HTTP request. However if you would like to override the
-global timeout parameter for a particular call, you can provide the ``timeout``
-parameter to that API invocation:
-
-.. code-block:: python
-
- import gitlab
-
- gl = gitlab.gitlab(url, token, api_version=4)
- gl.projects.import_github(ACCESS_TOKEN, 123456, "root", timeout=120.0)
-
-.. _object_attributes:
-
-Attributes in updated objects
-=============================
-
-When methods manipulate an existing object, such as with ``refresh()`` and ``save()``,
-the object will only have attributes that were returned by the server. In some cases,
-such as when the initial request fetches attributes that are needed later for additional
-processing, this may not be desired:
-
-.. code-block:: python
-
- project = gl.projects.get(1, statistics=True)
- project.statistics
-
- project.refresh()
- project.statistics # AttributeError
-
-To avoid this, either copy the object/attributes before calling ``refresh()``/``save()``
-or subsequently perform another ``get()`` call as needed, to fetch the attributes you want.
diff --git a/docs/api/gitlab.rst b/docs/api/gitlab.rst
deleted file mode 100644
index c13ae53..0000000
--- a/docs/api/gitlab.rst
+++ /dev/null
@@ -1,87 +0,0 @@
-API reference (``gitlab`` package)
-==================================
-
-Module contents
----------------
-
-.. automodule:: gitlab
- :members:
- :undoc-members:
- :show-inheritance:
-
-.. autoclass:: gitlab.Gitlab
- :members:
- :undoc-members:
- :show-inheritance:
-
-.. autoclass:: gitlab.GitlabList
- :members:
- :undoc-members:
- :show-inheritance:
-
-
-Subpackages
------------
-
-.. toctree::
-
- gitlab.v4
-
-Submodules
-----------
-
-gitlab.base module
-------------------
-
-.. automodule:: gitlab.base
- :members:
- :undoc-members:
- :show-inheritance:
-
-gitlab.cli module
------------------
-
-.. automodule:: gitlab.cli
- :members:
- :undoc-members:
- :show-inheritance:
-
-gitlab.config module
---------------------
-
-.. automodule:: gitlab.config
- :members:
- :undoc-members:
- :show-inheritance:
-
-gitlab.const module
--------------------
-
-.. automodule:: gitlab.const
- :members:
- :undoc-members:
- :show-inheritance:
-
-gitlab.exceptions module
-------------------------
-
-.. automodule:: gitlab.exceptions
- :members:
- :undoc-members:
- :show-inheritance:
-
-gitlab.mixins module
---------------------
-
-.. automodule:: gitlab.mixins
- :members:
- :undoc-members:
- :show-inheritance:
-
-gitlab.utils module
--------------------
-
-.. automodule:: gitlab.utils
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/api/gitlab.v4.rst b/docs/api/gitlab.v4.rst
deleted file mode 100644
index 70358c1..0000000
--- a/docs/api/gitlab.v4.rst
+++ /dev/null
@@ -1,22 +0,0 @@
-gitlab.v4 package
-=================
-
-Submodules
-----------
-
-gitlab.v4.objects module
-------------------------
-
-.. automodule:: gitlab.v4.objects
- :members:
- :undoc-members:
- :show-inheritance:
-
-
-Module contents
----------------
-
-.. automodule:: gitlab.v4
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/changelog.md b/docs/changelog.md
deleted file mode 100644
index 66efc0f..0000000
--- a/docs/changelog.md
+++ /dev/null
@@ -1,2 +0,0 @@
-```{include} ../CHANGELOG.md
-```
diff --git a/docs/cli-objects.rst b/docs/cli-objects.rst
deleted file mode 100644
index d6648f6..0000000
--- a/docs/cli-objects.rst
+++ /dev/null
@@ -1,17 +0,0 @@
-##################################
-CLI reference (``gitlab`` command)
-##################################
-
-.. warning::
-
- The following is a complete, auto-generated list of subcommands available
- via the :command:`gitlab` command-line tool. Some of the actions may
- currently not work as expected or lack functionality available via the API.
-
- Please see the existing `list of CLI related issues`_, or open a new one if
- it is not already listed there.
-
-.. _list of CLI related issues: https://github.com/python-gitlab/python-gitlab/issues?q=is%3Aopen+is%3Aissue+label%3Acli
-
-.. autoprogram:: gitlab.cli:docs()
- :prog: gitlab
diff --git a/docs/cli-usage.rst b/docs/cli-usage.rst
deleted file mode 100644
index ea10f93..0000000
--- a/docs/cli-usage.rst
+++ /dev/null
@@ -1,484 +0,0 @@
-####################
-``gitlab`` CLI usage
-####################
-
-``python-gitlab`` provides a :command:`gitlab` command-line tool to interact
-with GitLab servers. It uses a configuration file to define how to connect to
-the servers.
-
-.. _cli_configuration:
-
-Configuration
-=============
-
-Files
------
-
-``gitlab`` looks up 3 configuration files by default:
-
-``PYTHON_GITLAB_CFG`` environment variable
- An environment variable that contains the path to a configuration file
-
-``/etc/python-gitlab.cfg``
- System-wide configuration file
-
-``~/.python-gitlab.cfg``
- User configuration file
-
-You can use a different configuration file with the ``--config-file`` option.
-
-Content
--------
-
-The configuration file uses the ``INI`` format. It contains at least a
-``[global]`` section, and a specific section for each GitLab server. For
-example:
-
-.. code-block:: ini
-
- [global]
- default = somewhere
- ssl_verify = true
- timeout = 5
-
- [somewhere]
- url = https://some.whe.re
- private_token = vTbFeqJYCY3sibBP7BZM
- api_version = 4
-
- [elsewhere]
- url = http://else.whe.re:8080
- private_token = helper: path/to/helper.sh
- timeout = 1
-
-The ``default`` option of the ``[global]`` section defines the GitLab server to
-use if no server is explicitly specified with the ``--gitlab`` CLI option.
-
-The ``[global]`` section also defines the values for the default connection
-parameters. You can override the values in each GitLab server section.
-
-.. list-table:: Global options
- :header-rows: 1
-
- * - Option
- - Possible values
- - Description
- * - ``ssl_verify``
- - ``True``, ``False``, or a ``str``
- - Verify the SSL certificate. Set to ``False`` to disable verification,
- though this will create warnings. Any other value is interpreted as path
- to a CA_BUNDLE file or directory with certificates of trusted CAs.
- * - ``timeout``
- - Integer
- - Number of seconds to wait for an answer before failing.
- * - ``api_version``
- - ``4``
- - The API version to use to make queries. Only ``4`` is available since 1.5.0.
- * - ``per_page``
- - Integer between 1 and 100
- - The number of items to return in listing queries. GitLab limits the
- value at 100.
- * - ``user_agent``
- - ``str``
- - A string defining a custom user agent to use when ``gitlab`` makes requests.
-
-You must define the ``url`` in each GitLab server section.
-
-.. warning::
-
- Note that a url that results in 301/302 redirects will raise an error,
- so it is highly recommended to use the final destination in the ``url`` field.
- For example, if the GitLab server you are using redirects requests from http
- to https, make sure to use the ``https://`` protocol in the URL definition.
-
- A URL that redirects using 301/302 (rather than 307/308) will most likely
- `cause malformed POST and PUT requests <https://github.com/psf/requests/blob/c45a4dfe6bfc6017d4ea7e9f051d6cc30972b310/requests/sessions.py#L324-L332>`_.
-
- python-gitlab will therefore raise a ``RedirectionError`` when it encounters
- a redirect which it believes will cause such an error, to avoid confusion
- between successful GET and failing POST/PUT requests on the same instance.
-
-Only one of ``private_token``, ``oauth_token`` or ``job_token`` should be
-defined. If neither are defined an anonymous request will be sent to the Gitlab
-server, with very limited permissions.
-
-We recommend that you use `Credential helpers`_ to securely store your tokens.
-
-.. list-table:: GitLab server options
- :header-rows: 1
-
- * - Option
- - Description
- * - ``url``
- - URL for the GitLab server. Do **NOT** use a URL which redirects.
- * - ``private_token``
- - Your user token. Login/password is not supported. Refer to `the
- official documentation
- <https://docs.gitlab.com/ce/user/profile/personal_access_tokens.html>`__
- to learn how to obtain a token.
- * - ``oauth_token``
- - An Oauth token for authentication. The Gitlab server must be configured
- to support this authentication method.
- * - ``job_token``
- - Your job token. See `the official documentation
- <https://docs.gitlab.com/ce/api/jobs.html#get-job-artifacts>`__
- to learn how to obtain a token.
- * - ``api_version``
- - GitLab API version to use. Only ``4`` is available since 1.5.0.
- * - ``http_username``
- - Username for optional HTTP authentication
- * - ``http_password``
- - Password for optional HTTP authentication
-
-
-Credential helpers
-------------------
-
-For all configuration options that contain secrets (``http_password``,
-``personal_token``, ``oauth_token``, ``job_token``), you can specify
-a helper program to retrieve the secret indicated by a ``helper:``
-prefix. This allows you to fetch values from a local keyring store
-or cloud-hosted vaults such as Bitwarden. Environment variables are
-expanded if they exist and ``~`` expands to your home directory.
-
-It is expected that the helper program prints the secret to standard output.
-To use shell features such as piping to retrieve the value, you will need
-to use a wrapper script; see below.
-
-Example for a `keyring <https://github.com/jaraco/keyring>`_ helper:
-
-.. code-block:: ini
-
- [global]
- default = somewhere
- ssl_verify = true
- timeout = 5
-
- [somewhere]
- url = http://somewhe.re
- private_token = helper: keyring get Service Username
- timeout = 1
-
-Example for a `pass <https://www.passwordstore.org>`_ helper with a wrapper script:
-
-.. code-block:: ini
-
- [global]
- default = somewhere
- ssl_verify = true
- timeout = 5
-
- [somewhere]
- url = http://somewhe.re
- private_token = helper: /path/to/helper.sh
- timeout = 1
-
-In `/path/to/helper.sh`:
-
-.. code-block:: bash
-
- #!/bin/bash
- pass show path/to/password | head -n 1
-
-CLI
-===
-
-Objects and actions
--------------------
-
-The ``gitlab`` command expects two mandatory arguments. The first one is the
-type of object that you want to manipulate. The second is the action that you
-want to perform. For example:
-
-.. code-block:: console
-
- $ gitlab project list
-
-Use the ``--help`` option to list the available object types and actions:
-
-.. code-block:: console
-
- $ gitlab --help
- $ gitlab project --help
-
-Some actions require additional parameters. Use the ``--help`` option to
-list mandatory and optional arguments for an action:
-
-.. code-block:: console
-
- $ gitlab project create --help
-
-Optional arguments
-------------------
-
-Use the following optional arguments to change the behavior of ``gitlab``.
-These options must be defined before the mandatory arguments.
-
-``--verbose``, ``-v``
- Outputs detail about retrieved objects. Available for legacy (default)
- output only.
-
-``--config-file``, ``-c``
- Path to a configuration file.
-
-``--gitlab``, ``-g``
- ID of a GitLab server defined in the configuration file.
-
-``--output``, ``-o``
- Output format. Defaults to a custom format. Can also be ``yaml`` or ``json``.
-
- **Notice:**
-
- The `PyYAML package <https://pypi.org/project/PyYAML/>`_ is required to use the yaml output option.
- You need to install it explicitly using ``pip install python-gitlab[yaml]``
-
-``--fields``, ``-f``
- Comma-separated list of fields to display (``yaml`` and ``json`` output
- formats only). If not used, all the object fields are displayed.
-
-Example:
-
-.. code-block:: console
-
- $ gitlab -o yaml -f id,permissions -g elsewhere -c /tmp/gl.cfg project list
-
-Examples
-========
-
- **Notice:**
-
- For a complete list of objects and actions available, see :doc:`/cli-objects`.
-
-List the projects (paginated):
-
-.. code-block:: console
-
- $ gitlab project list
-
-List all the projects:
-
-.. code-block:: console
-
- $ gitlab project list --all
-
-List all projects of a group:
-
-.. code-block:: console
-
- $ gitlab group-project list --all --group-id 1
-
-List all projects of a group and its subgroups:
-
-.. code-block:: console
-
- $ gitlab group-project list --all --include-subgroups true --group-id 1
-
-Limit to 5 items per request, display the 1st page only
-
-.. code-block:: console
-
- $ gitlab project list --page 1 --per-page 5
-
-Get a specific project (id 2):
-
-.. code-block:: console
-
- $ gitlab project get --id 2
-
-Get a specific user by id:
-
-.. code-block:: console
-
- $ gitlab user get --id 3
-
-Create a deploy token for a project:
-
-.. code-block:: console
-
- $ gitlab -v project-deploy-token create --project-id 2 \
- --name bar --username root --expires-at "2021-09-09" --scopes "read_repository"
-
-List deploy tokens for a group:
-
-.. code-block:: console
-
- $ gitlab -v group-deploy-token list --group-id 3
-
-List packages for a project:
-
-.. code-block:: console
-
- $ gitlab -v project-package list --project-id 3
-
-List packages for a group:
-
-.. code-block:: console
-
- $ gitlab -v group-package list --group-id 3
-
-Get a specific project package by id:
-
-.. code-block:: console
-
- $ gitlab -v project-package get --id 1 --project-id 3
-
-Delete a specific project package by id:
-
-.. code-block:: console
-
- $ gitlab -v project-package delete --id 1 --project-id 3
-
-Upload a generic package to a project:
-
-.. code-block:: console
-
- $ gitlab generic-package upload --project-id 1 --package-name hello-world \
- --package-version v1.0.0 --file-name hello.tar.gz --path /path/to/hello.tar.gz
-
-Download a project's generic package:
-
-.. code-block:: console
-
- $ gitlab generic-package download --project-id 1 --package-name hello-world \
- --package-version v1.0.0 --file-name hello.tar.gz > /path/to/hello.tar.gz
-
-Get a list of issues for this project:
-
-.. code-block:: console
-
- $ gitlab project-issue list --project-id 2
-
-Delete a snippet (id 3):
-
-.. code-block:: console
-
- $ gitlab project-snippet delete --id 3 --project-id 2
-
-Update a snippet:
-
-.. code-block:: console
-
- $ gitlab project-snippet update --id 4 --project-id 2 \
- --code "My New Code"
-
-Create a snippet:
-
-.. code-block:: console
-
- $ gitlab project-snippet create --project-id 2
- Impossible to create object (Missing attribute(s): title, file-name, code)
- $ # oops, let's add the attributes:
- $ gitlab project-snippet create --project-id 2 --title "the title" \
- --file-name "the name" --code "the code"
-
-Get a specific project commit by its SHA id:
-
-.. code-block:: console
-
- $ gitlab project-commit get --project-id 2 --id a43290c
-
-Get the signature (e.g. GPG or x509) of a signed commit:
-
-.. code-block:: console
-
- $ gitlab project-commit signature --project-id 2 --id a43290c
-
-Define the status of a commit (as would be done from a CI tool for example):
-
-.. code-block:: console
-
- $ gitlab project-commit-status create --project-id 2 \
- --commit-id a43290c --state success --name ci/jenkins \
- --target-url http://server/build/123 \
- --description "Jenkins build succeeded"
-
-Download the artifacts zip archive of a job:
-
-.. code-block:: console
-
- $ gitlab project-job artifacts --id 10 --project-id 1 > artifacts.zip
-
-Use sudo to act as another user (admin only):
-
-.. code-block:: console
-
- $ gitlab project create --name user_project1 --sudo username
-
-List values are comma-separated:
-
-.. code-block:: console
-
- $ gitlab issue list --labels foo,bar
-
-Reading values from files
--------------------------
-
-You can make ``gitlab`` read values from files instead of providing them on the
-command line. This is handy for values containing new lines for instance:
-
-.. code-block:: console
-
- $ cat > /tmp/description << EOF
- This is the description of my project.
-
- It is obviously the best project around
- EOF
- $ gitlab project create --name SuperProject --description @/tmp/description
-
-Enabling shell autocompletion
-=============================
-
-To get autocompletion, you'll need to install the package with the extra
-"autocompletion":
-
-.. code-block:: console
-
- pip install python_gitlab[autocompletion]
-
-
-Add the appropriate command below to your shell's config file so that it is run on
-startup. You will likely have to restart or re-login for the autocompletion to
-start working.
-
-Bash
-----
-
-.. code-block:: console
-
- eval "$(register-python-argcomplete gitlab)"
-
-tcsh
-----
-
-.. code-block:: console
-
- eval `register-python-argcomplete --shell tcsh gitlab`
-
-fish
-----
-
-.. code-block:: console
-
- register-python-argcomplete --shell fish gitlab | .
-
-Zsh
----
-
-.. warning::
-
- Zsh autocompletion support is broken right now in the argcomplete python
- package. Perhaps it will be fixed in a future release of argcomplete at
- which point the following instructions will enable autocompletion in zsh.
-
-To activate completions for zsh you need to have bashcompinit enabled in zsh:
-
-.. code-block:: console
-
- autoload -U bashcompinit
- bashcompinit
-
-Afterwards you can enable completion for gitlab:
-
-.. code-block:: console
-
- eval "$(register-python-argcomplete gitlab)"
diff --git a/docs/conf.py b/docs/conf.py
deleted file mode 100644
index 9e0ad83..0000000
--- a/docs/conf.py
+++ /dev/null
@@ -1,295 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-#
-# python-gitlab documentation build configuration file, created by
-# sphinx-quickstart on Mon Dec 8 15:17:39 2014.
-#
-# This file is execfile()d with the current directory set to its
-# containing dir.
-#
-# Note that not all possible configuration values are present in this
-# autogenerated file.
-#
-# All configuration values have a default; values that are commented out
-# serve to show the default.
-
-from __future__ import unicode_literals
-
-import os
-import sys
-
-sys.path.append("../")
-sys.path.append(os.path.dirname(__file__))
-import gitlab # noqa: E402. Needed purely for readthedocs' build
-
-on_rtd = os.environ.get("READTHEDOCS", None) == "True"
-
-# If extensions (or modules to document with autodoc) are in another directory,
-# add these directories to sys.path here. If the directory is relative to the
-# documentation root, use os.path.abspath to make it absolute, like shown here.
-sys.path.insert(0, os.path.abspath(".."))
-
-# -- General configuration ------------------------------------------------
-
-# If your documentation needs a minimal Sphinx version, state it here.
-# needs_sphinx = '1.0'
-
-# Add any Sphinx extension module names here, as strings. They can be
-# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
-# ones.
-extensions = [
- "myst_parser",
- "sphinx.ext.autodoc",
- "sphinx.ext.autosummary",
- "ext.docstrings",
- "sphinxcontrib.autoprogram",
-]
-
-# Add any paths that contain templates here, relative to this directory.
-templates_path = ["_templates"]
-
-# The suffix of source filenames.
-source_suffix = {".rst": "restructuredtext", ".md": "markdown"}
-
-# The encoding of source files.
-# source_encoding = 'utf-8-sig'
-
-# The master toctree document.
-master_doc = "index"
-
-# General information about the project.
-project = "python-gitlab"
-copyright = "2013-2018, Gauvain Pocentek, Mika Mäenpää"
-
-# The version info for the project you're documenting, acts as replacement for
-# |version| and |release|, also used in various other places throughout the
-# built documents.
-#
-# The short X.Y version.
-version = gitlab.__version__
-# The full version, including alpha/beta/rc tags.
-release = version
-
-# The language for content autogenerated by Sphinx. Refer to documentation
-# for a list of supported languages.
-# language = None
-
-# There are two options for replacing |today|: either, you set today to some
-# non-false value, then it is used:
-# today = ''
-# Else, today_fmt is used as the format for a strftime call.
-# today_fmt = '%B %d, %Y'
-
-# List of patterns, relative to source directory, that match files and
-# directories to ignore when looking for source files.
-exclude_patterns = ["_build"]
-
-# The reST default role (used for this markup: `text`) to use for all
-# documents.
-# default_role = None
-
-# If true, '()' will be appended to :func: etc. cross-reference text.
-# add_function_parentheses = True
-
-# If true, the current module name will be prepended to all description
-# unit titles (such as .. function::).
-# add_module_names = True
-
-# If true, sectionauthor and moduleauthor directives will be shown in the
-# output. They are ignored by default.
-# show_authors = False
-
-# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = "sphinx"
-
-# A list of ignored prefixes for module index sorting.
-# modindex_common_prefix = []
-
-# If true, keep warnings as "system message" paragraphs in the built documents.
-# keep_warnings = False
-
-
-# -- Options for HTML output ----------------------------------------------
-
-# The theme to use for HTML and HTML Help pages. See the documentation for
-# a list of builtin themes.
-html_theme = "default"
-if not on_rtd: # only import and set the theme if we're building docs locally
- try:
- import sphinx_rtd_theme
-
- html_theme = "sphinx_rtd_theme"
- html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
- except ImportError: # Theme not found, use default
- pass
-
-# Theme options are theme-specific and customize the look and feel of a theme
-# further. For a list of options available for each theme, see the
-# documentation.
-# html_theme_options = {}
-
-# Add any paths that contain custom themes here, relative to this directory.
-# html_theme_path = []
-
-# The name for this set of Sphinx documents. If None, it defaults to
-# "<project> v<release> documentation".
-# html_title = None
-
-# A shorter title for the navigation bar. Default is the same as html_title.
-# html_short_title = None
-
-# The name of an image file (relative to this directory) to place at the top
-# of the sidebar.
-# html_logo = None
-
-# The name of an image file (within the static path) to use as favicon of the
-# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
-# pixels large.
-# html_favicon = None
-
-# Add any paths that contain custom static files (such as style sheets) here,
-# relative to this directory. They are copied after the builtin static files,
-# so a file named "default.css" will overwrite the builtin "default.css".
-# html_static_path = ['_static']
-
-# Add any extra paths that contain custom files (such as robots.txt or
-# .htaccess) here, relative to this directory. These files are copied
-# directly to the root of the documentation.
-# html_extra_path = []
-
-# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
-# using the given strftime format.
-# html_last_updated_fmt = '%b %d, %Y'
-
-# If true, SmartyPants will be used to convert quotes and dashes to
-# typographically correct entities.
-# html_use_smartypants = True
-
-# Custom sidebar templates, maps document names to template names.
-# html_sidebars = {}
-
-# Additional templates that should be rendered to pages, maps page names to
-# template names.
-# html_additional_pages = {}
-
-# If false, no module index is generated.
-# html_domain_indices = True
-
-# If false, no index is generated.
-# html_use_index = True
-
-# If true, the index is split into individual pages for each letter.
-# html_split_index = False
-
-# If true, links to the reST sources are added to the pages.
-# html_show_sourcelink = True
-
-# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-# html_show_sphinx = True
-
-# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-# html_show_copyright = True
-
-# If true, an OpenSearch description file will be output, and all pages will
-# contain a <link> tag referring to it. The value of this option must be the
-# base URL from which the finished HTML is served.
-# html_use_opensearch = ''
-
-# This is the file name suffix for HTML files (e.g. ".xhtml").
-# html_file_suffix = None
-
-# Output file base name for HTML help builder.
-htmlhelp_basename = "python-gitlabdoc"
-
-
-# -- Options for LaTeX output ---------------------------------------------
-
-latex_elements = {
- # The paper size ('letterpaper' or 'a4paper').
- # 'papersize': 'letterpaper',
- # The font size ('10pt', '11pt' or '12pt').
- # 'pointsize': '10pt',
- # Additional stuff for the LaTeX preamble.
- # 'preamble': '',
-}
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title,
-# author, documentclass [howto, manual, or own class]).
-latex_documents = [
- (
- "index",
- "python-gitlab.tex",
- "python-gitlab Documentation",
- "Gauvain Pocentek, Mika Mäenpää",
- "manual",
- )
-]
-
-# The name of an image file (relative to this directory) to place at the top of
-# the title page.
-# latex_logo = None
-
-# For "manual" documents, if this is true, then toplevel headings are parts,
-# not chapters.
-# latex_use_parts = False
-
-# If true, show page references after internal links.
-# latex_show_pagerefs = False
-
-# If true, show URL addresses after external links.
-# latex_show_urls = False
-
-# Documents to append as an appendix to all manuals.
-# latex_appendices = []
-
-# If false, no module index is generated.
-# latex_domain_indices = True
-
-
-# -- Options for manual page output ---------------------------------------
-
-# One entry per manual page. List of tuples
-# (source start file, name, description, authors, manual section).
-man_pages = [
- (
- "index",
- "python-gitlab",
- "python-gitlab Documentation",
- ["Gauvain Pocentek, Mika Mäenpää"],
- 1,
- )
-]
-
-# If true, show URL addresses after external links.
-# man_show_urls = False
-
-
-# -- Options for Texinfo output -------------------------------------------
-
-# Grouping the document tree into Texinfo files. List of tuples
-# (source start file, target name, title, author,
-# dir menu entry, description, category)
-texinfo_documents = [
- (
- "index",
- "python-gitlab",
- "python-gitlab Documentation",
- "Gauvain Pocentek, Mika Mäenpää",
- "python-gitlab",
- "One line description of project.",
- "Miscellaneous",
- )
-]
-
-# Documents to append as an appendix to all manuals.
-# texinfo_appendices = []
-
-# If false, no module index is generated.
-# texinfo_domain_indices = True
-
-# How to display URL addresses: 'footnote', 'no', or 'inline'.
-# texinfo_show_urls = 'footnote'
-
-# If true, do not generate a @detailmenu in the "Top" node's menu.
-# texinfo_no_detailmenu = False
diff --git a/docs/ext/__init__.py b/docs/ext/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/docs/ext/__init__.py
+++ /dev/null
diff --git a/docs/ext/docstrings.py b/docs/ext/docstrings.py
deleted file mode 100644
index fc1c10b..0000000
--- a/docs/ext/docstrings.py
+++ /dev/null
@@ -1,56 +0,0 @@
-import inspect
-import os
-
-import jinja2
-import sphinx
-import sphinx.ext.napoleon as napoleon
-from sphinx.ext.napoleon.docstring import GoogleDocstring
-
-
-def classref(value, short=True):
- return value
-
- if not inspect.isclass(value):
- return ":class:%s" % value
- tilde = "~" if short else ""
- return ":class:`%sgitlab.objects.%s`" % (tilde, value.__name__)
-
-
-def setup(app):
- app.connect("autodoc-process-docstring", _process_docstring)
- app.connect("autodoc-skip-member", napoleon._skip_member)
-
- conf = napoleon.Config._config_values
-
- for name, (default, rebuild) in conf.items():
- app.add_config_value(name, default, rebuild)
- return {"version": sphinx.__display_version__, "parallel_read_safe": True}
-
-
-def _process_docstring(app, what, name, obj, options, lines):
- result_lines = lines
- docstring = GitlabDocstring(result_lines, app.config, app, what, name, obj, options)
- result_lines = docstring.lines()
- lines[:] = result_lines[:]
-
-
-class GitlabDocstring(GoogleDocstring):
- def _build_doc(self, tmpl, **kwargs):
- env = jinja2.Environment(
- loader=jinja2.FileSystemLoader(os.path.dirname(__file__)), trim_blocks=False
- )
- env.filters["classref"] = classref
- template = env.get_template(tmpl)
- output = template.render(**kwargs)
-
- return output.split("\n")
-
- def __init__(
- self, docstring, config=None, app=None, what="", name="", obj=None, options=None
- ):
- super(GitlabDocstring, self).__init__(
- docstring, config, app, what, name, obj, options
- )
-
- if name.startswith("gitlab.v4.objects") and name.endswith("Manager"):
- self._parsed_lines.extend(self._build_doc("manager_tmpl.j2", cls=self._obj))
diff --git a/docs/ext/manager_tmpl.j2 b/docs/ext/manager_tmpl.j2
deleted file mode 100644
index 6e71c0c..0000000
--- a/docs/ext/manager_tmpl.j2
+++ /dev/null
@@ -1,38 +0,0 @@
-{% if cls._list_filters %}
-**Object listing filters**
-{% for item in cls._list_filters %}
-- ``{{ item }}``
-{% endfor %}
-{% endif %}
-
-{% if cls._create_attrs %}
-**Object Creation**
-{% if cls._create_attrs[0] %}
-Mandatory attributes:
-{% for item in cls._create_attrs[0] %}
-- ``{{ item }}``
-{% endfor %}
-{% endif %}
-{% if cls._create_attrs[1] %}
-Optional attributes:
-{% for item in cls._create_attrs[1] %}
-- ``{{ item }}``
-{% endfor %}
-{% endif %}
-{% endif %}
-
-{% if cls._update_attrs %}
-**Object update**
-{% if cls._update_attrs[0] %}
-Mandatory attributes for object update:
-{% for item in cls._update_attrs[0] %}
-- ``{{ item }}``
-{% endfor %}
-{% endif %}
-{% if cls._update_attrs[1] %}
-Optional attributes for object update:
-{% for item in cls._update_attrs[1] %}
-- ``{{ item }}``
-{% endfor %}
-{% endif %}
-{% endif %}
diff --git a/docs/faq.rst b/docs/faq.rst
deleted file mode 100644
index 0f914ed..0000000
--- a/docs/faq.rst
+++ /dev/null
@@ -1,38 +0,0 @@
-###
-FAQ
-###
-
-I cannot edit the merge request / issue I've just retrieved
- It is likely that you used a ``MergeRequest``, ``GroupMergeRequest``,
- ``Issue`` or ``GroupIssue`` object. These objects cannot be edited. But you
- can create a new ``ProjectMergeRequest`` or ``ProjectIssue`` object to
- apply changes. For example::
-
- issue = gl.issues.list()[0]
- project = gl.projects.get(issue.project_id, lazy=True)
- editable_issue = project.issues.get(issue.iid, lazy=True)
- # you can now edit the object
-
- See the :ref:`merge requests example <merge_requests_examples>` and the
- :ref:`issues examples <issues_examples>`.
-
-How can I clone the repository of a project?
- python-gitlab doesn't provide an API to clone a project. You have to use a
- git library or call the ``git`` command.
-
- The git URI is exposed in the ``ssh_url_to_repo`` attribute of ``Project``
- objects.
-
- Example::
-
- import subprocess
-
- project = gl.projects.create(data) # or gl.projects.get(project_id)
- print(project.attributes) # displays all the attributes
- git_url = project.ssh_url_to_repo
- subprocess.call(['git', 'clone', git_url])
-
-I get an ``AttributeError`` when accessing attributes after ``save()`` or ``refresh()``.
- You are most likely trying to access an attribute that was not returned
- by the server on the second request. Please look at the documentation in
- :ref:`object_attributes` to see how to avoid this.
diff --git a/docs/gl_objects/access_requests.rst b/docs/gl_objects/access_requests.rst
deleted file mode 100644
index 467c3e5..0000000
--- a/docs/gl_objects/access_requests.rst
+++ /dev/null
@@ -1,53 +0,0 @@
-###############
-Access requests
-###############
-
-Users can request access to groups and projects.
-
-When access is granted the user should be given a numerical access level. The
-following constants are provided to represent the access levels:
-
-* ``gitlab.GUEST_ACCESS``: ``10``
-* ``gitlab.REPORTER_ACCESS``: ``20``
-* ``gitlab.DEVELOPER_ACCESS``: ``30``
-* ``gitlab.MAINTAINER_ACCESS``: ``40``
-* ``gitlab.OWNER_ACCESS``: ``50``
-
-References
-----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectAccessRequest`
- + :class:`gitlab.v4.objects.ProjectAccessRequestManager`
- + :attr:`gitlab.v4.objects.Project.accessrequests`
- + :class:`gitlab.v4.objects.GroupAccessRequest`
- + :class:`gitlab.v4.objects.GroupAccessRequestManager`
- + :attr:`gitlab.v4.objects.Group.accessrequests`
-
-* GitLab API: https://docs.gitlab.com/ce/api/access_requests.html
-
-Examples
---------
-
-List access requests from projects and groups::
-
- p_ars = project.accessrequests.list()
- g_ars = group.accessrequests.list()
-
-Create an access request::
-
- p_ar = project.accessrequests.create()
- g_ar = group.accessrequests.create()
-
-Approve an access request::
-
- ar.approve() # defaults to DEVELOPER level
- ar.approve(access_level=gitlab.MAINTAINER_ACCESS) # explicitly set access level
-
-Deny (delete) an access request::
-
- project.accessrequests.delete(user_id)
- group.accessrequests.delete(user_id)
- # or
- ar.delete()
diff --git a/docs/gl_objects/appearance.rst b/docs/gl_objects/appearance.rst
deleted file mode 100644
index 0c05268..0000000
--- a/docs/gl_objects/appearance.rst
+++ /dev/null
@@ -1,26 +0,0 @@
-##########
-Appearance
-##########
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ApplicationAppearance`
- + :class:`gitlab.v4.objects.ApplicationAppearanceManager`
- + :attr:`gitlab.Gitlab.appearance`
-
-* GitLab API: https://docs.gitlab.com/ce/api/appearance.html
-
-Examples
---------
-
-Get the appearance::
-
- appearance = gl.appearance.get()
-
-Update the appearance::
-
- appearance.title = "Test"
- appearance.save()
diff --git a/docs/gl_objects/applications.rst b/docs/gl_objects/applications.rst
deleted file mode 100644
index 146b6e8..0000000
--- a/docs/gl_objects/applications.rst
+++ /dev/null
@@ -1,31 +0,0 @@
-############
-Applications
-############
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.Applications`
- + :class:`gitlab.v4.objects.ApplicationManager`
- + :attr:`gitlab.Gitlab.applications`
-
-* GitLab API: https://docs.gitlab.com/ce/api/applications.html
-
-Examples
---------
-
-List all OAuth applications::
-
- applications = gl.applications.list()
-
-Create an application::
-
- gl.applications.create({'name': 'your_app', 'redirect_uri': 'http://application.url', 'scopes': ['api']})
-
-Delete an applications::
-
- gl.applications.delete(app_id)
- # or
- application.delete()
diff --git a/docs/gl_objects/badges.rst b/docs/gl_objects/badges.rst
deleted file mode 100644
index 2a26bb3..0000000
--- a/docs/gl_objects/badges.rst
+++ /dev/null
@@ -1,52 +0,0 @@
-######
-Badges
-######
-
-Badges can be associated with groups and projects.
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.GroupBadge`
- + :class:`gitlab.v4.objects.GroupBadgeManager`
- + :attr:`gitlab.v4.objects.Group.badges`
- + :class:`gitlab.v4.objects.ProjectBadge`
- + :class:`gitlab.v4.objects.ProjectBadgeManager`
- + :attr:`gitlab.v4.objects.Project.badges`
-
-* GitLab API:
-
- + https://docs.gitlab.com/ce/api/group_badges.html
- + https://docs.gitlab.com/ce/api/project_badges.html
-
-Examples
---------
-
-List badges::
-
- badges = group_or_project.badges.list()
-
-Get a badge::
-
- badge = group_or_project.badges.get(badge_id)
-
-Create a badge::
-
- badge = group_or_project.badges.create({'link_url': link, 'image_url': image_link})
-
-Update a badge::
-
- badge.image_link = new_link
- badge.save()
-
-Delete a badge::
-
- badge.delete()
-
-Render a badge (preview the generate URLs)::
-
- output = group_or_project.badges.render(link, image_link)
- print(output['rendered_link_url'])
- print(output['rendered_image_url'])
diff --git a/docs/gl_objects/boards.rst b/docs/gl_objects/boards.rst
deleted file mode 100644
index 3bdbb51..0000000
--- a/docs/gl_objects/boards.rst
+++ /dev/null
@@ -1,104 +0,0 @@
-############
-Issue boards
-############
-
-Boards
-======
-
-Boards are a visual representation of existing issues for a project or a group.
-Issues can be moved from one list to the other to track progress and help with
-priorities.
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectBoard`
- + :class:`gitlab.v4.objects.ProjectBoardManager`
- + :attr:`gitlab.v4.objects.Project.boards`
- + :class:`gitlab.v4.objects.GroupBoard`
- + :class:`gitlab.v4.objects.GroupBoardManager`
- + :attr:`gitlab.v4.objects.Group.boards`
-
-* GitLab API:
-
- + https://docs.gitlab.com/ce/api/boards.html
- + https://docs.gitlab.com/ce/api/group_boards.html
-
-Examples
---------
-
-Get the list of existing boards for a project or a group::
-
- # item is a Project or a Group
- boards = project_or_group.boards.list()
-
-Get a single board for a project or a group::
-
- board = project_or_group.boards.get(board_id)
-
-Create a board::
-
- board = project_or_group.boards.create({'name': 'new-board'})
-
-.. note:: Board creation is not supported in the GitLab CE edition.
-
-Delete a board::
-
- board.delete()
- # or
- project_or_group.boards.delete(board_id)
-
-.. note:: Board deletion is not supported in the GitLab CE edition.
-
-Board lists
-===========
-
-Boards are made of lists of issues. Each list is associated to a label, and
-issues tagged with this label automatically belong to the list.
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectBoardList`
- + :class:`gitlab.v4.objects.ProjectBoardListManager`
- + :attr:`gitlab.v4.objects.ProjectBoard.lists`
- + :class:`gitlab.v4.objects.GroupBoardList`
- + :class:`gitlab.v4.objects.GroupBoardListManager`
- + :attr:`gitlab.v4.objects.GroupBoard.lists`
-
-* GitLab API:
-
- + https://docs.gitlab.com/ce/api/boards.html
- + https://docs.gitlab.com/ce/api/group_boards.html
-
-Examples
---------
-
-List the issue lists for a board::
-
- b_lists = board.lists.list()
-
-Get a single list::
-
- b_list = board.lists.get(list_id)
-
-Create a new list::
-
- # First get a ProjectLabel
- label = get_or_create_label()
- # Then use its ID to create the new board list
- b_list = board.lists.create({'label_id': label.id})
-
-Change a list position. The first list is at position 0. Moving a list will
-set it at the given position and move the following lists up a position::
-
- b_list.position = 2
- b_list.save()
-
-Delete a list::
-
- b_list.delete()
diff --git a/docs/gl_objects/branches.rst b/docs/gl_objects/branches.rst
deleted file mode 100644
index aeba8ea..0000000
--- a/docs/gl_objects/branches.rst
+++ /dev/null
@@ -1,42 +0,0 @@
-########
-Branches
-########
-
-References
-----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectBranch`
- + :class:`gitlab.v4.objects.ProjectBranchManager`
- + :attr:`gitlab.v4.objects.Project.branches`
-
-* GitLab API: https://docs.gitlab.com/ce/api/branches.html
-
-Examples
---------
-
-Get the list of branches for a repository::
-
- branches = project.branches.list()
-
-Get a single repository branch::
-
- branch = project.branches.get('master')
-
-Create a repository branch::
-
- branch = project.branches.create({'branch': 'feature1',
- 'ref': 'master'})
-
-Delete a repository branch::
-
- project.branches.delete('feature1')
- # or
- branch.delete()
-
-Delete the merged branches for a project::
-
- project.delete_merged_branches()
-
-To manage protected branches, see :doc:`/gl_objects/protected_branches`.
diff --git a/docs/gl_objects/clusters.rst b/docs/gl_objects/clusters.rst
deleted file mode 100644
index 96edd82..0000000
--- a/docs/gl_objects/clusters.rst
+++ /dev/null
@@ -1,82 +0,0 @@
-############
-Clusters
-############
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectCluster`
- + :class:`gitlab.v4.objects.ProjectClusterManager`
- + :attr:`gitlab.v4.objects.Project.clusters`
- + :class:`gitlab.v4.objects.GroupCluster`
- + :class:`gitlab.v4.objects.GroupClusterManager`
- + :attr:`gitlab.v4.objects.Group.clusters`
-
-* GitLab API: https://docs.gitlab.com/ee/api/project_clusters.html
-* GitLab API: https://docs.gitlab.com/ee/api/group_clusters.html
-
-Examples
---------
-
-List clusters for a project::
-
- clusters = project.clusters.list()
-
-Create an cluster for a project::
-
- cluster = project.clusters.create(
- {
- "name": "cluster1",
- "platform_kubernetes_attributes": {
- "api_url": "http://url",
- "token": "tokenval",
- },
- })
-
-Retrieve a specific cluster for a project::
-
- cluster = project.clusters.get(cluster_id)
-
-Update an cluster for a project::
-
- cluster.platform_kubernetes_attributes = {"api_url": "http://newurl"}
- cluster.save()
-
-Delete an cluster for a project::
-
- cluster = project.clusters.delete(cluster_id)
- # or
- cluster.delete()
-
-
-List clusters for a group::
-
- clusters = group.clusters.list()
-
-Create an cluster for a group::
-
- cluster = group.clusters.create(
- {
- "name": "cluster1",
- "platform_kubernetes_attributes": {
- "api_url": "http://url",
- "token": "tokenval",
- },
- })
-
-Retrieve a specific cluster for a group::
-
- cluster = group.clusters.get(cluster_id)
-
-Update an cluster for a group::
-
- cluster.platform_kubernetes_attributes = {"api_url": "http://newurl"}
- cluster.save()
-
-Delete an cluster for a group::
-
- cluster = group.clusters.delete(cluster_id)
- # or
- cluster.delete()
diff --git a/docs/gl_objects/commits.rst b/docs/gl_objects/commits.rst
deleted file mode 100644
index a1d878c..0000000
--- a/docs/gl_objects/commits.rst
+++ /dev/null
@@ -1,147 +0,0 @@
-#######
-Commits
-#######
-
-Commits
-=======
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectCommit`
- + :class:`gitlab.v4.objects.ProjectCommitManager`
- + :attr:`gitlab.v4.objects.Project.commits`
-
-Examples
---------
-
-List the commits for a project::
-
- commits = project.commits.list()
-
-You can use the ``ref_name``, ``since`` and ``until`` filters to limit the
-results::
-
- commits = project.commits.list(ref_name='my_branch')
- commits = project.commits.list(since='2016-01-01T00:00:00Z')
-
-.. note::
-
- The available ``all`` listing argument conflicts with the python-gitlab
- argument. Use ``query_parameters`` to avoid the conflict::
-
- commits = project.commits.list(all=True,
- query_parameters={'ref_name': 'my_branch'})
-
-Create a commit::
-
- # See https://docs.gitlab.com/ce/api/commits.html#create-a-commit-with-multiple-files-and-actions
- # for actions detail
- data = {
- 'branch': 'master',
- 'commit_message': 'blah blah blah',
- 'actions': [
- {
- 'action': 'create',
- 'file_path': 'README.rst',
- 'content': open('path/to/file.rst').read(),
- },
- {
- # Binary files need to be base64 encoded
- 'action': 'create',
- 'file_path': 'logo.png',
- 'content': base64.b64encode(open('logo.png').read()),
- 'encoding': 'base64',
- }
- ]
- }
-
- commit = project.commits.create(data)
-
-Get a commit detail::
-
- commit = project.commits.get('e3d5a71b')
-
-Get the diff for a commit::
-
- diff = commit.diff()
-
-Cherry-pick a commit into another branch::
-
- commit.cherry_pick(branch='target_branch')
-
-Revert a commit on a given branch::
-
- commit.revert(branch='target_branch')
-
-Get the references the commit has been pushed to (branches and tags)::
-
- commit.refs() # all references
- commit.refs('tag') # only tags
- commit.refs('branch') # only branches
-
-Get the signature of the commit (if the commit was signed, e.g. with GPG or x509)::
-
- commit.signature()
-
-List the merge requests related to a commit::
-
- commit.merge_requests()
-
-Commit comments
-===============
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectCommitComment`
- + :class:`gitlab.v4.objects.ProjectCommitCommentManager`
- + :attr:`gitlab.v4.objects.ProjectCommit.comments`
-
-* GitLab API: https://docs.gitlab.com/ce/api/commits.html
-
-Examples
---------
-
-Get the comments for a commit::
-
- comments = commit.comments.list()
-
-Add a comment on a commit::
-
- # Global comment
- commit = commit.comments.create({'note': 'This is a nice comment'})
- # Comment on a line in a file (on the new version of the file)
- commit = commit.comments.create({'note': 'This is another comment',
- 'line': 12,
- 'line_type': 'new',
- 'path': 'README.rst'})
-
-Commit status
-=============
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectCommitStatus`
- + :class:`gitlab.v4.objects.ProjectCommitStatusManager`
- + :attr:`gitlab.v4.objects.ProjectCommit.statuses`
-
-* GitLab API: https://docs.gitlab.com/ce/api/commits.html
-
-Examples
---------
-
-List the statuses for a commit::
-
- statuses = commit.statuses.list()
-
-Change the status of a commit::
-
- commit.statuses.create({'state': 'success'})
diff --git a/docs/gl_objects/deploy_keys.rst b/docs/gl_objects/deploy_keys.rst
deleted file mode 100644
index 31e31a9..0000000
--- a/docs/gl_objects/deploy_keys.rst
+++ /dev/null
@@ -1,70 +0,0 @@
-###########
-Deploy keys
-###########
-
-Deploy keys
-===========
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.DeployKey`
- + :class:`gitlab.v4.objects.DeployKeyManager`
- + :attr:`gitlab.Gitlab.deploykeys`
-
-* GitLab API: https://docs.gitlab.com/ce/api/deploy_keys.html
-
-Examples
---------
-
-List the deploy keys::
-
- keys = gl.deploykeys.list()
-
-Deploy keys for projects
-========================
-
-Deploy keys can be managed on a per-project basis.
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectKey`
- + :class:`gitlab.v4.objects.ProjectKeyManager`
- + :attr:`gitlab.v4.objects.Project.keys`
-
-* GitLab API: https://docs.gitlab.com/ce/api/deploy_keys.html
-
-Examples
---------
-
-List keys for a project::
-
- keys = project.keys.list()
-
-Get a single deploy key::
-
- key = project.keys.get(key_id)
-
-Create a deploy key for a project::
-
- key = project.keys.create({'title': 'jenkins key',
- 'key': open('/home/me/.ssh/id_rsa.pub').read()})
-
-Delete a deploy key for a project::
-
- key = project.keys.list(key_id)
- # or
- key.delete()
-
-Enable a deploy key for a project::
-
- project.keys.enable(key_id)
-
-Disable a deploy key for a project::
-
- project_key.delete()
diff --git a/docs/gl_objects/deploy_tokens.rst b/docs/gl_objects/deploy_tokens.rst
deleted file mode 100644
index 302cb9c..0000000
--- a/docs/gl_objects/deploy_tokens.rst
+++ /dev/null
@@ -1,137 +0,0 @@
-#############
-Deploy tokens
-#############
-
-Deploy tokens allow read-only access to your repository and registry images
-without having a user and a password.
-
-Deploy tokens
-=============
-
-This endpoint requires admin access.
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.DeployToken`
- + :class:`gitlab.v4.objects.DeployTokenManager`
- + :attr:`gitlab.Gitlab.deploytokens`
-
-* GitLab API: https://docs.gitlab.com/ce/api/deploy_tokens.html
-
-Examples
---------
-
-Use the ``list()`` method to list all deploy tokens across the GitLab instance.
-
-::
-
- # List deploy tokens
- deploy_tokens = gl.deploytokens.list()
-
-Project deploy tokens
-=====================
-
-This endpoint requires project maintainer access or higher.
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectDeployToken`
- + :class:`gitlab.v4.objects.ProjectDeployTokenManager`
- + :attr:`gitlab.v4.objects.Project.deploytokens`
-
-* GitLab API: https://docs.gitlab.com/ce/api/deploy_tokens.html#project-deploy-tokens
-
-Examples
---------
-
-List the deploy tokens for a project::
-
- deploy_tokens = project.deploytokens.list()
-
-Create a new deploy token to access registry images of a project:
-
-In addition to required parameters ``name`` and ``scopes``, this method accepts
-the following parameters:
-
-* ``expires_at`` Expiration date of the deploy token. Does not expire if no value is provided.
-* ``username`` Username for deploy token. Default is ``gitlab+deploy-token-{n}``
-
-
-::
-
- deploy_token = project.deploytokens.create({'name': 'token1', 'scopes': ['read_registry'], 'username':'', 'expires_at':''})
- # show its id
- print(deploy_token.id)
- # show the token value. Make sure you save it, you won't be able to access it again.
- print(deploy_token.token)
-
-.. warning::
-
- With GitLab 12.9, even though ``username`` and ``expires_at`` are not required, they always have to be passed to the API.
- You can set them to empty strings, see: https://gitlab.com/gitlab-org/gitlab/-/issues/211878.
- Also, the ``username``'s value is ignored by the API and will be overridden with ``gitlab+deploy-token-{n}``,
- see: https://gitlab.com/gitlab-org/gitlab/-/issues/211963
- These issues were fixed in GitLab 12.10.
-
-Remove a deploy token from the project::
-
- deploy_token.delete()
- # or
- project.deploytokens.delete(deploy_token.id)
-
-
-Group deploy tokens
-===================
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.GroupDeployToken`
- + :class:`gitlab.v4.objects.GroupDeployTokenManager`
- + :attr:`gitlab.v4.objects.Group.deploytokens`
-
-* GitLab API: https://docs.gitlab.com/ce/api/deploy_tokens.html#group-deploy-tokens
-
-Examples
---------
-
-List the deploy tokens for a group::
-
- deploy_tokens = group.deploytokens.list()
-
-Create a new deploy token to access all repositories of all projects in a group:
-
-In addition to required parameters ``name`` and ``scopes``, this method accepts
-the following parameters:
-
-* ``expires_at`` Expiration date of the deploy token. Does not expire if no value is provided.
-* ``username`` Username for deploy token. Default is ``gitlab+deploy-token-{n}``
-
-::
-
- deploy_token = group.deploytokens.create({'name': 'token1', 'scopes': ['read_repository'], 'username':'', 'expires_at':''})
- # show its id
- print(deploy_token.id)
-
-.. warning::
-
- With GitLab 12.9, even though ``username`` and ``expires_at`` are not required, they always have to be passed to the API.
- You can set them to empty strings, see: https://gitlab.com/gitlab-org/gitlab/-/issues/211878.
- Also, the ``username``'s value is ignored by the API and will be overridden with ``gitlab+deploy-token-{n}``,
- see: https://gitlab.com/gitlab-org/gitlab/-/issues/211963
- These issues were fixed in GitLab 12.10.
-
-Remove a deploy token from the group::
-
- deploy_token.delete()
- # or
- group.deploytokens.delete(deploy_token.id)
-
diff --git a/docs/gl_objects/deployments.rst b/docs/gl_objects/deployments.rst
deleted file mode 100644
index 945ad41..0000000
--- a/docs/gl_objects/deployments.rst
+++ /dev/null
@@ -1,63 +0,0 @@
-###########
-Deployments
-###########
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectDeployment`
- + :class:`gitlab.v4.objects.ProjectDeploymentManager`
- + :attr:`gitlab.v4.objects.Project.deployments`
-
-* GitLab API: https://docs.gitlab.com/ce/api/deployments.html
-
-Examples
---------
-
-List deployments for a project::
-
- deployments = project.deployments.list()
-
-Get a single deployment::
-
- deployment = project.deployments.get(deployment_id)
-
-Create a new deployment::
-
- deployment = project.deployments.create({
- "environment": "Test",
- "sha": "1agf4gs",
- "ref": "master",
- "tag": False,
- "status": "created",
- })
-
-Update a deployment::
-
- deployment = project.deployments.get(42)
- deployment.status = "failed"
- deployment.save()
-
-Merge requests associated with a deployment
-===========================================
-
-Reference
-----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectDeploymentMergeRequest`
- + :class:`gitlab.v4.objects.ProjectDeploymentMergeRequestManager`
- + :attr:`gitlab.v4.objects.ProjectDeployment.mergerequests`
-
-* GitLab API: https://docs.gitlab.com/ee/api/deployments.html#list-of-merge-requests-associated-with-a-deployment
-
-Examples
---------
-
-List the merge requests associated with a deployment::
-
- deployment = project.deployments.get(42, lazy=True)
- mrs = deployment.mergerequests.list()
diff --git a/docs/gl_objects/discussions.rst b/docs/gl_objects/discussions.rst
deleted file mode 100644
index 444d883..0000000
--- a/docs/gl_objects/discussions.rst
+++ /dev/null
@@ -1,107 +0,0 @@
-###########
-Discussions
-###########
-
-Discussions organize the notes in threads. See the :ref:`project-notes` chapter
-for more information about notes.
-
-Discussions are available for project issues, merge requests, snippets and
-commits.
-
-Reference
-=========
-
-* v4 API:
-
- Issues:
-
- + :class:`gitlab.v4.objects.ProjectIssueDiscussion`
- + :class:`gitlab.v4.objects.ProjectIssueDiscussionManager`
- + :class:`gitlab.v4.objects.ProjectIssueDiscussionNote`
- + :class:`gitlab.v4.objects.ProjectIssueDiscussionNoteManager`
- + :attr:`gitlab.v4.objects.ProjectIssue.notes`
-
- MergeRequests:
-
- + :class:`gitlab.v4.objects.ProjectMergeRequestDiscussion`
- + :class:`gitlab.v4.objects.ProjectMergeRequestDiscussionManager`
- + :class:`gitlab.v4.objects.ProjectMergeRequestDiscussionNote`
- + :class:`gitlab.v4.objects.ProjectMergeRequestDiscussionNoteManager`
- + :attr:`gitlab.v4.objects.ProjectMergeRequest.notes`
-
- Snippets:
-
- + :class:`gitlab.v4.objects.ProjectSnippetDiscussion`
- + :class:`gitlab.v4.objects.ProjectSnippetDiscussionManager`
- + :class:`gitlab.v4.objects.ProjectSnippetDiscussionNote`
- + :class:`gitlab.v4.objects.ProjectSnippetDiscussionNoteManager`
- + :attr:`gitlab.v4.objects.ProjectSnippet.notes`
-
-* GitLab API: https://docs.gitlab.com/ce/api/discussions.html
-
-Examples
-========
-
-List the discussions for a resource (issue, merge request, snippet or commit)::
-
- discussions = resource.discussions.list()
-
-Get a single discussion::
-
- discussion = resource.discussions.get(discussion_id)
-
-You can access the individual notes in the discussion through the ``notes``
-attribute. It holds a list of notes in chronological order::
-
- # ``resource.notes`` is a DiscussionNoteManager, so we need to get the
- # object notes using ``attributes``
- for note in discussion.attributes['notes']:
- print(note['body'])
-
-.. note::
-
- The notes are dicts, not objects.
-
-You can add notes to existing discussions::
-
- new_note = discussion.notes.create({'body': 'Episode IV: A new note'})
-
-You can get and update a single note using the ``*DiscussionNote`` resources::
-
- discussion = resource.discussions.get(discussion_id)
- # Get the latest note's id
- note_id = discussion.attributes['note'][-1]['id']
- last_note = discussion.notes.get(note_id)
- last_note.body = 'Updated comment'
- last_note.save()
-
-Create a new discussion::
-
- discussion = resource.discussions.create({'body': 'First comment of discussion'})
-
-You can comment on merge requests and commit diffs. Provide the ``position``
-dict to define where the comment should appear in the diff::
-
- mr_diff = mr.diffs.get(diff_id)
- mr.discussions.create({'body': 'Note content',
- 'position': {
- 'base_sha': mr_diff.base_commit_sha,
- 'start_sha': mr_diff.start_commit_sha,
- 'head_sha': mr_diff.head_commit_sha,
- 'position_type': 'text',
- 'new_line': 1,
- 'old_path': 'README.rst',
- 'new_path': 'README.rst'}
- })
-
-Resolve / unresolve a merge request discussion::
-
- mr_d = mr.discussions.get(d_id)
- mr_d.resolved = True # True to resolve, False to unresolve
- mr_d.save()
-
-Delete a comment::
-
- discussions.notes.delete(note_id)
- # or
- note.delete()
diff --git a/docs/gl_objects/emojis.rst b/docs/gl_objects/emojis.rst
deleted file mode 100644
index 179141f..0000000
--- a/docs/gl_objects/emojis.rst
+++ /dev/null
@@ -1,45 +0,0 @@
-############
-Award Emojis
-############
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectIssueAwardEmoji`
- + :class:`gitlab.v4.objects.ProjectIssueNoteAwardEmoji`
- + :class:`gitlab.v4.objects.ProjectMergeRequestAwardEmoji`
- + :class:`gitlab.v4.objects.ProjectMergeRequestNoteAwardEmoji`
- + :class:`gitlab.v4.objects.ProjectSnippetAwardEmoji`
- + :class:`gitlab.v4.objects.ProjectSnippetNoteAwardEmoji`
- + :class:`gitlab.v4.objects.ProjectIssueAwardEmojiManager`
- + :class:`gitlab.v4.objects.ProjectIssueNoteAwardEmojiManager`
- + :class:`gitlab.v4.objects.ProjectMergeRequestAwardEmojiManager`
- + :class:`gitlab.v4.objects.ProjectMergeRequestNoteAwardEmojiManager`
- + :class:`gitlab.v4.objects.ProjectSnippetAwardEmojiManager`
- + :class:`gitlab.v4.objects.ProjectSnippetNoteAwardEmojiManager`
-
-
-* GitLab API: https://docs.gitlab.com/ce/api/award_emoji.html
-
-Examples
---------
-
-List emojis for a resource::
-
- emojis = obj.awardemojis.list()
-
-Get a single emoji::
-
- emoji = obj.awardemojis.get(emoji_id)
-
-Add (create) an emoji::
-
- emoji = obj.awardemojis.create({'name': 'tractor'})
-
-Delete an emoji::
-
- emoji.delete
- # or
- obj.awardemojis.delete(emoji_id)
diff --git a/docs/gl_objects/environments.rst b/docs/gl_objects/environments.rst
deleted file mode 100644
index 6edde12..0000000
--- a/docs/gl_objects/environments.rst
+++ /dev/null
@@ -1,44 +0,0 @@
-############
-Environments
-############
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectEnvironment`
- + :class:`gitlab.v4.objects.ProjectEnvironmentManager`
- + :attr:`gitlab.v4.objects.Project.environments`
-
-* GitLab API: https://docs.gitlab.com/ce/api/environments.html
-
-Examples
---------
-
-List environments for a project::
-
- environments = project.environments.list()
-
-Create an environment for a project::
-
- environment = project.environments.create({'name': 'production'})
-
-Retrieve a specific environment for a project::
-
- environment = project.environments.get(112)
-
-Update an environment for a project::
-
- environment.external_url = 'http://foo.bar.com'
- environment.save()
-
-Delete an environment for a project::
-
- environment = project.environments.delete(environment_id)
- # or
- environment.delete()
-
-Stop an environments::
-
- environment.stop()
diff --git a/docs/gl_objects/epics.rst b/docs/gl_objects/epics.rst
deleted file mode 100644
index 2b1e23e..0000000
--- a/docs/gl_objects/epics.rst
+++ /dev/null
@@ -1,79 +0,0 @@
-#####
-Epics
-#####
-
-Epics
-=====
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.GroupEpic`
- + :class:`gitlab.v4.objects.GroupEpicManager`
- + :attr:`gitlab.Gitlab.Group.epics`
-
-* GitLab API: https://docs.gitlab.com/ee/api/epics.html (EE feature)
-
-Examples
---------
-
-List the epics for a group::
-
- epics = groups.epics.list()
-
-Get a single epic for a group::
-
- epic = group.epics.get(epic_iid)
-
-Create an epic for a group::
-
- epic = group.epics.create({'title': 'My Epic'})
-
-Edit an epic::
-
- epic.title = 'New title'
- epic.labels = ['label1', 'label2']
- epic.save()
-
-Delete an epic::
-
- epic.delete()
-
-Epics issues
-============
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.GroupEpicIssue`
- + :class:`gitlab.v4.objects.GroupEpicIssueManager`
- + :attr:`gitlab.Gitlab.GroupEpic.issues`
-
-* GitLab API: https://docs.gitlab.com/ee/api/epic_issues.html (EE feature)
-
-Examples
---------
-
-List the issues associated with an issue::
-
- ei = epic.issues.list()
-
-Associate an issue with an epic::
-
- # use the issue id, not its iid
- ei = epic.issues.create({'issue_id': 4})
-
-Move an issue in the list::
-
- ei.move_before_id = epic_issue_id_1
- # or
- ei.move_after_id = epic_issue_id_2
- ei.save()
-
-Delete an issue association::
-
- ei.delete()
diff --git a/docs/gl_objects/events.rst b/docs/gl_objects/events.rst
deleted file mode 100644
index 5dc03c7..0000000
--- a/docs/gl_objects/events.rst
+++ /dev/null
@@ -1,83 +0,0 @@
-######
-Events
-######
-
-Events
-======
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.Event`
- + :class:`gitlab.v4.objects.EventManager`
- + :attr:`gitlab.Gitlab.events`
- + :class:`gitlab.v4.objects.ProjectEvent`
- + :class:`gitlab.v4.objects.ProjectEventManager`
- + :attr:`gitlab.v4.objects.Project.events`
- + :class:`gitlab.v4.objects.UserEvent`
- + :class:`gitlab.v4.objects.UserEventManager`
- + :attr:`gitlab.v4.objects.User.events`
-
-* GitLab API: https://docs.gitlab.com/ce/api/events.html
-
-Examples
---------
-
-You can list events for an entire Gitlab instance (admin), users and projects.
-You can filter you events you want to retrieve using the ``action`` and
-``target_type`` attributes. The possible values for these attributes are
-available on `the gitlab documentation
-<https://docs.gitlab.com/ce/api/events.html>`_.
-
-List all the events (paginated)::
-
- events = gl.events.list()
-
-List the issue events on a project::
-
- events = project.events.list(target_type='issue')
-
-List the user events::
-
- events = project.events.list()
-
-Resource state events
-=====================
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectIssueResourceStateEvent`
- + :class:`gitlab.v4.objects.ProjectIssueResourceStateEventManager`
- + :attr:`gitlab.v4.objects.ProjectIssue.resourcestateevents`
- + :class:`gitlab.v4.objects.ProjectMergeRequestResourceStateEvent`
- + :class:`gitlab.v4.objects.ProjectMergeRequestResourceStateEventManager`
- + :attr:`gitlab.v4.objects.ProjectMergeRequest.resourcestateevents`
-
-* GitLab API: https://docs.gitlab.com/ee/api/resource_state_events.html
-
-Examples
---------
-
-You can list and get specific resource state events (via their id) for project issues
-and project merge requests.
-
-List the state events of a project issue (paginated)::
-
- state_events = issue.resourcestateevents.list()
-
-Get a specific state event of a project issue by its id::
-
- state_event = issue.resourcestateevents.get(1)
-
-List the state events of a project merge request (paginated)::
-
- state_events = mr.resourcestateevents.list()
-
-Get a specific state event of a project merge request by its id::
-
- state_event = mr.resourcestateevents.get(1)
diff --git a/docs/gl_objects/features.rst b/docs/gl_objects/features.rst
deleted file mode 100644
index 2344895..0000000
--- a/docs/gl_objects/features.rst
+++ /dev/null
@@ -1,32 +0,0 @@
-##############
-Features flags
-##############
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.Feature`
- + :class:`gitlab.v4.objects.FeatureManager`
- + :attr:`gitlab.Gitlab.features`
-
-* GitLab API: https://docs.gitlab.com/ce/api/features.html
-
-Examples
---------
-
-List features::
-
- features = gl.features.list()
-
-Create or set a feature::
-
- feature = gl.features.set(feature_name, True)
- feature = gl.features.set(feature_name, 30)
- feature = gl.features.set(feature_name, True, user=filipowm)
- feature = gl.features.set(feature_name, 40, group=mygroup)
-
-Delete a feature::
-
- feature.delete()
diff --git a/docs/gl_objects/geo_nodes.rst b/docs/gl_objects/geo_nodes.rst
deleted file mode 100644
index 181ec91..0000000
--- a/docs/gl_objects/geo_nodes.rst
+++ /dev/null
@@ -1,43 +0,0 @@
-#########
-Geo nodes
-#########
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.GeoNode`
- + :class:`gitlab.v4.objects.GeoNodeManager`
- + :attr:`gitlab.Gitlab.geonodes`
-
-* GitLab API: https://docs.gitlab.com/ee/api/geo_nodes.html (EE feature)
-
-Examples
---------
-
-List the geo nodes::
-
- nodes = gl.geonodes.list()
-
-Get the status of all the nodes::
-
- status = gl.geonodes.status()
-
-Get a specific node and its status::
-
- node = gl.geonodes.get(node_id)
- node.status()
-
-Edit a node configuration::
-
- node.url = 'https://secondary.mygitlab.domain'
- node.save()
-
-Delete a node::
-
- node.delete()
-
-List the sync failure on the current node::
-
- failures = gl.geonodes.current_failures()
diff --git a/docs/gl_objects/groups.rst b/docs/gl_objects/groups.rst
deleted file mode 100644
index 549fe53..0000000
--- a/docs/gl_objects/groups.rst
+++ /dev/null
@@ -1,378 +0,0 @@
-######
-Groups
-######
-
-Groups
-======
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.Group`
- + :class:`gitlab.v4.objects.GroupManager`
- + :attr:`gitlab.Gitlab.groups`
-
-* GitLab API: https://docs.gitlab.com/ce/api/groups.html
-
-Examples
---------
-
-List the groups::
-
- groups = gl.groups.list()
-
-Get a group's detail::
-
- group = gl.groups.get(group_id)
-
-List a group's projects::
-
- projects = group.projects.list()
-
-.. note::
-
- ``GroupProject`` objects returned by this API call are very limited, and do
- not provide all the features of ``Project`` objects. If you need to
- manipulate projects, create a new ``Project`` object::
-
- first_group_project = group.projects.list()[0]
- manageable_project = gl.projects.get(first_group_project.id, lazy=True)
-
-You can filter and sort the result using the following parameters:
-
-* ``archived``: limit by archived status
-* ``visibility``: limit by visibility. Allowed values are ``public``,
- ``internal`` and ``private``
-* ``search``: limit to groups matching the given value
-* ``order_by``: sort by criteria. Allowed values are ``id``, ``name``, ``path``,
- ``created_at``, ``updated_at`` and ``last_activity_at``
-* ``sort``: sort order: ``asc`` or ``desc``
-* ``ci_enabled_first``: return CI enabled groups first
-* ``include_subgroups``: include projects in subgroups
-
-Create a group::
-
- group = gl.groups.create({'name': 'group1', 'path': 'group1'})
-
-Create a subgroup under an existing group::
-
- subgroup = gl.groups.create({'name': 'subgroup1', 'path': 'subgroup1', 'parent_id': parent_group_id})
-
-Update a group::
-
- group.description = 'My awesome group'
- group.save()
-
-Set the avatar image for a group::
-
- # the avatar image can be passed as data (content of the file) or as a file
- # object opened in binary mode
- group.avatar = open('path/to/file.png', 'rb')
- group.save()
-
-Remove a group::
-
- gl.groups.delete(group_id)
- # or
- group.delete()
-
-Share/unshare the group with a group::
-
- group.share(group2.id, gitlab.DEVELOPER_ACCESS)
- group.unshare(group2.id)
-
-Import / Export
-===============
-
-You can export groups from gitlab, and re-import them to create new groups.
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.GroupExport`
- + :class:`gitlab.v4.objects.GroupExportManager`
- + :attr:`gitlab.v4.objects.Group.exports`
- + :class:`gitlab.v4.objects.GroupImport`
- + :class:`gitlab.v4.objects.GroupImportManager`
- + :attr:`gitlab.v4.objects.Group.imports`
- + :attr:`gitlab.v4.objects.GroupManager.import_group`
-
-* GitLab API: https://docs.gitlab.com/ce/api/group_import_export.html
-
-Examples
---------
-
-A group export is an asynchronous operation. To retrieve the archive
-generated by GitLab you need to:
-
-#. Create an export using the API
-#. Wait for the export to be done
-#. Download the result
-
-.. warning::
-
- Unlike the Project Export API, GitLab does not provide an export_status
- for Group Exports. It is up to the user to ensure the export is finished.
-
- However, Group Exports only contain metadata, so they are much faster
- than Project Exports.
-
-::
-
- # Create the export
- group = gl.groups.get(my_group)
- export = group.exports.create()
-
- # Wait for the export to finish
- time.sleep(3)
-
- # Download the result
- with open('/tmp/export.tgz', 'wb') as f:
- export.download(streamed=True, action=f.write)
-
-Import the group::
-
- with open('/tmp/export.tgz', 'rb') as f:
- gl.groups.import_group(f, path='imported-group', name="Imported Group")
-
-Subgroups
-=========
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.GroupSubgroup`
- + :class:`gitlab.v4.objects.GroupSubgroupManager`
- + :attr:`gitlab.v4.objects.Group.subgroups`
-
-Examples
---------
-
-List the subgroups for a group::
-
- subgroups = group.subgroups.list()
-
-.. note::
-
- The ``GroupSubgroup`` objects don't expose the same API as the ``Group``
- objects. If you need to manipulate a subgroup as a group, create a new
- ``Group`` object::
-
- real_group = gl.groups.get(subgroup_id, lazy=True)
- real_group.issues.list()
-
-Descendant Groups
-=================
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.GroupDescendantGroup`
- + :class:`gitlab.v4.objects.GroupDescendantGroupManager`
- + :attr:`gitlab.v4.objects.Group.descendant_groups`
-
-Examples
---------
-
-List the descendant groups of a group::
-
- descendant_groups = group.descendant_groups.list()
-
-.. note::
-
- Like the ``GroupSubgroup`` objects described above, ``GroupDescendantGroup``
- objects do not expose the same API as the ``Group`` objects. Create a new
- ``Group`` object instead if needed, as shown in the subgroup example.
-
-Group custom attributes
-=======================
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.GroupCustomAttribute`
- + :class:`gitlab.v4.objects.GroupCustomAttributeManager`
- + :attr:`gitlab.v4.objects.Group.customattributes`
-
-* GitLab API: https://docs.gitlab.com/ce/api/custom_attributes.html
-
-Examples
---------
-
-List custom attributes for a group::
-
- attrs = group.customattributes.list()
-
-Get a custom attribute for a group::
-
- attr = group.customattributes.get(attr_key)
-
-Set (create or update) a custom attribute for a group::
-
- attr = group.customattributes.set(attr_key, attr_value)
-
-Delete a custom attribute for a group::
-
- attr.delete()
- # or
- group.customattributes.delete(attr_key)
-
-Search groups by custom attribute::
-
- group.customattributes.set('role': 'admin')
- gl.groups.list(custom_attributes={'role': 'admin'})
-
-Group members
-=============
-
-The following constants define the supported access levels:
-
-* ``gitlab.GUEST_ACCESS = 10``
-* ``gitlab.REPORTER_ACCESS = 20``
-* ``gitlab.DEVELOPER_ACCESS = 30``
-* ``gitlab.MAINTAINER_ACCESS = 40``
-* ``gitlab.OWNER_ACCESS = 50``
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.GroupMember`
- + :class:`gitlab.v4.objects.GroupMemberManager`
- + :class:`gitlab.v4.objects.GroupMemberAllManager`
- + :class:`gitlab.v4.objects.GroupBillableMember`
- + :class:`gitlab.v4.objects.GroupBillableMemberManager`
- + :attr:`gitlab.v4.objects.Group.members`
- + :attr:`gitlab.v4.objects.Group.members_all`
- + :attr:`gitlab.v4.objects.Group.billable_members`
-
-* GitLab API: https://docs.gitlab.com/ce/api/members.html
-
-Billable group members are only available in GitLab EE.
-
-Examples
---------
-
-List only direct group members::
-
- members = group.members.list()
-
-List the group members recursively (including inherited members through
-ancestor groups)::
-
- members = group.members_all.list(all=True)
-
-Get only direct group member::
-
- members = group.members.get(member_id)
-
-Get a member of a group, including members inherited through ancestor groups::
-
- members = group.members_all.get(member_id)
-
-Add a member to the group::
-
- member = group.members.create({'user_id': user_id,
- 'access_level': gitlab.GUEST_ACCESS})
-
-Update a member (change the access level)::
-
- member.access_level = gitlab.DEVELOPER_ACCESS
- member.save()
-
-Remove a member from the group::
-
- group.members.delete(member_id)
- # or
- member.delete()
-
-List billable members of a group (top-level groups only)::
-
- billable_members = group.billable_members.list()
-
-Remove a billable member from the group::
-
- group.billable_members.delete(member_id)
- # or
- billable_member.delete()
-
-List memberships of a billable member::
-
- billable_member.memberships.list()
-
-LDAP group links
-================
-
-Add an LDAP group link to an existing GitLab group::
-
- group.add_ldap_group_link(ldap_group_cn, gitlab.DEVELOPER_ACCESS, 'ldapmain')
-
-Remove a link::
-
- group.delete_ldap_group_link(ldap_group_cn, 'ldapmain')
-
-Sync the LDAP groups::
-
- group.ldap_sync()
-
-You can use the ``ldapgroups`` manager to list available LDAP groups::
-
- # listing (supports pagination)
- ldap_groups = gl.ldapgroups.list()
-
- # filter using a group name
- ldap_groups = gl.ldapgroups.list(search='foo')
-
- # list the groups for a specific LDAP provider
- ldap_groups = gl.ldapgroups.list(search='foo', provider='ldapmain')
-
-Groups hooks
-============
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.GroupHook`
- + :class:`gitlab.v4.objects.GroupHookManager`
- + :attr:`gitlab.v4.objects.Group.hooks`
-
-* GitLab API: https://docs.gitlab.com/ce/api/groups.html#hooks
-
-Examples
---------
-
-List the group hooks::
-
- hooks = group.hooks.list()
-
-Get a group hook::
-
- hook = group.hooks.get(hook_id)
-
-Create a group hook::
-
- hook = group.hooks.create({'url': 'http://my/action/url', 'push_events': 1})
-
-Update a group hook::
-
- hook.push_events = 0
- hook.save()
-
-Delete a group hook::
-
- group.hooks.delete(hook_id)
- # or
- hook.delete()
diff --git a/docs/gl_objects/issues.rst b/docs/gl_objects/issues.rst
deleted file mode 100644
index dfb1ff7..0000000
--- a/docs/gl_objects/issues.rst
+++ /dev/null
@@ -1,279 +0,0 @@
-.. _issues_examples:
-
-######
-Issues
-######
-
-Reported issues
-===============
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.Issue`
- + :class:`gitlab.v4.objects.IssueManager`
- + :attr:`gitlab.Gitlab.issues`
-
-* GitLab API: https://docs.gitlab.com/ce/api/issues.html
-
-Examples
---------
-
-List the issues::
-
- issues = gl.issues.list()
-
-Use the ``state`` and ``label`` parameters to filter the results. Use the
-``order_by`` and ``sort`` attributes to sort the results::
-
- open_issues = gl.issues.list(state='opened')
- closed_issues = gl.issues.list(state='closed')
- tagged_issues = gl.issues.list(labels=['foo', 'bar'])
-
-.. note::
-
- It is not possible to edit or delete Issue objects. You need to create a
- ProjectIssue object to perform changes::
-
- issue = gl.issues.list()[0]
- project = gl.projects.get(issue.project_id, lazy=True)
- editable_issue = project.issues.get(issue.iid, lazy=True)
- editable_issue.title = updated_title
- editable_issue.save()
-
-Group issues
-============
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.GroupIssue`
- + :class:`gitlab.v4.objects.GroupIssueManager`
- + :attr:`gitlab.v4.objects.Group.issues`
-
-* GitLab API: https://docs.gitlab.com/ce/api/issues.html
-
-Examples
---------
-
-List the group issues::
-
- issues = group.issues.list()
- # Filter using the state, labels and milestone parameters
- issues = group.issues.list(milestone='1.0', state='opened')
- # Order using the order_by and sort parameters
- issues = group.issues.list(order_by='created_at', sort='desc')
-
-.. note::
-
- It is not possible to edit or delete GroupIssue objects. You need to create
- a ProjectIssue object to perform changes::
-
- issue = group.issues.list()[0]
- project = gl.projects.get(issue.project_id, lazy=True)
- editable_issue = project.issues.get(issue.iid, lazy=True)
- editable_issue.title = updated_title
- editable_issue.save()
-
-Project issues
-==============
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectIssue`
- + :class:`gitlab.v4.objects.ProjectIssueManager`
- + :attr:`gitlab.v4.objects.Project.issues`
-
-* GitLab API: https://docs.gitlab.com/ce/api/issues.html
-
-Examples
---------
-
-List the project issues::
-
- issues = project.issues.list()
- # Filter using the state, labels and milestone parameters
- issues = project.issues.list(milestone='1.0', state='opened')
- # Order using the order_by and sort parameters
- issues = project.issues.list(order_by='created_at', sort='desc')
-
-Get a project issue::
-
- issue = project.issues.get(issue_iid)
-
-Create a new issue::
-
- issue = project.issues.create({'title': 'I have a bug',
- 'description': 'Something useful here.'})
-
-Update an issue::
-
- issue.labels = ['foo', 'bar']
- issue.save()
-
-Close / reopen an issue::
-
- # close an issue
- issue.state_event = 'close'
- issue.save()
- # reopen it
- issue.state_event = 'reopen'
- issue.save()
-
-Delete an issue (admin or project owner only)::
-
- project.issues.delete(issue_id)
- # pr
- issue.delete()
-
-Subscribe / unsubscribe from an issue::
-
- issue.subscribe()
- issue.unsubscribe()
-
-Move an issue to another project::
-
- issue.move(other_project_id)
-
-Make an issue as todo::
-
- issue.todo()
-
-Get time tracking stats::
-
- issue.time_stats()
-
-On recent versions of Gitlab the time stats are also returned as an issue
-object attribute::
-
- issue = project.issue.get(iid)
- print(issue.attributes['time_stats'])
-
-Set a time estimate for an issue::
-
- issue.time_estimate('3h30m')
-
-Reset a time estimate for an issue::
-
- issue.reset_time_estimate()
-
-Add spent time for an issue::
-
- issue.add_spent_time('3h30m')
-
-Reset spent time for an issue::
-
- issue.reset_spent_time()
-
-Get user agent detail for the issue (admin only)::
-
- detail = issue.user_agent_detail()
-
-Get the list of merge requests that will close an issue when merged::
-
- mrs = issue.closed_by()
-
-Get the merge requests related to an issue::
-
- mrs = issue.related_merge_requests()
-
-Get the list of participants::
-
- users = issue.participants()
-
-Issue links
-===========
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectIssueLink`
- + :class:`gitlab.v4.objects.ProjectIssueLinkManager`
- + :attr:`gitlab.v4.objects.ProjectIssue.links`
-
-* GitLab API: https://docs.gitlab.com/ee/api/issue_links.html (EE feature)
-
-Examples
---------
-
-List the issues linked to ``i1``::
-
- links = i1.links.list()
-
-Link issue ``i1`` to issue ``i2``::
-
- data = {
- 'target_project_id': i2.project_id,
- 'target_issue_iid': i2.iid
- }
- src_issue, dest_issue = i1.links.create(data)
-
-.. note::
-
- The ``create()`` method returns the source and destination ``ProjectIssue``
- objects, not a ``ProjectIssueLink`` object.
-
-Delete a link::
-
- i1.links.delete(issue_link_id)
-
-Issues statistics
-=========================
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.IssuesStatistics`
- + :class:`gitlab.v4.objects.IssuesStatisticsManager`
- + :attr:`gitlab.issues_statistics`
- + :class:`gitlab.v4.objects.GroupIssuesStatistics`
- + :class:`gitlab.v4.objects.GroupIssuesStatisticsManager`
- + :attr:`gitlab.v4.objects.Group.issues_statistics`
- + :class:`gitlab.v4.objects.ProjectIssuesStatistics`
- + :class:`gitlab.v4.objects.ProjectIssuesStatisticsManager`
- + :attr:`gitlab.v4.objects.Project.issues_statistics`
-
-
-* GitLab API: https://docs.gitlab.com/ce/api/issues_statistics.htm
-
-Examples
----------
-
-Get statistics of all issues created by the current user::
-
- statistics = gl.issues_statistics.get()
-
-Get statistics of all issues the user has access to::
-
- statistics = gl.issues_statistics.get(scope='all')
-
-Get statistics of issues for the user with ``foobar`` in the ``title`` or the ``description``::
-
- statistics = gl.issues_statistics.get(search='foobar')
-
-Get statistics of all issues in a group::
-
- statistics = group.issues_statistics.get()
-
-Get statistics of issues in a group with ``foobar`` in the ``title`` or the ``description``::
-
- statistics = group.issues_statistics.get(search='foobar')
-
-Get statistics of all issues in a project::
-
- statistics = project.issues_statistics.get()
-
-Get statistics of issues in a project with ``foobar`` in the ``title`` or the ``description``::
-
- statistics = project.issues_statistics.get(search='foobar')
diff --git a/docs/gl_objects/keys.rst b/docs/gl_objects/keys.rst
deleted file mode 100644
index 6d35218..0000000
--- a/docs/gl_objects/keys.rst
+++ /dev/null
@@ -1,28 +0,0 @@
-####
-Keys
-####
-
-Keys
-====
-
-Reference
----------
-
-* v4 API
-
- + :class:`gitlab.v4.objects.Key`
- + :class:`gitlab.v4.objects.KeyManager`
- + :attr:`gitlab.Gitlab.keys`
-
-* GitLab API: https://docs.gitlab.com/ce/api/keys.html
-
-Examples
---------
-
-Get an ssh key by its id (requires admin access)::
-
- key = gl.keys.get(key_id)
-
-Get an ssh key (requires admin access) or a deploy key by its fingerprint::
-
- key = gl.keys.get(fingerprint="SHA256:ERJJ/OweAM6jA8OjJ/gXs4N5fqUaREEJnz/EyfywfXY")
diff --git a/docs/gl_objects/labels.rst b/docs/gl_objects/labels.rst
deleted file mode 100644
index a4667aa..0000000
--- a/docs/gl_objects/labels.rst
+++ /dev/null
@@ -1,89 +0,0 @@
-######
-Labels
-######
-
-Project labels
-==============
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectLabel`
- + :class:`gitlab.v4.objects.ProjectLabelManager`
- + :attr:`gitlab.v4.objects.Project.labels`
-
-* GitLab API: https://docs.gitlab.com/ce/api/labels.html
-
-Examples
---------
-
-List labels for a project::
-
- labels = project.labels.list()
-
-Create a label for a project::
-
- label = project.labels.create({'name': 'foo', 'color': '#8899aa'})
-
-Update a label for a project::
-
- # change the name of the label:
- label.new_name = 'bar'
- label.save()
- # change its color:
- label.color = '#112233'
- label.save()
-
-Delete a label for a project::
-
- project.labels.delete(label_id)
- # or
- label.delete()
-
-Manage labels in issues and merge requests::
-
- # Labels are defined as lists in issues and merge requests. The labels must
- # exist.
- issue = p.issues.create({'title': 'issue title',
- 'description': 'issue description',
- 'labels': ['foo']})
- issue.labels.append('bar')
- issue.save()
-
-Label events
-============
-
-Resource label events keep track about who, when, and which label was added or
-removed to an issuable.
-
-Group epic label events are only available in the EE edition.
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectIssueResourceLabelEvent`
- + :class:`gitlab.v4.objects.ProjectIssueResourceLabelEventManager`
- + :attr:`gitlab.v4.objects.ProjectIssue.resourcelabelevents`
- + :class:`gitlab.v4.objects.ProjectMergeRequestResourceLabelEvent`
- + :class:`gitlab.v4.objects.ProjectMergeRequestResourceLabelEventManager`
- + :attr:`gitlab.v4.objects.ProjectMergeRequest.resourcelabelevents`
- + :class:`gitlab.v4.objects.GroupEpicResourceLabelEvent`
- + :class:`gitlab.v4.objects.GroupEpicResourceLabelEventManager`
- + :attr:`gitlab.v4.objects.GroupEpic.resourcelabelevents`
-
-* GitLab API: https://docs.gitlab.com/ee/api/resource_label_events.html
-
-Examples
---------
-
-Get the events for a resource (issue, merge request or epic)::
-
- events = resource.resourcelabelevents.list()
-
-Get a specific event for a resource::
-
- event = resource.resourcelabelevents.get(event_id)
diff --git a/docs/gl_objects/messages.rst b/docs/gl_objects/messages.rst
deleted file mode 100644
index 32fbb95..0000000
--- a/docs/gl_objects/messages.rst
+++ /dev/null
@@ -1,48 +0,0 @@
-##################
-Broadcast messages
-##################
-
-You can use broadcast messages to display information on all pages of the
-gitlab web UI. You must have administration permissions to manipulate broadcast
-messages.
-
-References
-----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.BroadcastMessage`
- + :class:`gitlab.v4.objects.BroadcastMessageManager`
- + :attr:`gitlab.Gitlab.broadcastmessages`
-
-* GitLab API: https://docs.gitlab.com/ce/api/broadcast_messages.html
-
-Examples
---------
-
-List the messages::
-
- msgs = gl.broadcastmessages.list()
-
-Get a single message::
-
- msg = gl.broadcastmessages.get(msg_id)
-
-Create a message::
-
- msg = gl.broadcastmessages.create({'message': 'Important information'})
-
-The date format for the ``starts_at`` and ``ends_at`` parameters is
-``YYYY-MM-ddThh:mm:ssZ``.
-
-Update a message::
-
- msg.font = '#444444'
- msg.color = '#999999'
- msg.save()
-
-Delete a message::
-
- gl.broadcastmessages.delete(msg_id)
- # or
- msg.delete()
diff --git a/docs/gl_objects/milestones.rst b/docs/gl_objects/milestones.rst
deleted file mode 100644
index 3830f81..0000000
--- a/docs/gl_objects/milestones.rst
+++ /dev/null
@@ -1,105 +0,0 @@
-##########
-Milestones
-##########
-
-Project milestones
-==================
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectMilestone`
- + :class:`gitlab.v4.objects.ProjectMilestoneManager`
- + :attr:`gitlab.v4.objects.Project.milestones`
-
- + :class:`gitlab.v4.objects.GroupMilestone`
- + :class:`gitlab.v4.objects.GroupMilestoneManager`
- + :attr:`gitlab.v4.objects.Group.milestones`
-
-* GitLab API:
-
- + https://docs.gitlab.com/ce/api/milestones.html
- + https://docs.gitlab.com/ce/api/group_milestones.html
-
-Examples
---------
-
-List the milestones for a project or a group::
-
- p_milestones = project.milestones.list()
- g_milestones = group.milestones.list()
-
-You can filter the list using the following parameters:
-
-* ``iids``: unique IDs of milestones for the project
-* ``state``: either ``active`` or ``closed``
-* ``search``: to search using a string
-
-::
-
- p_milestones = project.milestones.list(state='closed')
- g_milestones = group.milestones.list(state='active')
-
-Get a single milestone::
-
- p_milestone = project.milestones.get(milestone_id)
- g_milestone = group.milestones.get(milestone_id)
-
-Create a milestone::
-
- milestone = project.milestones.create({'title': '1.0'})
-
-Edit a milestone::
-
- milestone.description = 'v 1.0 release'
- milestone.save()
-
-Change the state of a milestone (activate / close)::
-
- # close a milestone
- milestone.state_event = 'close'
- milestone.save()
-
- # activate a milestone
- milestone.state_event = 'activate'
- milestone.save()
-
-List the issues related to a milestone::
-
- issues = milestone.issues()
-
-List the merge requests related to a milestone::
-
- merge_requests = milestone.merge_requests()
-
-Milestone events
-================
-
-Resource milestone events keep track of what happens to GitLab issues and merge requests.
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectIssueResourceMilestoneEvent`
- + :class:`gitlab.v4.objects.ProjectIssueResourceMilestoneEventManager`
- + :attr:`gitlab.v4.objects.ProjectIssue.resourcemilestoneevents`
- + :class:`gitlab.v4.objects.ProjectMergeRequestResourceMilestoneEvent`
- + :class:`gitlab.v4.objects.ProjectMergeRequestResourceMilestoneEventManager`
- + :attr:`gitlab.v4.objects.ProjectMergeRequest.resourcemilestoneevents`
-
-* GitLab API: https://docs.gitlab.com/ee/api/resource_milestone_events.html
-
-Examples
---------
-
-Get milestones for a resource (issue, merge request)::
-
- milestones = resource.resourcemilestoneevents.list()
-
-Get a specific milestone for a resource::
-
- milestone = resource.resourcemilestoneevents.get(milestone_id)
diff --git a/docs/gl_objects/mr_approvals.rst b/docs/gl_objects/mr_approvals.rst
deleted file mode 100644
index 9e47535..0000000
--- a/docs/gl_objects/mr_approvals.rst
+++ /dev/null
@@ -1,86 +0,0 @@
-################################
-Merge request approvals settings
-################################
-
-Merge request approvals can be defined at the project level or at the merge
-request level.
-
-References
-----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectApproval`
- + :class:`gitlab.v4.objects.ProjectApprovalManager`
- + :class:`gitlab.v4.objects.ProjectApprovalRule`
- + :class:`gitlab.v4.objects.ProjectApprovalRuleManager`
- + :attr:`gitlab.v4.objects.Project.approvals`
- + :class:`gitlab.v4.objects.ProjectMergeRequestApproval`
- + :class:`gitlab.v4.objects.ProjectMergeRequestApprovalManager`
- + :attr:`gitlab.v4.objects.ProjectMergeRequest.approvals`
- + :class:`gitlab.v4.objects.ProjectMergeRequestApprovalRule`
- + :class:`gitlab.v4.objects.ProjectMergeRequestApprovalRuleManager`
- + :attr:`gitlab.v4.objects.ProjectMergeRequest.approval_rules`
-
-* GitLab API: https://docs.gitlab.com/ee/api/merge_request_approvals.html
-
-Examples
---------
-
-List project-level MR approval rules::
-
- p_mras = project.approvalrules.list()
-
-Change project-level MR approval rule::
-
- p_approvalrule.user_ids = [234]
- p_approvalrule.save()
-
-Delete project-level MR approval rule::
-
- p_approvalrule.delete()
-
-Get project-level or MR-level MR approvals settings::
-
- p_mras = project.approvals.get()
-
- mr_mras = mr.approvals.get()
-
-Change project-level or MR-level MR approvals settings::
-
- p_mras.approvals_before_merge = 2
- p_mras.save()
-
- mr_mras.set_approvers(approvals_required = 1)
-
-Change project-level MR allowed approvers::
-
- project.approvals.set_approvers(approver_ids=[105],
- approver_group_ids=[653, 654])
-
-Create a new MR-level approval rule or change an existing MR-level approval rule::
-
- mr.approvals.set_approvers(approvals_required = 1, approver_ids=[105],
- approver_group_ids=[653, 654],
- approval_rule_name="my MR custom approval rule")
-
-List MR-level MR approval rules::
-
- mr.approval_rules.list()
-
-Change MR-level MR approval rule::
-
- mr_approvalrule.user_ids = [105]
- mr_approvalrule.approvals_required = 2
- mr_approvalrule.group_ids = [653, 654]
- mr_approvalrule.save()
-
-Create a MR-level MR approval rule::
-
- mr.approval_rules.create({
- "name": "my MR custom approval rule",
- "approvals_required": 2,
- "rule_type": "regular",
- "user_ids": [105],
- "group_ids": [653, 654],
- })
diff --git a/docs/gl_objects/mrs.rst b/docs/gl_objects/mrs.rst
deleted file mode 100644
index f17ad26..0000000
--- a/docs/gl_objects/mrs.rst
+++ /dev/null
@@ -1,217 +0,0 @@
-.. _merge_requests_examples:
-
-##############
-Merge requests
-##############
-
-You can use merge requests to notify a project that a branch is ready for
-merging. The owner of the target projet can accept the merge request.
-
-Merge requests are linked to projects, but they can be listed globally or for
-groups.
-
-Group and global listing
-========================
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.GroupMergeRequest`
- + :class:`gitlab.v4.objects.GroupMergeRequestManager`
- + :attr:`gitlab.v4.objects.Group.mergerequests`
- + :class:`gitlab.v4.objects.MergeRequest`
- + :class:`gitlab.v4.objects.MergeRequestManager`
- + :attr:`gitlab.Gitlab.mergerequests`
-
-* GitLab API: https://docs.gitlab.com/ce/api/merge_requests.html
-
-Examples
---------
-
-List the merge requests created by the user of the token on the GitLab server::
-
- mrs = gl.mergerequests.list()
-
-List the merge requests available on the GitLab server::
-
- mrs = gl.mergerequests.list(scope="all")
-
-List the merge requests for a group::
-
- group = gl.groups.get('mygroup')
- mrs = group.mergerequests.list()
-
-.. note::
-
- It is not possible to edit or delete ``MergeRequest`` and
- ``GroupMergeRequest`` objects. You need to create a ``ProjectMergeRequest``
- object to apply changes::
-
- mr = group.mergerequests.list()[0]
- project = gl.projects.get(mr.project_id, lazy=True)
- editable_mr = project.mergerequests.get(mr.iid, lazy=True)
- editable_mr.title = updated_title
- editable_mr.save()
-
-Project merge requests
-======================
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectMergeRequest`
- + :class:`gitlab.v4.objects.ProjectMergeRequestManager`
- + :attr:`gitlab.v4.objects.Project.mergerequests`
-
-* GitLab API: https://docs.gitlab.com/ce/api/merge_requests.html
-
-Examples
---------
-
-List MRs for a project::
-
- mrs = project.mergerequests.list()
-
-You can filter and sort the returned list with the following parameters:
-
-* ``state``: state of the MR. It can be one of ``all``, ``merged``, ``opened``
- or ``closed``
-* ``order_by``: sort by ``created_at`` or ``updated_at``
-* ``sort``: sort order (``asc`` or ``desc``)
-
-For example::
-
- mrs = project.mergerequests.list(state='merged', order_by='updated_at')
-
-Get a single MR::
-
- mr = project.mergerequests.get(mr_id)
-
-Create a MR::
-
- mr = project.mergerequests.create({'source_branch': 'cool_feature',
- 'target_branch': 'master',
- 'title': 'merge cool feature',
- 'labels': ['label1', 'label2']})
-
-Update a MR::
-
- mr.description = 'New description'
- mr.labels = ['foo', 'bar']
- mr.save()
-
-Change the state of a MR (close or reopen)::
-
- mr.state_event = 'close' # or 'reopen'
- mr.save()
-
-Delete a MR::
-
- project.mergerequests.delete(mr_id)
- # or
- mr.delete()
-
-Accept a MR::
-
- mr.merge()
-
-Cancel a MR when the build succeeds::
-
- mr.cancel_merge_when_pipeline_succeeds()
-
-List commits of a MR::
-
- commits = mr.commits()
-
-List the changes of a MR::
-
- changes = mr.changes()
-
-List issues that will close on merge::
-
- mr.closes_issues()
-
-Subscribe to / unsubscribe from a MR::
-
- mr.subscribe()
- mr.unsubscribe()
-
-Mark a MR as todo::
-
- mr.todo()
-
-List the diffs for a merge request::
-
- diffs = mr.diffs.list()
-
-Get a diff for a merge request::
-
- diff = mr.diffs.get(diff_id)
-
-Get time tracking stats::
-
- merge request.time_stats()
-
-On recent versions of Gitlab the time stats are also returned as a merge
-request object attribute::
-
- mr = project.mergerequests.get(id)
- print(mr.attributes['time_stats'])
-
-Set a time estimate for a merge request::
-
- mr.time_estimate('3h30m')
-
-Reset a time estimate for a merge request::
-
- mr.reset_time_estimate()
-
-Add spent time for a merge request::
-
- mr.add_spent_time('3h30m')
-
-Reset spent time for a merge request::
-
- mr.reset_spent_time()
-
-Get user agent detail for the issue (admin only)::
-
- detail = issue.user_agent_detail()
-
-Attempt to rebase an MR::
-
- mr.rebase()
-
-Attempt to merge changes between source and target branch::
-
- response = mr.merge_ref()
- print(response['commit_id'])
-
-Merge Request Pipelines
-=======================
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectMergeRequestPipeline`
- + :class:`gitlab.v4.objects.ProjectMergeRequestPipelineManager`
- + :attr:`gitlab.v4.objects.ProjectMergeRequest.pipelines`
-
-* GitLab API: https://docs.gitlab.com/ee/api/merge_requests.html#list-mr-pipelines
-
-Examples
---------
-
-List pipelines for a merge request::
-
- pipelines = mr.pipelines.list()
-
-Create a pipeline for a merge request::
-
- pipeline = mr.pipelines.create()
diff --git a/docs/gl_objects/namespaces.rst b/docs/gl_objects/namespaces.rst
deleted file mode 100644
index 1aebd29..0000000
--- a/docs/gl_objects/namespaces.rst
+++ /dev/null
@@ -1,25 +0,0 @@
-##########
-Namespaces
-##########
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.Namespace`
- + :class:`gitlab.v4.objects.NamespaceManager`
- + :attr:`gitlab.Gitlab.namespaces`
-
-* GitLab API: https://docs.gitlab.com/ce/api/namespaces.html
-
-Examples
---------
-
-List namespaces::
-
- namespaces = gl.namespaces.list()
-
-Search namespaces::
-
- namespaces = gl.namespaces.list(search='foo')
diff --git a/docs/gl_objects/notes.rst b/docs/gl_objects/notes.rst
deleted file mode 100644
index 053c0a0..0000000
--- a/docs/gl_objects/notes.rst
+++ /dev/null
@@ -1,63 +0,0 @@
-.. _project-notes:
-
-#####
-Notes
-#####
-
-You can manipulate notes (comments) on project issues, merge requests and
-snippets.
-
-Reference
----------
-
-* v4 API:
-
- Issues:
-
- + :class:`gitlab.v4.objects.ProjectIssueNote`
- + :class:`gitlab.v4.objects.ProjectIssueNoteManager`
- + :attr:`gitlab.v4.objects.ProjectIssue.notes`
-
- MergeRequests:
-
- + :class:`gitlab.v4.objects.ProjectMergeRequestNote`
- + :class:`gitlab.v4.objects.ProjectMergeRequestNoteManager`
- + :attr:`gitlab.v4.objects.ProjectMergeRequest.notes`
-
- Snippets:
-
- + :class:`gitlab.v4.objects.ProjectSnippetNote`
- + :class:`gitlab.v4.objects.ProjectSnippetNoteManager`
- + :attr:`gitlab.v4.objects.ProjectSnippet.notes`
-
-* GitLab API: https://docs.gitlab.com/ce/api/notes.html
-
-Examples
---------
-
-List the notes for a resource::
-
- i_notes = issue.notes.list()
- mr_notes = mr.notes.list()
- s_notes = snippet.notes.list()
-
-Get a note for a resource::
-
- i_note = issue.notes.get(note_id)
- mr_note = mr.notes.get(note_id)
- s_note = snippet.notes.get(note_id)
-
-Create a note for a resource::
-
- i_note = issue.notes.create({'body': 'note content'})
- mr_note = mr.notes.create({'body': 'note content'})
- s_note = snippet.notes.create({'body': 'note content'})
-
-Update a note for a resource::
-
- note.body = 'updated note content'
- note.save()
-
-Delete a note for a resource::
-
- note.delete()
diff --git a/docs/gl_objects/notifications.rst b/docs/gl_objects/notifications.rst
deleted file mode 100644
index ab0287f..0000000
--- a/docs/gl_objects/notifications.rst
+++ /dev/null
@@ -1,59 +0,0 @@
-#####################
-Notification settings
-#####################
-
-You can define notification settings globally, for groups and for projects.
-Valid levels are defined as constants:
-
-* ``gitlab.NOTIFICATION_LEVEL_DISABLED``
-* ``gitlab.NOTIFICATION_LEVEL_PARTICIPATING``
-* ``gitlab.NOTIFICATION_LEVEL_WATCH``
-* ``gitlab.NOTIFICATION_LEVEL_GLOBAL``
-* ``gitlab.NOTIFICATION_LEVEL_MENTION``
-* ``gitlab.NOTIFICATION_LEVEL_CUSTOM``
-
-You get access to fine-grained settings if you use the
-``NOTIFICATION_LEVEL_CUSTOM`` level.
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.NotificationSettings`
- + :class:`gitlab.v4.objects.NotificationSettingsManager`
- + :attr:`gitlab.Gitlab.notificationsettings`
- + :class:`gitlab.v4.objects.GroupNotificationSettings`
- + :class:`gitlab.v4.objects.GroupNotificationSettingsManager`
- + :attr:`gitlab.v4.objects.Group.notificationsettings`
- + :class:`gitlab.v4.objects.ProjectNotificationSettings`
- + :class:`gitlab.v4.objects.ProjectNotificationSettingsManager`
- + :attr:`gitlab.v4.objects.Project.notificationsettings`
-
-* GitLab API: https://docs.gitlab.com/ce/api/notification_settings.html
-
-Examples
---------
-
-Get the notifications settings::
-
- # global settings
- settings = gl.notificationsettings.get()
- # for a group
- settings = gl.groups.get(group_id).notificationsettings.get()
- # for a project
- settings = gl.projects.get(project_id).notificationsettings.get()
-
-Update the notifications settings::
-
- # use a predefined level
- settings.level = gitlab.NOTIFICATION_LEVEL_WATCH
-
- # create a custom setup
- settings.level = gitlab.NOTIFICATION_LEVEL_CUSTOM
- settings.save() # will create additional attributes, but not mandatory
-
- settings.new_merge_request = True
- settings.new_issue = True
- settings.new_note = True
- settings.save()
diff --git a/docs/gl_objects/packages.rst b/docs/gl_objects/packages.rst
deleted file mode 100644
index cc64e07..0000000
--- a/docs/gl_objects/packages.rst
+++ /dev/null
@@ -1,131 +0,0 @@
-########
-Packages
-########
-
-Packages allow you to utilize GitLab as a private repository for a variety
-of common package managers, as well as GitLab's generic package registry.
-
-Project Packages
-=====================
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectPackage`
- + :class:`gitlab.v4.objects.ProjectPackageManager`
- + :attr:`gitlab.v4.objects.Project.packages`
-
-* GitLab API: https://docs.gitlab.com/ee/api/packages.html#within-a-project
-
-Examples
---------
-
-List the packages in a project::
-
- packages = project.packages.list()
-
-Filter the results by ``package_type`` or ``package_name`` ::
-
- packages = project.packages.list(package_type='pypi')
-
-Get a specific package of a project by id::
-
- package = project.packages.get(1)
-
-Delete a package from a project::
-
- package.delete()
- # or
- project.packages.delete(package.id)
-
-
-Group Packages
-===================
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.GroupPackage`
- + :class:`gitlab.v4.objects.GroupPackageManager`
- + :attr:`gitlab.v4.objects.Group.packages`
-
-* GitLab API: https://docs.gitlab.com/ee/api/packages.html#within-a-group
-
-Examples
---------
-
-List the packages in a group::
-
- packages = group.packages.list()
-
-Filter the results by ``package_type`` or ``package_name`` ::
-
- packages = group.packages.list(package_type='pypi')
-
-
-Project Package Files
-=====================
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectPackageFile`
- + :class:`gitlab.v4.objects.ProjectPackageFileManager`
- + :attr:`gitlab.v4.objects.ProjectPackage.package_files`
-
-* GitLab API: https://docs.gitlab.com/ee/api/packages.html#list-package-files
-
-Examples
---------
-
-List package files for package in project::
-
- package = project.packages.get(1)
- package_files = package.package_files.list()
-
-Generic Packages
-================
-
-You can use python-gitlab to upload and download generic packages.
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.GenericPackage`
- + :class:`gitlab.v4.objects.GenericPackageManager`
- + :attr:`gitlab.v4.objects.Project.generic_packages`
-
-* GitLab API: https://docs.gitlab.com/ee/user/packages/generic_packages
-
-Examples
---------
-
-Upload a generic package to a project::
-
- project = gl.projects.get(1, lazy=True)
- package = project.generic_packages.upload(
- package_name="hello-world",
- package_version="v1.0.0",
- file_name="hello.tar.gz",
- path="/path/to/local/hello.tar.gz"
- )
-
-Download a project's generic package::
-
- project = gl.projects.get(1, lazy=True)
- package = project.generic_packages.download(
- package_name="hello-world",
- package_version="v1.0.0",
- file_name="hello.tar.gz",
- )
-
-.. hint:: You can use the Packages API described above to find packages and
- retrieve the metadata you need download them.
diff --git a/docs/gl_objects/pagesdomains.rst b/docs/gl_objects/pagesdomains.rst
deleted file mode 100644
index d6b39c7..0000000
--- a/docs/gl_objects/pagesdomains.rst
+++ /dev/null
@@ -1,65 +0,0 @@
-#############
-Pages domains
-#############
-
-Admin
-=====
-
-References
-----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.PagesDomain`
- + :class:`gitlab.v4.objects.PagesDomainManager`
- + :attr:`gitlab.Gitlab.pagesdomains`
-
-* GitLab API: https://docs.gitlab.com/ce/api/pages_domains.html#list-all-pages-domains
-
-Examples
---------
-
-List all the existing domains (admin only)::
-
- domains = gl.pagesdomains.list()
-
-Project pages domain
-====================
-
-References
-----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectPagesDomain`
- + :class:`gitlab.v4.objects.ProjectPagesDomainManager`
- + :attr:`gitlab.v4.objects.Project.pagesdomains`
-
-* GitLab API: https://docs.gitlab.com/ce/api/pages_domains.html#list-pages-domains
-
-Examples
---------
-
-List domains for a project::
-
- domains = project.pagesdomains.list()
-
-Get a single domain::
-
- domain = project.pagesdomains.get('d1.example.com')
-
-Create a new domain::
-
- domain = project.pagesdomains.create({'domain': 'd2.example.com})
-
-Update an existing domain::
-
- domain.certificate = open('d2.crt').read()
- domain.key = open('d2.key').read()
- domain.save()
-
-Delete an existing domain::
-
- domain.delete
- # or
- project.pagesdomains.delete('d2.example.com')
diff --git a/docs/gl_objects/personal_access_tokens.rst b/docs/gl_objects/personal_access_tokens.rst
deleted file mode 100644
index 0704c75..0000000
--- a/docs/gl_objects/personal_access_tokens.rst
+++ /dev/null
@@ -1,54 +0,0 @@
-######################
-Personal Access Tokens
-######################
-
-References
-----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.PersonalAccessToken`
- + :class:`gitlab.v4.objects.PersonalAcessTokenManager`
- + :attr:`gitlab.Gitlab.personal_access_tokens`
- + :class:`gitlab.v4.objects.UserPersonalAccessToken`
- + :class:`gitlab.v4.objects.UserPersonalAcessTokenManager`
- + :attr:`gitlab.Gitlab.User.personal_access_tokens`
-
-* GitLab API:
-
- + https://docs.gitlab.com/ee/api/personal_access_tokens.html
- + https://docs.gitlab.com/ee/api/users.html#create-a-personal-access-token
-
-Examples
---------
-
-List personal access tokens::
-
- access_tokens = gl.personal_access_tokens.list()
- print(access_tokens[0].name)
-
-List personal access tokens from other user_id (admin only)::
-
- access_tokens = gl.personal_access_tokens.list(user_id=25)
-
-Revoke a personal access token fetched via list::
-
- access_token = access_tokens[0]
- access_token.delete()
-
-Revoke a personal access token by id::
-
- gl.personal_access_tokens.delete(123)
-
-Create a personal access token for a user (admin only)::
-
- user = gl.users.get(25, lazy=True)
- access_token = user.personal_access_tokens.create({"name": "test", "scopes": "api"})
-
-.. note:: As you can see above, you can only create personal access tokens
- via the Users API, but you cannot revoke these objects directly.
- This is because the create API uses a different endpoint than the list and revoke APIs.
- You need to fetch the token via the list API first to revoke it.
-
- As of 14.2, GitLab does not provide a GET API for single personal access tokens.
- You must use the list method to retrieve single tokens.
diff --git a/docs/gl_objects/pipelines_and_jobs.rst b/docs/gl_objects/pipelines_and_jobs.rst
deleted file mode 100644
index 675ff4e..0000000
--- a/docs/gl_objects/pipelines_and_jobs.rst
+++ /dev/null
@@ -1,355 +0,0 @@
-##################
-Pipelines and Jobs
-##################
-
-Project pipelines
-=================
-
-A pipeline is a group of jobs executed by GitLab CI.
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectPipeline`
- + :class:`gitlab.v4.objects.ProjectPipelineManager`
- + :attr:`gitlab.v4.objects.Project.pipelines`
-
-* GitLab API: https://docs.gitlab.com/ce/api/pipelines.html
-
-Examples
---------
-
-List pipelines for a project::
-
- pipelines = project.pipelines.list()
-
-Get a pipeline for a project::
-
- pipeline = project.pipelines.get(pipeline_id)
-
-Get variables of a pipeline::
-
- variables = pipeline.variables.list()
-
-Create a pipeline for a particular reference with custom variables::
-
- pipeline = project.pipelines.create({'ref': 'master', 'variables': [{'key': 'MY_VARIABLE', 'value': 'hello'}]})
-
-Retry the failed builds for a pipeline::
-
- pipeline.retry()
-
-Cancel builds in a pipeline::
-
- pipeline.cancel()
-
-Delete a pipeline::
-
- pipeline.delete()
-
-Triggers
-========
-
-Triggers provide a way to interact with the GitLab CI. Using a trigger a user
-or an application can run a new build/job for a specific commit.
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectTrigger`
- + :class:`gitlab.v4.objects.ProjectTriggerManager`
- + :attr:`gitlab.v4.objects.Project.triggers`
-
-* GitLab API: https://docs.gitlab.com/ce/api/pipeline_triggers.html
-
-Examples
---------
-
-List triggers::
-
- triggers = project.triggers.list()
-
-Get a trigger::
-
- trigger = project.triggers.get(trigger_token)
-
-Create a trigger::
-
- trigger = project.triggers.create({'description': 'mytrigger'})
-
-Remove a trigger::
-
- project.triggers.delete(trigger_token)
- # or
- trigger.delete()
-
-Full example with wait for finish::
-
- def get_or_create_trigger(project):
- trigger_decription = 'my_trigger_id'
- for t in project.triggers.list():
- if t.description == trigger_decription:
- return t
- return project.triggers.create({'description': trigger_decription})
-
- trigger = get_or_create_trigger(project)
- pipeline = project.trigger_pipeline('master', trigger.token, variables={"DEPLOY_ZONE": "us-west1"})
- while pipeline.finished_at is None:
- pipeline.refresh()
- time.sleep(1)
-
-You can trigger a pipeline using token authentication instead of user
-authentication. To do so create an anonymous Gitlab instance and use lazy
-objects to get the associated project::
-
- gl = gitlab.Gitlab(URL) # no authentication
- project = gl.projects.get(project_id, lazy=True) # no API call
- project.trigger_pipeline('master', trigger_token)
-
-Reference: https://docs.gitlab.com/ee/ci/triggers/#trigger-token
-
-Pipeline schedule
-=================
-
-You can schedule pipeline runs using a cron-like syntax. Variables can be
-associated with the scheduled pipelines.
-
-Reference
----------
-
-* v4 API
-
- + :class:`gitlab.v4.objects.ProjectPipelineSchedule`
- + :class:`gitlab.v4.objects.ProjectPipelineScheduleManager`
- + :attr:`gitlab.v4.objects.Project.pipelineschedules`
- + :class:`gitlab.v4.objects.ProjectPipelineScheduleVariable`
- + :class:`gitlab.v4.objects.ProjectPipelineScheduleVariableManager`
- + :attr:`gitlab.v4.objects.Project.pipelineschedules`
-
-* GitLab API: https://docs.gitlab.com/ce/api/pipeline_schedules.html
-
-Examples
---------
-
-List pipeline schedules::
-
- scheds = project.pipelineschedules.list()
-
-Get a single schedule::
-
- sched = projects.pipelineschedules.get(schedule_id)
-
-Create a new schedule::
-
- sched = project.pipelineschedules.create({
- 'ref': 'master',
- 'description': 'Daily test',
- 'cron': '0 1 * * *'})
-
-Update a schedule::
-
- sched.cron = '1 2 * * *'
- sched.save()
-
-Take ownership of a schedule:
-
- sched.take_ownership()
-
-Trigger a pipeline schedule immediately::
-
- sched = projects.pipelineschedules.get(schedule_id)
- sched.play()
-
-Delete a schedule::
-
- sched.delete()
-
-List schedule variables::
-
- # note: you need to use get() to retrieve the schedule variables. The
- # attribute is not present in the response of a list() call
- sched = projects.pipelineschedules.get(schedule_id)
- vars = sched.attributes['variables']
-
-Create a schedule variable::
-
- var = sched.variables.create({'key': 'foo', 'value': 'bar'})
-
-Edit a schedule variable::
-
- var.value = 'new_value'
- var.save()
-
-Delete a schedule variable::
-
- var.delete()
-
-
-Jobs
-====
-
-Jobs are associated to projects, pipelines and commits. They provide
-information on the jobs that have been run, and methods to manipulate
-them.
-
-Reference
----------
-
-* v4 API
-
- + :class:`gitlab.v4.objects.ProjectJob`
- + :class:`gitlab.v4.objects.ProjectJobManager`
- + :attr:`gitlab.v4.objects.Project.jobs`
-
-* GitLab API: https://docs.gitlab.com/ce/api/jobs.html
-
-Examples
---------
-
-Jobs are usually automatically triggered, but you can explicitly trigger a new
-job::
-
- project.trigger_build('master', trigger_token,
- {'extra_var1': 'foo', 'extra_var2': 'bar'})
-
-List jobs for the project::
-
- jobs = project.jobs.list()
-
-Get a single job::
-
- project.jobs.get(job_id)
-
-List the jobs of a pipeline::
-
- project = gl.projects.get(project_id)
- pipeline = project.pipelines.get(pipeline_id)
- jobs = pipeline.jobs.list()
-
-.. note::
-
- Job methods (play, cancel, and so on) are not available on
- ``ProjectPipelineJob`` objects. To use these methods create a ``ProjectJob``
- object::
-
- pipeline_job = pipeline.jobs.list()[0]
- job = project.jobs.get(pipeline_job.id, lazy=True)
- job.retry()
-
-Get the artifacts of a job::
-
- build_or_job.artifacts()
-
-Get the artifacts of a job by its name from the latest successful pipeline of
-a branch or tag:
-
- project.artifacts(ref_name='master', job='build')
-
-.. warning::
-
- Artifacts are entirely stored in memory in this example.
-
-.. _streaming_example:
-
-You can download artifacts as a stream. Provide a callable to handle the
-stream::
-
- with open("archive.zip", "wb") as f:
- build_or_job.artifacts(streamed=True, action=f.write)
-
-You can also directly stream the output into a file, and unzip it afterwards::
-
- zipfn = "___artifacts.zip"
- with open(zipfn, "wb") as f:
- build_or_job.artifacts(streamed=True, action=f.write)
- subprocess.run(["unzip", "-bo", zipfn])
- os.unlink(zipfn)
-
-Get a single artifact file::
-
- build_or_job.artifact('path/to/file')
-
-Get a single artifact file by branch and job::
-
- project.artifact('branch', 'path/to/file', 'job')
-
-Mark a job artifact as kept when expiration is set::
-
- build_or_job.keep_artifacts()
-
-Delete the artifacts of a job::
-
- build_or_job.delete_artifacts()
-
-Get a job trace::
-
- build_or_job.trace()
-
-.. warning::
-
- Traces are entirely stored in memory unless you use the streaming feature.
- See :ref:`the artifacts example <streaming_example>`.
-
-Cancel/retry a job::
-
- build_or_job.cancel()
- build_or_job.retry()
-
-Play (trigger) a job::
-
- build_or_job.play()
-
-Erase a job (artifacts and trace)::
-
- build_or_job.erase()
-
-
-Pipeline bridges
-=====================
-
-Get a list of bridge jobs (including child pipelines) for a pipeline.
-
-Reference
----------
-
-* v4 API
-
- + :class:`gitlab.v4.objects.ProjectPipelineBridge`
- + :class:`gitlab.v4.objects.ProjectPipelineBridgeManager`
- + :attr:`gitlab.v4.objects.ProjectPipeline.bridges`
-
-* GitLab API: https://docs.gitlab.com/ee/api/jobs.html#list-pipeline-bridges
-
-Examples
---------
-
-List bridges for the pipeline::
-
- bridges = pipeline.bridges.list()
-
-Pipeline test report
-====================
-
-Get a pipeline's complete test report.
-
-Reference
----------
-
-* v4 API
-
- + :class:`gitlab.v4.objects.ProjectPipelineTestReport`
- + :class:`gitlab.v4.objects.ProjectPipelineTestReportManager`
- + :attr:`gitlab.v4.objects.ProjectPipeline.test_report`
-
-* GitLab API: https://docs.gitlab.com/ee/api/pipelines.html#get-a-pipelines-test-report
-
-Examples
---------
-
-Get the test report for a pipeline::
-
- test_report = pipeline.test_report.get()
diff --git a/docs/gl_objects/project_access_tokens.rst b/docs/gl_objects/project_access_tokens.rst
deleted file mode 100644
index 850cd25..0000000
--- a/docs/gl_objects/project_access_tokens.rst
+++ /dev/null
@@ -1,34 +0,0 @@
-#####################
-Project Access Tokens
-#####################
-
-Get a list of project access tokens
-
-References
-----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectAccessToken`
- + :class:`gitlab.v4.objects.ProjectAccessTokenManager`
- + :attr:`gitlab.Gitlab.project_access_tokens`
-
-* GitLab API: https://docs.gitlab.com/ee/api/resource_access_tokens.html
-
-Examples
---------
-
-List project access tokens::
-
- access_tokens = gl.projects.get(1, lazy=True).access_tokens.list()
- print(access_tokens[0].name)
-
-Create project access token::
-
- access_token = gl.projects.get(1).access_tokens.create({"name": "test", "scopes": ["api"]})
-
-Revoke a project access tokens::
-
- gl.projects.get(1).access_tokens.delete(42)
- # or
- access_token.delete()
diff --git a/docs/gl_objects/projects.rst b/docs/gl_objects/projects.rst
deleted file mode 100644
index fdf5ac5..0000000
--- a/docs/gl_objects/projects.rst
+++ /dev/null
@@ -1,768 +0,0 @@
-########
-Projects
-########
-
-Projects
-========
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.Project`
- + :class:`gitlab.v4.objects.ProjectManager`
- + :attr:`gitlab.Gitlab.projects`
-
-* GitLab API: https://docs.gitlab.com/ce/api/projects.html
-
-Examples
---------
-
-List projects::
-
- projects = gl.projects.list()
-
-The API provides several filtering parameters for the listing methods:
-
-* ``archived``: if ``True`` only archived projects will be returned
-* ``visibility``: returns only projects with the specified visibility (can be
- ``public``, ``internal`` or ``private``)
-* ``search``: returns project matching the given pattern
-
-Results can also be sorted using the following parameters:
-
-* ``order_by``: sort using the given argument. Valid values are ``id``,
- ``name``, ``path``, ``created_at``, ``updated_at`` and ``last_activity_at``.
- The default is to sort by ``created_at``
-* ``sort``: sort order (``asc`` or ``desc``)
-
-::
-
- # List all projects (default 20)
- projects = gl.projects.list(all=True)
- # Archived projects
- projects = gl.projects.list(archived=1)
- # Limit to projects with a defined visibility
- projects = gl.projects.list(visibility='public')
-
- # List owned projects
- projects = gl.projects.list(owned=True)
-
- # List starred projects
- projects = gl.projects.list(starred=True)
-
- # Search projects
- projects = gl.projects.list(search='keyword')
-
-.. note::
-
- Fetching a list of projects, doesn't include all attributes of all projects.
- To retrieve all attributes, you'll need to fetch a single project
-
-Get a single project::
-
- # Get a project by ID
- project_id = 851
- project = gl.projects.get(project_id)
-
- # Get a project by name with namespace
- project_name_with_namespace = "namespace/project_name"
- project = gl.projects.get(project_name_with_namespace)
-
-Create a project::
-
- project = gl.projects.create({'name': 'project1'})
-
-Create a project for a user (admin only)::
-
- alice = gl.users.list(username='alice')[0]
- user_project = alice.projects.create({'name': 'project'})
- user_projects = alice.projects.list()
-
-Create a project in a group::
-
- # You need to get the id of the group, then use the namespace_id attribute
- # to create the group
- group_id = gl.groups.list(search='my-group')[0].id
- project = gl.projects.create({'name': 'myrepo', 'namespace_id': group_id})
-
-Update a project::
-
- project.snippets_enabled = 1
- project.save()
-
-Set the avatar image for a project::
-
- # the avatar image can be passed as data (content of the file) or as a file
- # object opened in binary mode
- project.avatar = open('path/to/file.png', 'rb')
- project.save()
-
-Delete a project::
-
- gl.projects.delete(project_id)
- # or
- project.delete()
-
-Fork a project::
-
- fork = project.forks.create({})
-
- # fork to a specific namespace
- fork = project.forks.create({'namespace': 'myteam'})
-
-Get a list of forks for the project::
-
- forks = project.forks.list()
-
-Create/delete a fork relation between projects (requires admin permissions)::
-
- project.create_fork_relation(source_project.id)
- project.delete_fork_relation()
-
-Get languages used in the project with percentage value::
-
- languages = project.languages()
-
-Star/unstar a project::
-
- project.star()
- project.unstar()
-
-Archive/unarchive a project::
-
- project.archive()
- project.unarchive()
-
-Start the housekeeping job::
-
- project.housekeeping()
-
-List the repository tree::
-
- # list the content of the root directory for the default branch
- items = project.repository_tree()
-
- # list the content of a subdirectory on a specific branch
- items = project.repository_tree(path='docs', ref='branch1')
-
-Get the content and metadata of a file for a commit, using a blob sha::
-
- items = project.repository_tree(path='docs', ref='branch1')
- file_info = p.repository_blob(items[0]['id'])
- content = base64.b64decode(file_info['content'])
- size = file_info['size']
-
-Update a project submodule::
-
- items = project.update_submodule(
- submodule="foo/bar",
- branch="master",
- commit_sha="4c3674f66071e30b3311dac9b9ccc90502a72664",
- commit_message="Message", # optional
- )
-
-Get the repository archive::
-
- tgz = project.repository_archive()
-
- # get the archive for a branch/tag/commit
- tgz = project.repository_archive(sha='4567abc')
-
-.. warning::
-
- Archives are entirely stored in memory unless you use the streaming feature.
- See :ref:`the artifacts example <streaming_example>`.
-
-Get the content of a file using the blob id::
-
- # find the id for the blob (simple search)
- id = [d['id'] for d in p.repository_tree() if d['name'] == 'README.rst'][0]
-
- # get the content
- file_content = p.repository_raw_blob(id)
-
-.. warning::
-
- Blobs are entirely stored in memory unless you use the streaming feature.
- See :ref:`the artifacts example <streaming_example>`.
-
-Get a snapshot of the repository::
-
- tar_file = project.snapshot()
-
-.. warning::
-
- Snapshots are entirely stored in memory unless you use the streaming
- feature. See :ref:`the artifacts example <streaming_example>`.
-
-Compare two branches, tags or commits::
-
- result = project.repository_compare('master', 'branch1')
-
- # get the commits
- for commit in result['commits']:
- print(commit)
-
- # get the diffs
- for file_diff in result['diffs']:
- print(file_diff)
-
-Get a list of contributors for the repository::
-
- contributors = project.repository_contributors()
-
-Get a list of users for the repository::
-
- users = p.users.list()
-
- # search for users
- users = p.users.list(search='pattern')
-
-Start the pull mirroring process (EE edition)::
-
- project.mirror_pull()
-
-Import / Export
-===============
-
-You can export projects from gitlab, and re-import them to create new projects
-or overwrite existing ones.
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectExport`
- + :class:`gitlab.v4.objects.ProjectExportManager`
- + :attr:`gitlab.v4.objects.Project.exports`
- + :class:`gitlab.v4.objects.ProjectImport`
- + :class:`gitlab.v4.objects.ProjectImportManager`
- + :attr:`gitlab.v4.objects.Project.imports`
- + :attr:`gitlab.v4.objects.ProjectManager.import_project`
-
-* GitLab API: https://docs.gitlab.com/ce/api/project_import_export.html
-
-Examples
---------
-
-A project export is an asynchronous operation. To retrieve the archive
-generated by GitLab you need to:
-
-#. Create an export using the API
-#. Wait for the export to be done
-#. Download the result
-
-::
-
- # Create the export
- p = gl.projects.get(my_project)
- export = p.exports.create()
-
- # Wait for the 'finished' status
- export.refresh()
- while export.export_status != 'finished':
- time.sleep(1)
- export.refresh()
-
- # Download the result
- with open('/tmp/export.tgz', 'wb') as f:
- export.download(streamed=True, action=f.write)
-
-Import the project::
-
- output = gl.projects.import_project(open('/tmp/export.tgz', 'rb'), 'my_new_project')
- # Get a ProjectImport object to track the import status
- project_import = gl.projects.get(output['id'], lazy=True).imports.get()
- while project_import.import_status != 'finished':
- time.sleep(1)
- project_import.refresh()
-
-
-Project custom attributes
-=========================
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectCustomAttribute`
- + :class:`gitlab.v4.objects.ProjectCustomAttributeManager`
- + :attr:`gitlab.v4.objects.Project.customattributes`
-
-* GitLab API: https://docs.gitlab.com/ce/api/custom_attributes.html
-
-Examples
---------
-
-List custom attributes for a project::
-
- attrs = project.customattributes.list()
-
-Get a custom attribute for a project::
-
- attr = project.customattributes.get(attr_key)
-
-Set (create or update) a custom attribute for a project::
-
- attr = project.customattributes.set(attr_key, attr_value)
-
-Delete a custom attribute for a project::
-
- attr.delete()
- # or
- project.customattributes.delete(attr_key)
-
-Search projects by custom attribute::
-
- project.customattributes.set('type', 'internal')
- gl.projects.list(custom_attributes={'type': 'internal'})
-
-Project files
-=============
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectFile`
- + :class:`gitlab.v4.objects.ProjectFileManager`
- + :attr:`gitlab.v4.objects.Project.files`
-
-* GitLab API: https://docs.gitlab.com/ce/api/repository_files.html
-
-Examples
---------
-
-Get a file::
-
- f = project.files.get(file_path='README.rst', ref='master')
-
- # get the base64 encoded content
- print(f.content)
-
- # get the decoded content
- print(f.decode())
-
-Get a raw file::
-
- raw_content = project.files.raw(file_path='README.rst', ref='master')
- print(raw_content)
- with open('/tmp/raw-download.txt', 'wb') as f:
- project.files.raw(file_path='README.rst', ref='master', streamed=True, action=f.write)
-
-Create a new file::
-
- f = project.files.create({'file_path': 'testfile.txt',
- 'branch': 'master',
- 'content': file_content,
- 'author_email': 'test@example.com',
- 'author_name': 'yourname',
- 'encoding': 'text',
- 'commit_message': 'Create testfile'})
-
-Update a file. The entire content must be uploaded, as plain text or as base64
-encoded text::
-
- f.content = 'new content'
- f.save(branch='master', commit_message='Update testfile')
-
- # or for binary data
- # Note: decode() is required with python 3 for data serialization. You can omit
- # it with python 2
- f.content = base64.b64encode(open('image.png').read()).decode()
- f.save(branch='master', commit_message='Update testfile', encoding='base64')
-
-Delete a file::
-
- f.delete(commit_message='Delete testfile', branch='master')
- # or
- project.files.delete(file_path='testfile.txt', commit_message='Delete testfile', branch='master')
-
-Get file blame::
-
- b = project.files.blame(file_path='README.rst', ref='master')
-
-Project tags
-============
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectTag`
- + :class:`gitlab.v4.objects.ProjectTagManager`
- + :attr:`gitlab.v4.objects.Project.tags`
-
-* GitLab API: https://docs.gitlab.com/ce/api/tags.html
-
-Examples
---------
-
-List the project tags::
-
- tags = project.tags.list()
-
-Get a tag::
-
- tag = project.tags.get('1.0')
-
-Create a tag::
-
- tag = project.tags.create({'tag_name': '1.0', 'ref': 'master'})
-
-Delete a tag::
-
- project.tags.delete('1.0')
- # or
- tag.delete()
-
-.. _project_snippets:
-
-Project snippets
-================
-
-The snippet visibility can be defined using the following constants:
-
-* ``gitlab.VISIBILITY_PRIVATE``
-* ``gitlab.VISIBILITY_INTERNAL``
-* ``gitlab.VISIBILITY_PUBLIC``
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectSnippet`
- + :class:`gitlab.v4.objects.ProjectSnippetManager`
- + :attr:`gitlab.v4.objects.Project.files`
-
-* GitLab API: https://docs.gitlab.com/ce/api/project_snippets.html
-
-Examples
---------
-
-List the project snippets::
-
- snippets = project.snippets.list()
-
-Get a snippet::
-
- snippet = project.snippets.get(snippet_id)
-
-Get the content of a snippet::
-
- print(snippet.content())
-
-.. warning::
-
- The snippet content is entirely stored in memory unless you use the
- streaming feature. See :ref:`the artifacts example <streaming_example>`.
-
-Create a snippet::
-
- snippet = project.snippets.create({'title': 'sample 1',
- 'file_name': 'foo.py',
- 'code': 'import gitlab',
- 'visibility_level':
- gitlab.VISIBILITY_PRIVATE})
-
-Update a snippet::
-
- snippet.code = 'import gitlab\nimport whatever'
- snippet.save
-
-Delete a snippet::
-
- project.snippets.delete(snippet_id)
- # or
- snippet.delete()
-
-Get user agent detail (admin only)::
-
- detail = snippet.user_agent_detail()
-
-Notes
-=====
-
-See :ref:`project-notes`.
-
-Project members
-===============
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectMember`
- + :class:`gitlab.v4.objects.ProjectMemberManager`
- + :class:`gitlab.v4.objects.ProjectMemberAllManager`
- + :attr:`gitlab.v4.objects.Project.members`
- + :attr:`gitlab.v4.objects.Project.members_all`
-
-* GitLab API: https://docs.gitlab.com/ce/api/members.html
-
-Examples
---------
-
-List only direct project members::
-
- members = project.members.list()
-
-List the project members recursively (including inherited members through
-ancestor groups)::
-
- members = project.members_all.list(all=True)
-
-Search project members matching a query string::
-
- members = project.members.list(query='bar')
-
-Get only direct project member::
-
- member = project.members.get(user_id)
-
-Get a member of a project, including members inherited through ancestor groups::
-
- members = project.members_all.get(member_id)
-
-
-Add a project member::
-
- member = project.members.create({'user_id': user.id, 'access_level':
- gitlab.DEVELOPER_ACCESS})
-
-Modify a project member (change the access level)::
-
- member.access_level = gitlab.MAINTAINER_ACCESS
- member.save()
-
-Remove a member from the project team::
-
- project.members.delete(user.id)
- # or
- member.delete()
-
-Share/unshare the project with a group::
-
- project.share(group.id, gitlab.DEVELOPER_ACCESS)
- project.unshare(group.id)
-
-Project hooks
-=============
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectHook`
- + :class:`gitlab.v4.objects.ProjectHookManager`
- + :attr:`gitlab.v4.objects.Project.hooks`
-
-* GitLab API: https://docs.gitlab.com/ce/api/projects.html#hooks
-
-Examples
---------
-
-List the project hooks::
-
- hooks = project.hooks.list()
-
-Get a project hook::
-
- hook = project.hooks.get(hook_id)
-
-Create a project hook::
-
- hook = project.hooks.create({'url': 'http://my/action/url', 'push_events': 1})
-
-Update a project hook::
-
- hook.push_events = 0
- hook.save()
-
-Delete a project hook::
-
- project.hooks.delete(hook_id)
- # or
- hook.delete()
-
-Project Services
-================
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectService`
- + :class:`gitlab.v4.objects.ProjectServiceManager`
- + :attr:`gitlab.v4.objects.Project.services`
-
-* GitLab API: https://docs.gitlab.com/ce/api/services.html
-
-Examples
----------
-
-Get a service::
-
- service = project.services.get('asana')
- # display its status (enabled/disabled)
- print(service.active)
-
-List active project services::
-
- service = project.services.list()
-
-List the code names of available services (doesn't return objects)::
-
- services = project.services.available()
-
-Configure and enable a service::
-
- service.api_key = 'randomkey'
- service.save()
-
-Disable a service::
-
- service.delete()
-
-File uploads
-============
-
-Reference
----------
-
-* v4 API:
-
- + :attr:`gitlab.v4.objects.Project.upload`
-
-* Gitlab API: https://docs.gitlab.com/ce/api/projects.html#upload-a-file
-
-Examples
---------
-
-Upload a file into a project using a filesystem path::
-
- project.upload("filename.txt", filepath="/some/path/filename.txt")
-
-Upload a file into a project without a filesystem path::
-
- project.upload("filename.txt", filedata="Raw data")
-
-Upload a file and comment on an issue using the uploaded file's
-markdown::
-
- uploaded_file = project.upload("filename.txt", filedata="data")
- issue = project.issues.get(issue_id)
- issue.notes.create({
- "body": "See the attached file: {}".format(uploaded_file["markdown"])
- })
-
-Upload a file and comment on an issue while using custom
-markdown to reference the uploaded file::
-
- uploaded_file = project.upload("filename.txt", filedata="data")
- issue = project.issues.get(issue_id)
- issue.notes.create({
- "body": "See the [attached file]({})".format(uploaded_file["url"])
- })
-
-Project push rules
-==================
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectPushRules`
- + :class:`gitlab.v4.objects.ProjectPushRulesManager`
- + :attr:`gitlab.v4.objects.Project.pushrules`
-
-* GitLab API: https://docs.gitlab.com/ee/api/projects.html#push-rules
-
-Examples
----------
-
-Create project push rules (at least one rule is necessary)::
-
- project.pushrules.create({'deny_delete_tag': True})
-
-Get project push rules (returns None is there are no push rules)::
-
- pr = project.pushrules.get()
-
-Edit project push rules::
-
- pr.branch_name_regex = '^(master|develop|support-\d+|release-\d+\..+|hotfix-.+|feature-.+)$'
- pr.save()
-
-Delete project push rules::
-
- pr.delete()
-
-Project protected tags
-======================
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectProtectedTag`
- + :class:`gitlab.v4.objects.ProjectProtectedTagManager`
- + :attr:`gitlab.v4.objects.Project.protectedtags`
-
-* GitLab API: https://docs.gitlab.com/ce/api/protected_tags.html
-
-Examples
----------
-
-Get a list of protected tags from a project::
-
- protected_tags = project.protectedtags.list()
-
-Get a single protected tag or wildcard protected tag::
-
- protected_tag = project.protectedtags.get('v*')
-
-Protect a single repository tag or several project repository tags using a wildcard protected tag::
-
- project.protectedtags.create({'name': 'v*', 'create_access_level': '40'})
-
-Unprotect the given protected tag or wildcard protected tag.::
-
- protected_tag.delete()
-
-Additional project statistics
-=============================
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectAdditionalStatistics`
- + :class:`gitlab.v4.objects.ProjectAdditionalStatisticsManager`
- + :attr:`gitlab.v4.objects.Project.additionalstatistics`
-
-* GitLab API: https://docs.gitlab.com/ce/api/project_statistics.html
-
-Examples
----------
-
-Get all additional statistics of a project::
-
- statistics = project.additionalstatistics.get()
-
-Get total fetches in last 30 days of a project::
-
- total_fetches = project.additionalstatistics.get().fetches['total']
diff --git a/docs/gl_objects/protected_branches.rst b/docs/gl_objects/protected_branches.rst
deleted file mode 100644
index 3498aa5..0000000
--- a/docs/gl_objects/protected_branches.rst
+++ /dev/null
@@ -1,51 +0,0 @@
-##################
-Protected branches
-##################
-
-You can define a list of protected branch names on a repository. Names can use
-wildcards (``*``).
-
-References
-----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectProtectedBranch`
- + :class:`gitlab.v4.objects.ProjectProtectedBranchManager`
- + :attr:`gitlab.v4.objects.Project.protectedbranches`
-
-* GitLab API: https://docs.gitlab.com/ce/api/protected_branches.html#protected-branches-api
-
-Examples
---------
-
-Get the list of protected branches for a project::
-
- p_branches = project.protectedbranches.list()
-
-Get a single protected branch::
-
- p_branch = project.protectedbranches.get('master')
-
-Create a protected branch::
-
- p_branch = project.protectedbranches.create({
- 'name': '*-stable',
- 'merge_access_level': gitlab.DEVELOPER_ACCESS,
- 'push_access_level': gitlab.MAINTAINER_ACCESS
- })
-
-Create a protected branch with more granular access control::
-
- p_branch = project.protectedbranches.create({
- 'name': '*-stable',
- 'allowed_to_push': [{"user_id": 99}, {"user_id": 98}],
- 'allowed_to_merge': [{"group_id": 653}],
- 'allowed_to_unprotect': [{"access_level": gitlab.MAINTAINER_ACCESS}]
- })
-
-Delete a protected branch::
-
- project.protectedbranches.delete('*-stable')
- # or
- p_branch.delete()
diff --git a/docs/gl_objects/releases.rst b/docs/gl_objects/releases.rst
deleted file mode 100644
index 6077fe9..0000000
--- a/docs/gl_objects/releases.rst
+++ /dev/null
@@ -1,83 +0,0 @@
-########
-Releases
-########
-
-Project releases
-================
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectRelease`
- + :class:`gitlab.v4.objects.ProjectReleaseManager`
- + :attr:`gitlab.v4.objects.Project.releases`
-
-* Gitlab API: https://docs.gitlab.com/ee/api/releases/index.html
-
-Examples
---------
-
-Get a list of releases from a project::
-
- release = project.releases.list()
-
-Get a single release::
-
- release = project.releases.get('v1.2.3')
-
-Edit a release::
-
- release.name = "Demo Release"
- release.description = "release notes go here"
- release.save()
-
-Create a release for a project tag::
-
- release = project.releases.create({'name':'Demo Release', 'tag_name':'v1.2.3', 'description':'release notes go here'})
-
-Delete a release::
-
- # via its tag name from project attributes
- release = project.releases.delete('v1.2.3')
-
- # delete object directly
- release.delete()
-
-Project release links
-=====================
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectReleaseLink`
- + :class:`gitlab.v4.objects.ProjectReleaseLinkManager`
- + :attr:`gitlab.v4.objects.ProjectRelease.links`
-
-* Gitlab API: https://docs.gitlab.com/ee/api/releases/links.html
-
-Examples
---------
-
-Get a list of releases from a project::
-
- links = release.links.list()
-
-Get a single release link::
-
- link = release.links.get(1)
-
-Create a release link for a release::
-
- link = release.links.create({"url": "https://example.com/asset", "name": "asset"})
-
-Delete a release link::
-
- # via its ID from release attributes
- release.links.delete(1)
-
- # delete object directly
- link.delete()
diff --git a/docs/gl_objects/remote_mirrors.rst b/docs/gl_objects/remote_mirrors.rst
deleted file mode 100644
index 9024228..0000000
--- a/docs/gl_objects/remote_mirrors.rst
+++ /dev/null
@@ -1,34 +0,0 @@
-######################
-Project Remote Mirrors
-######################
-
-Remote Mirrors allow you to set up push mirroring for a project.
-
-References
-==========
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectRemoteMirror`
- + :class:`gitlab.v4.objects.ProjectRemoteMirrorManager`
- + :attr:`gitlab.v4.objects.Project.remote_mirrors`
-
-* GitLab API: https://docs.gitlab.com/ce/api/remote_mirrors.html
-
-Examples
---------
-
-Get the list of a project's remote mirrors::
-
- mirrors = project.remote_mirrors.list()
-
-Create (and enable) a remote mirror for a project::
-
- mirror = project.remote_mirrors.create({'url': 'https://gitlab.com/example.git',
- 'enabled': True})
-
-Update an existing remote mirror's attributes::
-
- mirror.enabled = False
- mirror.only_protected_branches = True
- mirror.save()
diff --git a/docs/gl_objects/repositories.rst b/docs/gl_objects/repositories.rst
deleted file mode 100644
index 6622c0c..0000000
--- a/docs/gl_objects/repositories.rst
+++ /dev/null
@@ -1,28 +0,0 @@
-#####################
-Registry Repositories
-#####################
-
-References
-----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectRegistryRepository`
- + :class:`gitlab.v4.objects.ProjectRegistryRepositoryManager`
- + :attr:`gitlab.v4.objects.Project.repositories`
-
-* Gitlab API: https://docs.gitlab.com/ce/api/container_registry.html
-
-Examples
---------
-
-Get the list of container registry repositories associated with the project::
-
- repositories = project.repositories.list()
-
-Delete repository::
-
- project.repositories.delete(id=x)
- # or
- repository = repositories.pop()
- repository.delete()
diff --git a/docs/gl_objects/repository_tags.rst b/docs/gl_objects/repository_tags.rst
deleted file mode 100644
index 2fa807c..0000000
--- a/docs/gl_objects/repository_tags.rst
+++ /dev/null
@@ -1,47 +0,0 @@
-########################
-Registry Repository Tags
-########################
-
-References
-----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectRegistryTag`
- + :class:`gitlab.v4.objects.ProjectRegistryTagManager`
- + :attr:`gitlab.v4.objects.Repository.tags`
-
-* Gitlab API: https://docs.gitlab.com/ce/api/container_registry.html
-
-Examples
---------
-
-Get the list of repository tags in given registry::
-
- repositories = project.repositories.list()
- repository = repositories.pop()
- tags = repository.tags.list()
-
-Get specific tag::
-
- repository.tags.get(id=tag_name)
-
-Delete tag::
-
- repository.tags.delete(id=tag_name)
- # or
- tag = repository.tags.get(id=tag_name)
- tag.delete()
-
-Delete tag in bulk::
-
- repository.tags.delete_in_bulk(keep_n=1)
- # or
- repository.tags.delete_in_bulk(older_than="1m")
- # or
- repository.tags.delete_in_bulk(name_regex="v.+", keep_n=2)
-
-.. note::
-
- Delete in bulk is asynchronous operation and may take a while.
- Refer to: https://docs.gitlab.com/ce/api/container_registry.html#delete-repository-tags-in-bulk
diff --git a/docs/gl_objects/runners.rst b/docs/gl_objects/runners.rst
deleted file mode 100644
index 1919975..0000000
--- a/docs/gl_objects/runners.rst
+++ /dev/null
@@ -1,137 +0,0 @@
-#######
-Runners
-#######
-
-Runners are external processes used to run CI jobs. They are deployed by the
-administrator and registered to the GitLab instance.
-
-Shared runners are available for all projects. Specific runners are enabled for
-a list of projects.
-
-Global runners (admin)
-======================
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.Runner`
- + :class:`gitlab.v4.objects.RunnerManager`
- + :attr:`gitlab.Gitlab.runners`
-
-* GitLab API: https://docs.gitlab.com/ce/api/runners.html
-
-Examples
---------
-
-Use the ``list()`` and ``all()`` methods to list runners.
-
-Both methods accept a ``scope`` parameter to filter the list. Allowed values
-for this parameter are:
-
-* ``active``
-* ``paused``
-* ``online``
-* ``specific`` (``all()`` only)
-* ``shared`` (``all()`` only)
-
-.. note::
-
- The returned objects hold minimal information about the runners. Use the
- ``get()`` method to retrieve detail about a runner.
-
-::
-
- # List owned runners
- runners = gl.runners.list()
- # With a filter
- runners = gl.runners.list(scope='active')
- # List all runners, using a filter
- runners = gl.runners.all(scope='paused')
-
-Get a runner's detail::
-
- runner = gl.runners.get(runner_id)
-
-Register a new runner::
-
- runner = gl.runners.create({'token': secret_token})
-
-Update a runner::
-
- runner = gl.runners.get(runner_id)
- runner.tag_list.append('new_tag')
- runner.save()
-
-Remove a runner::
-
- gl.runners.delete(runner_id)
- # or
- runner.delete()
-
-Verify a registered runner token::
-
- try:
- gl.runners.verify(runner_token)
- print("Valid token")
- except GitlabVerifyError:
- print("Invalid token")
-
-Project/Group runners
-=====================
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectRunner`
- + :class:`gitlab.v4.objects.ProjectRunnerManager`
- + :attr:`gitlab.v4.objects.Project.runners`
- + :class:`gitlab.v4.objects.GroupRunner`
- + :class:`gitlab.v4.objects.GroupRunnerManager`
- + :attr:`gitlab.v4.objects.Group.runners`
-
-* GitLab API: https://docs.gitlab.com/ce/api/runners.html
-
-Examples
---------
-
-List the runners for a project::
-
- runners = project.runners.list()
-
-Enable a specific runner for a project::
-
- p_runner = project.runners.create({'runner_id': runner.id})
-
-Disable a specific runner for a project::
-
- project.runners.delete(runner.id)
-
-Runner jobs
-===========
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.RunnerJob`
- + :class:`gitlab.v4.objects.RunnerJobManager`
- + :attr:`gitlab.v4.objects.Runner.jobs`
-
-* GitLab API: https://docs.gitlab.com/ce/api/runners.html
-
-Examples
---------
-
-List for jobs for a runner::
-
- jobs = runner.jobs.list()
-
-Filter the list using the jobs status::
-
- # status can be 'running', 'success', 'failed' or 'canceled'
- active_jobs = runner.jobs.list(status='running')
diff --git a/docs/gl_objects/search.rst b/docs/gl_objects/search.rst
deleted file mode 100644
index eb8ba80..0000000
--- a/docs/gl_objects/search.rst
+++ /dev/null
@@ -1,77 +0,0 @@
-##########
-Search API
-##########
-
-You can search for resources at the top level, in a project or in a group.
-Searches are based on a scope (issues, merge requests, and so on) and a search
-string. The following constants are provided to represent the possible scopes:
-
-
-* Shared scopes (global, group and project):
-
- + ``gitlab.SEARCH_SCOPE_PROJECTS``: ``projects``
- + ``gitlab.SEARCH_SCOPE_ISSUES``: ``issues``
- + ``gitlab.SEARCH_SCOPE_MERGE_REQUESTS``: ``merge_requests``
- + ``gitlab.SEARCH_SCOPE_MILESTONES``: ``milestones``
- + ``gitlab.SEARCH_SCOPE_WIKI_BLOBS``: ``wiki_blobs``
- + ``gitlab.SEARCH_SCOPE_COMMITS``: ``commits``
- + ``gitlab.SEARCH_SCOPE_BLOBS``: ``blobs``
- + ``gitlab.SEARCH_SCOPE_USERS``: ``users``
-
-
-* specific global scope:
-
- + ``gitlab.SEARCH_SCOPE_GLOBAL_SNIPPET_TITLES``: ``snippet_titles``
-
-
-* specific project scope:
-
- + ``gitlab.SEARCH_SCOPE_PROJECT_NOTES``: ``notes``
-
-
-Reference
----------
-
-* v4 API:
-
- + :attr:`gitlab.Gitlab.search`
- + :attr:`gitlab.v4.objects.Group.search`
- + :attr:`gitlab.v4.objects.Project.search`
-
-* GitLab API: https://docs.gitlab.com/ce/api/search.html
-
-Examples
---------
-
-Search for issues matching a specific string::
-
- # global search
- gl.search(gitlab.SEARCH_SCOPE_ISSUES, 'regression')
-
- # group search
- group = gl.groups.get('mygroup')
- group.search(gitlab.SEARCH_SCOPE_ISSUES, 'regression')
-
- # project search
- project = gl.projects.get('myproject')
- project.search(gitlab.SEARCH_SCOPE_ISSUES, 'regression')
-
-The ``search()`` methods implement the pagination support::
-
- # get lists of 10 items, and start at page 2
- gl.search(gitlab.SEARCH_SCOPE_ISSUES, search_str, page=2, per_page=10)
-
- # get a generator that will automatically make required API calls for
- # pagination
- for item in gl.search(gitlab.SEARCH_SCOPE_ISSUES, search_str, as_list=False):
- do_something(item)
-
-The search API doesn't return objects, but dicts. If you need to act on
-objects, you need to create them explicitly::
-
- for item in gl.search(gitlab.SEARCH_SCOPE_ISSUES, search_str, as_list=False):
- issue_project = gl.projects.get(item['project_id'], lazy=True)
- issue = issue_project.issues.get(item['iid'])
- issue.state = 'closed'
- issue.save()
-
diff --git a/docs/gl_objects/settings.rst b/docs/gl_objects/settings.rst
deleted file mode 100644
index 4accfe0..0000000
--- a/docs/gl_objects/settings.rst
+++ /dev/null
@@ -1,26 +0,0 @@
-########
-Settings
-########
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ApplicationSettings`
- + :class:`gitlab.v4.objects.ApplicationSettingsManager`
- + :attr:`gitlab.Gitlab.settings`
-
-* GitLab API: https://docs.gitlab.com/ce/api/settings.html
-
-Examples
---------
-
-Get the settings::
-
- settings = gl.settings.get()
-
-Update the settings::
-
- settings.signin_enabled = False
- settings.save()
diff --git a/docs/gl_objects/sidekiq.rst b/docs/gl_objects/sidekiq.rst
deleted file mode 100644
index 5f44762..0000000
--- a/docs/gl_objects/sidekiq.rst
+++ /dev/null
@@ -1,23 +0,0 @@
-###############
-Sidekiq metrics
-###############
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.SidekiqManager`
- + :attr:`gitlab.Gitlab.sidekiq`
-
-* GitLab API: https://docs.gitlab.com/ce/api/sidekiq_metrics.html
-
-Examples
---------
-
-.. code-block:: python
-
- gl.sidekiq.queue_metrics()
- gl.sidekiq.process_metrics()
- gl.sidekiq.job_stats()
- gl.sidekiq.compound_metrics()
diff --git a/docs/gl_objects/snippets.rst b/docs/gl_objects/snippets.rst
deleted file mode 100644
index 1bedb07..0000000
--- a/docs/gl_objects/snippets.rst
+++ /dev/null
@@ -1,66 +0,0 @@
-########
-Snippets
-########
-
-Reference
-=========
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.Snippet`
- + :class:`gitlab.v4.objects.SnipptManager`
- + :attr:`gitlab.Gitlab.snippets`
-
-* GitLab API: https://docs.gitlab.com/ce/api/snippets.html
-
-Examples
-========
-
-List snippets owned by the current user::
-
- snippets = gl.snippets.list()
-
-List the public snippets::
-
- public_snippets = gl.snippets.public()
-
-Get a snippet::
-
- snippet = gl.snippets.get(snippet_id)
- # get the content
- content = snippet.content()
-
-.. warning::
-
- Blobs are entirely stored in memory unless you use the streaming feature.
- See :ref:`the artifacts example <streaming_example>`.
-
-
-Create a snippet::
-
- snippet = gl.snippets.create({'title': 'snippet1',
- 'file_name': 'snippet1.py',
- 'content': open('snippet1.py').read()})
-
-Update the snippet attributes::
-
- snippet.visibility_level = gitlab.VISIBILITY_PUBLIC
- snippet.save()
-
-To update a snippet code you need to create a ``ProjectSnippet`` object::
-
- snippet = gl.snippets.get(snippet_id)
- project = gl.projects.get(snippet.projec_id, lazy=True)
- editable_snippet = project.snippets.get(snippet.id)
- editable_snippet.code = new_snippet_content
- editable_snippet.save()
-
-Delete a snippet::
-
- gl.snippets.delete(snippet_id)
- # or
- snippet.delete()
-
-Get user agent detail (admin only)::
-
- detail = snippet.user_agent_detail()
diff --git a/docs/gl_objects/system_hooks.rst b/docs/gl_objects/system_hooks.rst
deleted file mode 100644
index 6203168..0000000
--- a/docs/gl_objects/system_hooks.rst
+++ /dev/null
@@ -1,35 +0,0 @@
-############
-System hooks
-############
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.Hook`
- + :class:`gitlab.v4.objects.HookManager`
- + :attr:`gitlab.Gitlab.hooks`
-
-* GitLab API: https://docs.gitlab.com/ce/api/system_hooks.html
-
-Examples
---------
-
-List the system hooks::
-
- hooks = gl.hooks.list()
-
-Create a system hook::
-
- gl.hooks.get(hook_id)
-
-Test a system hook. The returned object is not usable (it misses the hook ID)::
-
- hook = gl.hooks.create({'url': 'http://your.target.url'})
-
-Delete a system hook::
-
- gl.hooks.delete(hook_id)
- # or
- hook.delete()
diff --git a/docs/gl_objects/templates.rst b/docs/gl_objects/templates.rst
deleted file mode 100644
index f939e5f..0000000
--- a/docs/gl_objects/templates.rst
+++ /dev/null
@@ -1,114 +0,0 @@
-#########
-Templates
-#########
-
-You can request templates for different type of files:
-
-* License files
-* .gitignore files
-* GitLab CI configuration files
-* Dockerfiles
-
-License templates
-=================
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.License`
- + :class:`gitlab.v4.objects.LicenseManager`
- + :attr:`gitlab.Gitlab.licenses`
-
-* GitLab API: https://docs.gitlab.com/ce/api/templates/licenses.html
-
-Examples
---------
-
-List known license templates::
-
- licenses = gl.licenses.list()
-
-Generate a license content for a project::
-
- license = gl.licenses.get('apache-2.0', project='foobar', fullname='John Doe')
- print(license.content)
-
-.gitignore templates
-====================
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.Gitignore`
- + :class:`gitlab.v4.objects.GitignoreManager`
- + :attr:`gitlab.Gitlab.gitignores`
-
-* GitLab API: https://docs.gitlab.com/ce/api/templates/gitignores.html
-
-Examples
---------
-
-List known gitignore templates::
-
- gitignores = gl.gitignores.list()
-
-Get a gitignore template::
-
- gitignore = gl.gitignores.get('Python')
- print(gitignore.content)
-
-GitLab CI templates
-===================
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.Gitlabciyml`
- + :class:`gitlab.v4.objects.GitlabciymlManager`
- + :attr:`gitlab.Gitlab.gitlabciymls`
-
-* GitLab API: https://docs.gitlab.com/ce/api/templates/gitlab_ci_ymls.html
-
-Examples
---------
-
-List known GitLab CI templates::
-
- gitlabciymls = gl.gitlabciymls.list()
-
-Get a GitLab CI template::
-
- gitlabciyml = gl.gitlabciymls.get('Pelican')
- print(gitlabciyml.content)
-
-Dockerfile templates
-====================
-
-Reference
----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.Dockerfile`
- + :class:`gitlab.v4.objects.DockerfileManager`
- + :attr:`gitlab.Gitlab.gitlabciymls`
-
-* GitLab API: Not documented.
-
-Examples
---------
-
-List known Dockerfile templates::
-
- dockerfiles = gl.dockerfiles.list()
-
-Get a Dockerfile template::
-
- dockerfile = gl.dockerfiles.get('Python')
- print(dockerfile.content)
diff --git a/docs/gl_objects/todos.rst b/docs/gl_objects/todos.rst
deleted file mode 100644
index 24a14c2..0000000
--- a/docs/gl_objects/todos.rst
+++ /dev/null
@@ -1,44 +0,0 @@
-#####
-Todos
-#####
-
-Reference
----------
-
-* v4 API:
-
- + :class:`~gitlab.objects.Todo`
- + :class:`~gitlab.objects.TodoManager`
- + :attr:`gitlab.Gitlab.todos`
-
-* GitLab API: https://docs.gitlab.com/ce/api/todos.html
-
-Examples
---------
-
-List active todos::
-
- todos = gl.todos.list()
-
-You can filter the list using the following parameters:
-
-* ``action``: can be ``assigned``, ``mentioned``, ``build_failed``, ``marked``,
- or ``approval_required``
-* ``author_id``
-* ``project_id``
-* ``state``: can be ``pending`` or ``done``
-* ``type``: can be ``Issue`` or ``MergeRequest``
-
-For example::
-
- todos = gl.todos.list(project_id=1)
- todos = gl.todos.list(state='done', type='Issue')
-
-Mark a todo as done::
-
- todos = gl.todos.list(project_id=1)
- todos[0].mark_as_done()
-
-Mark all the todos as done::
-
- gl.todos.mark_all_as_done()
diff --git a/docs/gl_objects/users.rst b/docs/gl_objects/users.rst
deleted file mode 100644
index dd6db6a..0000000
--- a/docs/gl_objects/users.rst
+++ /dev/null
@@ -1,404 +0,0 @@
-######################
-Users and current user
-######################
-
-The Gitlab API exposes user-related method that can be manipulated by admins
-only.
-
-The currently logged-in user is also exposed.
-
-Users
-=====
-
-References
-----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.User`
- + :class:`gitlab.v4.objects.UserManager`
- + :attr:`gitlab.Gitlab.users`
-
-* GitLab API: https://docs.gitlab.com/ce/api/users.html
-
-Examples
---------
-
-Get the list of users::
-
- users = gl.users.list()
-
-Search users whose username match a given string::
-
- users = gl.users.list(search='foo')
-
-Get a single user::
-
- # by ID
- user = gl.users.get(user_id)
- # by username
- user = gl.users.list(username='root')[0]
-
-Create a user::
-
- user = gl.users.create({'email': 'john@doe.com',
- 'password': 's3cur3s3cr3T',
- 'username': 'jdoe',
- 'name': 'John Doe'})
-
-Update a user::
-
- user.name = 'Real Name'
- user.save()
-
-Delete a user::
-
- gl.users.delete(user_id)
- # or
- user.delete()
-
-Block/Unblock a user::
-
- user.block()
- user.unblock()
-
-Activate/Deactivate a user::
-
- user.activate()
- user.deactivate()
-
-Follow/Unfollow a user::
-
- user.follow()
- user.unfollow()
-
-Set the avatar image for a user::
-
- # the avatar image can be passed as data (content of the file) or as a file
- # object opened in binary mode
- user.avatar = open('path/to/file.png', 'rb')
- user.save()
-
-Set an external identity for a user::
-
- user.provider = 'oauth2_generic'
- user.extern_uid = '3'
- user.save()
-
-Delete an external identity by provider name::
-
- user.identityproviders.delete('oauth2_generic')
-
-Get the followers of a user
-
- user.followers_users.list()
-
-Get the followings of a user
-
- user.following_users.list()
-
-
-User custom attributes
-======================
-
-References
-----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.UserCustomAttribute`
- + :class:`gitlab.v4.objects.UserCustomAttributeManager`
- + :attr:`gitlab.v4.objects.User.customattributes`
-
-* GitLab API: https://docs.gitlab.com/ce/api/custom_attributes.html
-
-Examples
---------
-
-List custom attributes for a user::
-
- attrs = user.customattributes.list()
-
-Get a custom attribute for a user::
-
- attr = user.customattributes.get(attr_key)
-
-Set (create or update) a custom attribute for a user::
-
- attr = user.customattributes.set(attr_key, attr_value)
-
-Delete a custom attribute for a user::
-
- attr.delete()
- # or
- user.customattributes.delete(attr_key)
-
-Search users by custom attribute::
-
- user.customattributes.set('role', 'QA')
- gl.users.list(custom_attributes={'role': 'QA'})
-
-User impersonation tokens
-=========================
-
-References
-----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.UserImpersonationToken`
- + :class:`gitlab.v4.objects.UserImpersonationTokenManager`
- + :attr:`gitlab.v4.objects.User.impersonationtokens`
-
-* GitLab API: https://docs.gitlab.com/ce/api/users.html#get-all-impersonation-tokens-of-a-user
-
-List impersonation tokens for a user::
-
- i_t = user.impersonationtokens.list(state='active')
- i_t = user.impersonationtokens.list(state='inactive')
-
-Get an impersonation token for a user::
-
- i_t = user.impersonationtokens.get(i_t_id)
-
-Create and use an impersonation token for a user::
-
- i_t = user.impersonationtokens.create({'name': 'token1', 'scopes': ['api']})
- # use the token to create a new gitlab connection
- user_gl = gitlab.Gitlab(gitlab_url, private_token=i_t.token)
-
-Revoke (delete) an impersonation token for a user::
-
- i_t.delete()
-
-
-User memberships
-=========================
-
-References
-----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.UserMembership`
- + :class:`gitlab.v4.objects.UserMembershipManager`
- + :attr:`gitlab.v4.objects.User.memberships`
-
-* GitLab API: https://docs.gitlab.com/ee/api/users.html#user-memberships-admin-only
-
-List direct memberships for a user::
-
- memberships = user.memberships.list()
-
-List only direct project memberships::
-
- memberships = user.memberships.list(type='Project')
-
-List only direct group memberships::
-
- memberships = user.memberships.list(type='Namespace')
-
-Current User
-============
-
-References
-----------
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.CurrentUser`
- + :class:`gitlab.v4.objects.CurrentUserManager`
- + :attr:`gitlab.Gitlab.user`
-
-* GitLab API: https://docs.gitlab.com/ce/api/users.html
-
-Examples
---------
-
-Get the current user::
-
- gl.auth()
- current_user = gl.user
-
-GPG keys
-========
-
-References
-----------
-
-You can manipulate GPG keys for the current user and for the other users if you
-are admin.
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.CurrentUserGPGKey`
- + :class:`gitlab.v4.objects.CurrentUserGPGKeyManager`
- + :attr:`gitlab.v4.objects.CurrentUser.gpgkeys`
- + :class:`gitlab.v4.objects.UserGPGKey`
- + :class:`gitlab.v4.objects.UserGPGKeyManager`
- + :attr:`gitlab.v4.objects.User.gpgkeys`
-
-* GitLab API: https://docs.gitlab.com/ce/api/users.html#list-all-gpg-keys
-
-Examples
---------
-
-List GPG keys for a user::
-
- gpgkeys = user.gpgkeys.list()
-
-Get a GPG gpgkey for a user::
-
- gpgkey = user.gpgkeys.get(key_id)
-
-Create a GPG gpgkey for a user::
-
- # get the key with `gpg --export -a GPG_KEY_ID`
- k = user.gpgkeys.create({'key': public_key_content})
-
-Delete a GPG gpgkey for a user::
-
- user.gpgkeys.delete(key_id)
- # or
- gpgkey.delete()
-
-SSH keys
-========
-
-References
-----------
-
-You can manipulate SSH keys for the current user and for the other users if you
-are admin.
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.CurrentUserKey`
- + :class:`gitlab.v4.objects.CurrentUserKeyManager`
- + :attr:`gitlab.v4.objects.CurrentUser.keys`
- + :class:`gitlab.v4.objects.UserKey`
- + :class:`gitlab.v4.objects.UserKeyManager`
- + :attr:`gitlab.v4.objects.User.keys`
-
-* GitLab API: https://docs.gitlab.com/ce/api/users.html#list-ssh-keys
-
-Examples
---------
-
-List SSH keys for a user::
-
- keys = user.keys.list()
-
-Create an SSH key for a user::
-
- k = user.keys.create({'title': 'my_key',
- 'key': open('/home/me/.ssh/id_rsa.pub').read()})
-
-Delete an SSH key for a user::
-
- user.keys.delete(key_id)
- # or
- key.delete()
-
-Status
-======
-
-References
-----------
-
-You can manipulate the status for the current user and you can read the status of other users.
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.CurrentUserStatus`
- + :class:`gitlab.v4.objects.CurrentUserStatusManager`
- + :attr:`gitlab.v4.objects.CurrentUser.status`
- + :class:`gitlab.v4.objects.UserStatus`
- + :class:`gitlab.v4.objects.UserStatusManager`
- + :attr:`gitlab.v4.objects.User.status`
-
-* GitLab API: https://docs.gitlab.com/ce/api/users.html#user-status
-
-Examples
---------
-
-Get current user status::
-
- status = user.status.get()
-
-Update the status for the current user::
-
- status = user.status.get()
- status.message = "message"
- status.emoji = "thumbsup"
- status.save()
-
-Get the status of other users::
-
- gl.users.get(1).status.get()
-
-Emails
-======
-
-References
-----------
-
-You can manipulate emails for the current user and for the other users if you
-are admin.
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.CurrentUserEmail`
- + :class:`gitlab.v4.objects.CurrentUserEmailManager`
- + :attr:`gitlab.v4.objects.CurrentUser.emails`
- + :class:`gitlab.v4.objects.UserEmail`
- + :class:`gitlab.v4.objects.UserEmailManager`
- + :attr:`gitlab.v4.objects.User.emails`
-
-* GitLab API: https://docs.gitlab.com/ce/api/users.html#list-emails
-
-Examples
---------
-
-List emails for a user::
-
- emails = user.emails.list()
-
-Get an email for a user::
-
- email = user.emails.get(email_id)
-
-Create an email for a user::
-
- k = user.emails.create({'email': 'foo@bar.com'})
-
-Delete an email for a user::
-
- user.emails.delete(email_id)
- # or
- email.delete()
-
-Users activities
-================
-
-References
-----------
-
-* admin only
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.UserActivities`
- + :class:`gitlab.v4.objects.UserActivitiesManager`
- + :attr:`gitlab.Gitlab.user_activities`
-
-* GitLab API: https://docs.gitlab.com/ce/api/users.html#get-user-activities-admin-only
-
-Examples
---------
-
-Get the users activities::
-
- activities = gl.user_activities.list(
- query_parameters={'from': '2018-07-01'},
- all=True, as_list=False)
diff --git a/docs/gl_objects/variables.rst b/docs/gl_objects/variables.rst
deleted file mode 100644
index f679925..0000000
--- a/docs/gl_objects/variables.rst
+++ /dev/null
@@ -1,104 +0,0 @@
-###############
-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()
- # or
- project.variables.update("key1", {"value": "new_value"})
-
-Remove a variable::
-
- project.variables.delete('key_name')
- group.variables.delete('key_name')
- # or
- var.delete()
diff --git a/docs/gl_objects/wikis.rst b/docs/gl_objects/wikis.rst
deleted file mode 100644
index e98b9d4..0000000
--- a/docs/gl_objects/wikis.rst
+++ /dev/null
@@ -1,56 +0,0 @@
-##########
-Wiki pages
-##########
-
-
-References
-==========
-
-* v4 API:
-
- + :class:`gitlab.v4.objects.ProjectWiki`
- + :class:`gitlab.v4.objects.ProjectWikiManager`
- + :attr:`gitlab.v4.objects.Project.wikis`
- + :class:`gitlab.v4.objects.GroupWiki`
- + :class:`gitlab.v4.objects.GroupWikiManager`
- + :attr:`gitlab.v4.objects.Group.wikis`
-
-* GitLab API for Projects: https://docs.gitlab.com/ce/api/wikis.html
-* GitLab API for Groups: https://docs.gitlab.com/ee/api/group_wikis.html
-
-Examples
---------
-
-Get the list of wiki pages for a project. These do not contain the contents of the wiki page. You will need to call get(slug) to retrieve the content by accessing the content attribute::
-
- pages = project.wikis.list()
-
-Get the list of wiki pages for a group. These do not contain the contents of the wiki page. You will need to call get(slug) to retrieve the content by accessing the content attribute::
-
- pages = group.wikis.list()
-
-Get a single wiki page for a project::
-
- page = project.wikis.get(page_slug)
-
-Get a single wiki page for a group::
-
- page = group.wikis.get(page_slug)
-
-Get the contents of a wiki page::
-
- print(page.content)
-
-Create a wiki page on a project level::
-
- page = project.wikis.create({'title': 'Wiki Page 1',
- 'content': open(a_file).read()})
-
-Update a wiki page::
-
- page.content = 'My new content'
- page.save()
-
-Delete a wiki page::
-
- page.delete()
diff --git a/docs/index.rst b/docs/index.rst
deleted file mode 100644
index 3f8672b..0000000
--- a/docs/index.rst
+++ /dev/null
@@ -1,30 +0,0 @@
-.. python-gitlab documentation master file, created by
- sphinx-quickstart on Mon Dec 8 15:17:39 2014.
- You can adapt this file completely to your liking, but it should at least
- contain the root `toctree` directive.
-
-Welcome to python-gitlab's documentation!
-=========================================
-
-Contents:
-
-.. toctree::
- :maxdepth: 2
-
- install
- cli-usage
- api-usage
- faq
- api-objects
- api/gitlab
- cli-objects
- changelog
- release-notes
-
-
-Indices and tables
-==================
-
-* :ref:`genindex`
-* :ref:`modindex`
-* :ref:`search`
diff --git a/docs/install.rst b/docs/install.rst
deleted file mode 100644
index acd2528..0000000
--- a/docs/install.rst
+++ /dev/null
@@ -1,26 +0,0 @@
-############
-Installation
-############
-
-``python-gitlab`` is compatible with Python 3.6+.
-
-Use :command:`pip` to install the latest stable version of ``python-gitlab``:
-
-.. code-block:: console
-
- $ pip install --upgrade python-gitlab
-
-The current development version is available on both `GitHub.com
-<https://github.com/python-gitlab/python-gitlab>`__ and `GitLab.com
-<https://gitlab.com/python-gitlab/python-gitlab>`__, and can be
-installed directly from the git repository:
-
-.. code-block:: console
-
- $ pip install git+https://github.com/python-gitlab/python-gitlab.git
-
-From GitLab:
-
-.. code-block:: console
-
- $ pip install git+https://gitlab.com/python-gitlab/python-gitlab.git
diff --git a/docs/make.bat b/docs/make.bat
deleted file mode 100644
index 7c29850..0000000
--- a/docs/make.bat
+++ /dev/null
@@ -1,242 +0,0 @@
-@ECHO OFF
-
-REM Command file for Sphinx documentation
-
-if "%SPHINXBUILD%" == "" (
- set SPHINXBUILD=sphinx-build
-)
-set BUILDDIR=_build
-set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
-set I18NSPHINXOPTS=%SPHINXOPTS% .
-if NOT "%PAPER%" == "" (
- set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
- set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
-)
-
-if "%1" == "" goto help
-
-if "%1" == "help" (
- :help
- echo.Please use `make ^<target^>` where ^<target^> is one of
- echo. html to make standalone HTML files
- echo. dirhtml to make HTML files named index.html in directories
- echo. singlehtml to make a single large HTML file
- echo. pickle to make pickle files
- echo. json to make JSON files
- echo. htmlhelp to make HTML files and a HTML help project
- echo. qthelp to make HTML files and a qthelp project
- echo. devhelp to make HTML files and a Devhelp project
- echo. epub to make an epub
- echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
- echo. text to make text files
- echo. man to make manual pages
- echo. texinfo to make Texinfo files
- echo. gettext to make PO message catalogs
- echo. changes to make an overview over all changed/added/deprecated items
- echo. xml to make Docutils-native XML files
- echo. pseudoxml to make pseudoxml-XML files for display purposes
- echo. linkcheck to check all external links for integrity
- echo. doctest to run all doctests embedded in the documentation if enabled
- goto end
-)
-
-if "%1" == "clean" (
- for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
- del /q /s %BUILDDIR%\*
- goto end
-)
-
-
-%SPHINXBUILD% 2> nul
-if errorlevel 9009 (
- echo.
- echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
- echo.installed, then set the SPHINXBUILD environment variable to point
- echo.to the full path of the 'sphinx-build' executable. Alternatively you
- echo.may add the Sphinx directory to PATH.
- echo.
- echo.If you don't have Sphinx installed, grab it from
- echo.http://sphinx-doc.org/
- exit /b 1
-)
-
-if "%1" == "html" (
- %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/html.
- goto end
-)
-
-if "%1" == "dirhtml" (
- %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
- goto end
-)
-
-if "%1" == "singlehtml" (
- %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
- goto end
-)
-
-if "%1" == "pickle" (
- %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can process the pickle files.
- goto end
-)
-
-if "%1" == "json" (
- %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can process the JSON files.
- goto end
-)
-
-if "%1" == "htmlhelp" (
- %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can run HTML Help Workshop with the ^
-.hhp project file in %BUILDDIR%/htmlhelp.
- goto end
-)
-
-if "%1" == "qthelp" (
- %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can run "qcollectiongenerator" with the ^
-.qhcp project file in %BUILDDIR%/qthelp, like this:
- echo.^> qcollectiongenerator %BUILDDIR%\qthelp\python-gitlab.qhcp
- echo.To view the help file:
- echo.^> assistant -collectionFile %BUILDDIR%\qthelp\python-gitlab.ghc
- goto end
-)
-
-if "%1" == "devhelp" (
- %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished.
- goto end
-)
-
-if "%1" == "epub" (
- %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The epub file is in %BUILDDIR%/epub.
- goto end
-)
-
-if "%1" == "latex" (
- %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
- goto end
-)
-
-if "%1" == "latexpdf" (
- %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
- cd %BUILDDIR%/latex
- make all-pdf
- cd %BUILDDIR%/..
- echo.
- echo.Build finished; the PDF files are in %BUILDDIR%/latex.
- goto end
-)
-
-if "%1" == "latexpdfja" (
- %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
- cd %BUILDDIR%/latex
- make all-pdf-ja
- cd %BUILDDIR%/..
- echo.
- echo.Build finished; the PDF files are in %BUILDDIR%/latex.
- goto end
-)
-
-if "%1" == "text" (
- %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The text files are in %BUILDDIR%/text.
- goto end
-)
-
-if "%1" == "man" (
- %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The manual pages are in %BUILDDIR%/man.
- goto end
-)
-
-if "%1" == "texinfo" (
- %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
- goto end
-)
-
-if "%1" == "gettext" (
- %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
- goto end
-)
-
-if "%1" == "changes" (
- %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
- if errorlevel 1 exit /b 1
- echo.
- echo.The overview file is in %BUILDDIR%/changes.
- goto end
-)
-
-if "%1" == "linkcheck" (
- %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
- if errorlevel 1 exit /b 1
- echo.
- echo.Link check complete; look for any errors in the above output ^
-or in %BUILDDIR%/linkcheck/output.txt.
- goto end
-)
-
-if "%1" == "doctest" (
- %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
- if errorlevel 1 exit /b 1
- echo.
- echo.Testing of doctests in the sources finished, look at the ^
-results in %BUILDDIR%/doctest/output.txt.
- goto end
-)
-
-if "%1" == "xml" (
- %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The XML files are in %BUILDDIR%/xml.
- goto end
-)
-
-if "%1" == "pseudoxml" (
- %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
- goto end
-)
-
-:end
diff --git a/docs/release-notes.rst b/docs/release-notes.rst
deleted file mode 100644
index 927d2c4..0000000
--- a/docs/release-notes.rst
+++ /dev/null
@@ -1,221 +0,0 @@
-#############
-Release notes
-#############
-
-Prior to version 2.0.0 and GitHub Releases, a summary of changes was maintained
-in release notes. They are available below for historical purposes.
-For the list of current releases, including breaking changes, please see the changelog.
-
-Changes from 1.8 to 1.9
-=======================
-
-* ``ProjectMemberManager.all()`` and ``GroupMemberManager.all()`` now return a
- list of ``ProjectMember`` and ``GroupMember`` objects respectively, instead
- of a list of dicts.
-
-Changes from 1.7 to 1.8
-=======================
-
-* You can now use the ``query_parameters`` argument in method calls to define
- arguments to send to the GitLab server. This allows to avoid conflicts
- between python-gitlab and GitLab server variables, and allows to use the
- python reserved keywords as GitLab arguments.
-
- The following examples make the same GitLab request with the 2 syntaxes::
-
- projects = gl.projects.list(owned=True, starred=True)
- projects = gl.projects.list(query_parameters={'owned': True, 'starred': True})
-
- The following example only works with the new parameter::
-
- activities = gl.user_activities.list(
- query_parameters={'from': '2019-01-01'},
- all=True)
-
-* Additionally the ``all`` paremeter is not sent to the GitLab anymore.
-
-Changes from 1.5 to 1.6
-=======================
-
-* When python-gitlab detects HTTP redirections from http to https it will raise
- a RedirectionError instead of a cryptic error.
-
- Make sure to use an ``https://`` protocol in your GitLab URL parameter if the
- server requires it.
-
-Changes from 1.4 to 1.5
-=======================
-
-* APIv3 support has been removed. Use the 1.4 release/branch if you need v3
- support.
-* GitLab EE features are now supported: Geo nodes, issue links, LDAP groups,
- project/group boards, project mirror pulling, project push rules, EE license
- configuration, epics.
-* The ``GetFromListMixin`` class has been removed. The ``get()`` method is not
- available anymore for the following managers:
-
- - UserKeyManager
- - DeployKeyManager
- - GroupAccessRequestManager
- - GroupIssueManager
- - GroupProjectManager
- - GroupSubgroupManager
- - IssueManager
- - ProjectCommitStatusManager
- - ProjectEnvironmentManager
- - ProjectLabelManager
- - ProjectPipelineJobManager
- - ProjectAccessRequestManager
- - TodoManager
-
-* ``ProjectPipelineJob`` do not heritate from ``ProjectJob`` anymore and thus
- can only be listed.
-
-Changes from 1.3 to 1.4
-=======================
-
-* 1.4 is the last release supporting the v3 API, and the related code will be
- removed in the 1.5 version.
-
- If you are using a Gitlab server version that does not support the v4 API you
- can:
-
- * upgrade the server (recommended)
- * make sure to use version 1.4 of python-gitlab (``pip install
- python-gitlab==1.4``)
-
- See also the `Switching to GitLab API v4 documentation
- <http://python-gitlab.readthedocs.io/en/master/switching-to-v4.html>`__.
-* python-gitlab now handles the server rate limiting feature. It will pause for
- the required time when reaching the limit (`documentation
- <http://python-gitlab.readthedocs.io/en/master/api-usage.html#rate-limits>`__)
-* The ``GetFromListMixin.get()`` method is deprecated and will be removed in
- the next python-gitlab version. The goal of this mixin/method is to provide a
- way to get an object by looping through a list for GitLab objects that don't
- support the GET method. The method `is broken
- <https://github.com/python-gitlab/python-gitlab/issues/499>`__ and conflicts
- with the GET method now supported by some GitLab objects.
-
- You can implement your own method with something like:
-
- .. code-block:: python
-
- def get_from_list(self, id):
- for obj in self.list(as_list=False):
- if obj.get_id() == id:
- return obj
-
-* The ``GroupMemberManager``, ``NamespaceManager`` and ``ProjectBoardManager``
- managers now use the GET API from GitLab instead of the
- ``GetFromListMixin.get()`` method.
-
-
-Changes from 1.2 to 1.3
-=======================
-
-* ``gitlab.Gitlab`` objects can be used as context managers in a ``with``
- block.
-
-Changes from 1.1 to 1.2
-=======================
-
-* python-gitlab now respects the ``*_proxy``, ``REQUESTS_CA_BUNDLE`` and
- ``CURL_CA_BUNDLE`` environment variables (#352)
-* The following deprecated methods and objects have been removed:
-
- * gitlab.v3.object ``Key`` and ``KeyManager`` objects: use ``DeployKey`` and
- ``DeployKeyManager`` instead
- * gitlab.v3.objects.Project ``archive_`` and ``unarchive_`` methods
- * gitlab.Gitlab ``credentials_auth``, ``token_auth``, ``set_url``,
- ``set_token`` and ``set_credentials`` methods. Once a Gitlab object has been
- created its URL and authentication information cannot be updated: create a
- new Gitlab object if you need to use new information
-* The ``todo()`` method raises a ``GitlabTodoError`` exception on error
-
-Changes from 1.0.2 to 1.1
-=========================
-
-* The ``ProjectUser`` class doesn't inherit from ``User`` anymore, and the
- ``GroupProject`` class doesn't inherit from ``Project`` anymore. The Gitlab
- API doesn't provide the same set of features for these objects, so
- python-gitlab objects shouldn't try to workaround that.
-
- You can create ``User`` or ``Project`` objects from ``ProjectUser`` and
- ``GroupProject`` objects using the ``id`` attribute:
-
- .. code-block:: python
-
- for gr_project in group.projects.list():
- # lazy object creation avoids a Gitlab API request
- project = gl.projects.get(gr_project.id, lazy=True)
- project.default_branch = 'develop'
- project.save()
-
-Changes from 0.21 to 1.0.0
-==========================
-
-1.0.0 brings a stable python-gitlab API for the v4 Gitlab API. v3 is still used
-by default.
-
-v4 is mostly compatible with the v3, but some important changes have been
-introduced. Make sure to read `Switching to GitLab API v4
-<http://python-gitlab.readthedocs.io/en/master/switching-to-v4.html>`_.
-
-The development focus will be v4 from now on. v3 has been deprecated by GitLab
-and will disappear from python-gitlab at some point.
-
-Changes from 0.20 to 0.21
-=========================
-
-* Initial support for the v4 API (experimental)
-
- The support for v4 is stable enough to be tested, but some features might be
- broken. Please report issues to
- https://github.com/python-gitlab/python-gitlab/issues/
-
- Be aware that the python-gitlab API for v4 objects might change in the next
- releases.
-
- .. warning::
-
- Consider defining explicitly which API version you want to use in the
- configuration files or in your ``gitlab.Gitlab`` instances. The default
- will change from v3 to v4 soon.
-
-* Several methods have been deprecated in the ``gitlab.Gitlab`` class:
-
- + ``credentials_auth()`` is deprecated and will be removed. Call ``auth()``.
- + ``token_auth()`` is deprecated and will be removed. Call ``auth()``.
- + ``set_url()`` is deprecated, create a new ``Gitlab`` instance if you need
- an updated URL.
- + ``set_token()`` is deprecated, use the ``private_token`` argument of the
- ``Gitlab`` constructor.
- + ``set_credentials()`` is deprecated, use the ``email`` and ``password``
- arguments of the ``Gitlab`` constructor.
-
-* The service listing method (``ProjectServiceManager.list()``) now returns a
- python list instead of a JSON string.
-
-Changes from 0.19 to 0.20
-=========================
-
-* The ``projects`` attribute of ``Group`` objects is not a list of ``Project``
- objects anymore. It is a Manager object giving access to ``GroupProject``
- objects. To get the list of projects use:
-
- .. code-block:: python
-
- group.projects.list()
-
- Documentation:
- http://python-gitlab.readthedocs.io/en/stable/gl_objects/groups.html#examples
-
- Related issue: https://github.com/python-gitlab/python-gitlab/issues/209
-
-* The ``Key`` objects are deprecated in favor of the new ``DeployKey`` objects.
- They are exactly the same but the name makes more sense.
-
- Documentation:
- http://python-gitlab.readthedocs.io/en/stable/gl_objects/deploy_keys.html
-
- Related issue: https://github.com/python-gitlab/python-gitlab/issues/212
diff --git a/gitlab/__init__.py b/gitlab/__init__.py
deleted file mode 100644
index 7b79f22..0000000
--- a/gitlab/__init__.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013-2017 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-"""Wrapper for the GitLab API."""
-
-import warnings
-
-import gitlab.config # noqa: F401
-from gitlab.__version__ import ( # noqa: F401
- __author__,
- __copyright__,
- __email__,
- __license__,
- __title__,
- __version__,
-)
-from gitlab.client import Gitlab, GitlabList # noqa: F401
-from gitlab.const import * # noqa: F401,F403
-from gitlab.exceptions import * # noqa: F401,F403
-
-warnings.filterwarnings("default", category=DeprecationWarning, module="^gitlab")
diff --git a/gitlab/__main__.py b/gitlab/__main__.py
deleted file mode 100644
index e1a914c..0000000
--- a/gitlab/__main__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import gitlab.cli
-
-if __name__ == "__main__":
- gitlab.cli.main()
diff --git a/gitlab/__version__.py b/gitlab/__version__.py
deleted file mode 100644
index d7e8431..0000000
--- a/gitlab/__version__.py
+++ /dev/null
@@ -1,6 +0,0 @@
-__author__ = "Gauvain Pocentek, python-gitlab team"
-__copyright__ = "Copyright 2013-2019 Gauvain Pocentek, 2019-2021 python-gitlab team"
-__email__ = "gauvainpocentek@gmail.com"
-__license__ = "LGPL3"
-__title__ = "python-gitlab"
-__version__ = "2.10.1"
diff --git a/gitlab/base.py b/gitlab/base.py
deleted file mode 100644
index a4a1ef9..0000000
--- a/gitlab/base.py
+++ /dev/null
@@ -1,331 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013-2017 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-import importlib
-from types import ModuleType
-from typing import Any, Dict, Iterable, NamedTuple, Optional, Tuple, Type
-
-from gitlab import types as g_types
-from gitlab.exceptions import GitlabParsingError
-
-from .client import Gitlab, GitlabList
-
-__all__ = [
- "RequiredOptional",
- "RESTObject",
- "RESTObjectList",
- "RESTManager",
-]
-
-
-class RESTObject(object):
- """Represents an object built from server data.
-
- It holds the attributes know from the server, and the updated attributes in
- another. This allows smart updates, if the object allows it.
-
- You can redefine ``_id_attr`` in child classes to specify which attribute
- must be used as uniq ID. ``None`` means that the object can be updated
- without ID in the url.
- """
-
- _id_attr: Optional[str] = "id"
- _attrs: Dict[str, Any]
- _module: ModuleType
- _parent_attrs: Dict[str, Any]
- _short_print_attr: Optional[str] = None
- _updated_attrs: Dict[str, Any]
- manager: "RESTManager"
-
- def __init__(self, manager: "RESTManager", attrs: Dict[str, Any]) -> None:
- if not isinstance(attrs, dict):
- raise GitlabParsingError(
- "Attempted to initialize RESTObject with a non-dictionary value: "
- "{!r}\nThis likely indicates an incorrect or malformed server "
- "response.".format(attrs)
- )
- self.__dict__.update(
- {
- "manager": manager,
- "_attrs": attrs,
- "_updated_attrs": {},
- "_module": importlib.import_module(self.__module__),
- }
- )
- self.__dict__["_parent_attrs"] = self.manager.parent_attrs
- self._create_managers()
-
- def __getstate__(self) -> Dict[str, Any]:
- state = self.__dict__.copy()
- module = state.pop("_module")
- state["_module_name"] = module.__name__
- return state
-
- def __setstate__(self, state: Dict[str, Any]) -> None:
- module_name = state.pop("_module_name")
- self.__dict__.update(state)
- self.__dict__["_module"] = importlib.import_module(module_name)
-
- def __getattr__(self, name: str) -> Any:
- try:
- return self.__dict__["_updated_attrs"][name]
- except KeyError:
- try:
- value = self.__dict__["_attrs"][name]
-
- # If the value is a list, we copy it in the _updated_attrs dict
- # because we are not able to detect changes made on the object
- # (append, insert, pop, ...). Without forcing the attr
- # creation __setattr__ is never called, the list never ends up
- # in the _updated_attrs dict, and the update() and save()
- # method never push the new data to the server.
- # See https://github.com/python-gitlab/python-gitlab/issues/306
- #
- # note: _parent_attrs will only store simple values (int) so we
- # don't make this check in the next except block.
- if isinstance(value, list):
- self.__dict__["_updated_attrs"][name] = value[:]
- return self.__dict__["_updated_attrs"][name]
-
- return value
-
- except KeyError:
- try:
- return self.__dict__["_parent_attrs"][name]
- except KeyError:
- raise AttributeError(name)
-
- def __setattr__(self, name: str, value: Any) -> None:
- self.__dict__["_updated_attrs"][name] = value
-
- def __str__(self) -> str:
- data = self._attrs.copy()
- data.update(self._updated_attrs)
- return "%s => %s" % (type(self), data)
-
- def __repr__(self) -> str:
- if self._id_attr:
- return "<%s %s:%s>" % (
- self.__class__.__name__,
- self._id_attr,
- self.get_id(),
- )
- else:
- return "<%s>" % self.__class__.__name__
-
- def __eq__(self, other: object) -> bool:
- if not isinstance(other, RESTObject):
- return NotImplemented
- if self.get_id() and other.get_id():
- return self.get_id() == other.get_id()
- return super(RESTObject, self) == other
-
- def __ne__(self, other: object) -> bool:
- if not isinstance(other, RESTObject):
- return NotImplemented
- if self.get_id() and other.get_id():
- return self.get_id() != other.get_id()
- return super(RESTObject, self) != other
-
- def __dir__(self) -> Iterable[str]:
- return set(self.attributes).union(super(RESTObject, self).__dir__())
-
- def __hash__(self) -> int:
- if not self.get_id():
- return super(RESTObject, self).__hash__()
- return hash(self.get_id())
-
- def _create_managers(self) -> None:
- # NOTE(jlvillal): We are creating our managers by looking at the class
- # annotations. If an attribute is annotated as being a *Manager type
- # then we create the manager and assign it to the attribute.
- for attr, annotation in sorted(self.__annotations__.items()):
- if not isinstance(annotation, (type, str)):
- continue
- if isinstance(annotation, type):
- cls_name = annotation.__name__
- else:
- cls_name = annotation
- # All *Manager classes are used except for the base "RESTManager" class
- if cls_name == "RESTManager" or not cls_name.endswith("Manager"):
- continue
- cls = getattr(self._module, cls_name)
- manager = cls(self.manager.gitlab, parent=self)
- # Since we have our own __setattr__ method, we can't use setattr()
- self.__dict__[attr] = manager
-
- def _update_attrs(self, new_attrs: Dict[str, Any]) -> None:
- self.__dict__["_updated_attrs"] = {}
- self.__dict__["_attrs"] = new_attrs
-
- def get_id(self) -> Any:
- """Returns the id of the resource."""
- if self._id_attr is None or not hasattr(self, self._id_attr):
- return None
- return getattr(self, self._id_attr)
-
- @property
- def attributes(self) -> Dict[str, Any]:
- d = self.__dict__["_updated_attrs"].copy()
- d.update(self.__dict__["_attrs"])
- d.update(self.__dict__["_parent_attrs"])
- return d
-
-
-class RESTObjectList(object):
- """Generator object representing a list of RESTObject's.
-
- This generator uses the Gitlab pagination system to fetch new data when
- required.
-
- Note: you should not instantiate such objects, they are returned by calls
- to RESTManager.list()
-
- Args:
- manager: Manager to attach to the created objects
- obj_cls: Type of objects to create from the json data
- _list: A GitlabList object
- """
-
- def __init__(
- self, manager: "RESTManager", obj_cls: Type[RESTObject], _list: GitlabList
- ) -> None:
- """Creates an objects list from a GitlabList.
-
- You should not create objects of this type, but use managers list()
- methods instead.
-
- Args:
- manager: the RESTManager to attach to the objects
- obj_cls: the class of the created objects
- _list: the GitlabList holding the data
- """
- self.manager = manager
- self._obj_cls = obj_cls
- self._list = _list
-
- def __iter__(self) -> "RESTObjectList":
- return self
-
- def __len__(self) -> int:
- return len(self._list)
-
- def __next__(self) -> RESTObject:
- return self.next()
-
- def next(self) -> RESTObject:
- data = self._list.next()
- return self._obj_cls(self.manager, data)
-
- @property
- def current_page(self) -> int:
- """The current page number."""
- return self._list.current_page
-
- @property
- def prev_page(self) -> Optional[int]:
- """The previous page number.
-
- If None, the current page is the first.
- """
- return self._list.prev_page
-
- @property
- def next_page(self) -> Optional[int]:
- """The next page number.
-
- If None, the current page is the last.
- """
- return self._list.next_page
-
- @property
- def per_page(self) -> int:
- """The number of items per page."""
- return self._list.per_page
-
- @property
- def total_pages(self) -> int:
- """The total number of pages."""
- return self._list.total_pages
-
- @property
- def total(self) -> int:
- """The total number of items."""
- return self._list.total
-
-
-class RequiredOptional(NamedTuple):
- required: Tuple[str, ...] = tuple()
- optional: Tuple[str, ...] = tuple()
-
-
-class RESTManager(object):
- """Base class for CRUD operations on objects.
-
- Derived class must define ``_path`` and ``_obj_cls``.
-
- ``_path``: Base URL path on which requests will be sent (e.g. '/projects')
- ``_obj_cls``: The class of objects that will be created
- """
-
- _create_attrs: RequiredOptional = RequiredOptional()
- _update_attrs: RequiredOptional = RequiredOptional()
- _path: Optional[str] = None
- _obj_cls: Optional[Type[RESTObject]] = None
- _from_parent_attrs: Dict[str, Any] = {}
- _types: Dict[str, Type[g_types.GitlabAttribute]] = {}
-
- _computed_path: Optional[str]
- _parent: Optional[RESTObject]
- _parent_attrs: Dict[str, Any]
- gitlab: Gitlab
-
- def __init__(self, gl: Gitlab, parent: Optional[RESTObject] = None) -> None:
- """REST manager constructor.
-
- Args:
- gl (Gitlab): :class:`~gitlab.Gitlab` connection to use to make
- requests.
- parent: REST object to which the manager is attached.
- """
- self.gitlab = gl
- self._parent = parent # for nested managers
- self._computed_path = self._compute_path()
-
- @property
- def parent_attrs(self) -> Optional[Dict[str, Any]]:
- return self._parent_attrs
-
- def _compute_path(self, path: Optional[str] = None) -> Optional[str]:
- self._parent_attrs = {}
- if path is None:
- path = self._path
- if path is None:
- return None
- if self._parent is None or not self._from_parent_attrs:
- return path
-
- data = {
- self_attr: getattr(self._parent, parent_attr, None)
- for self_attr, parent_attr in self._from_parent_attrs.items()
- }
- self._parent_attrs = data
- return path % data
-
- @property
- def path(self) -> Optional[str]:
- return self._computed_path
diff --git a/gitlab/cli.py b/gitlab/cli.py
deleted file mode 100644
index c053a38..0000000
--- a/gitlab/cli.py
+++ /dev/null
@@ -1,260 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013-2017 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-
-import argparse
-import functools
-import re
-import sys
-from types import ModuleType
-from typing import Any, Callable, cast, Dict, Optional, Tuple, Type, TypeVar, Union
-
-from requests.structures import CaseInsensitiveDict
-
-import gitlab.config
-from gitlab.base import RESTObject
-
-# This regex is based on:
-# https://github.com/jpvanhal/inflection/blob/master/inflection/__init__.py
-camel_upperlower_regex = re.compile(r"([A-Z]+)([A-Z][a-z])")
-camel_lowerupper_regex = re.compile(r"([a-z\d])([A-Z])")
-
-# custom_actions = {
-# cls: {
-# action: (mandatory_args, optional_args, in_obj),
-# },
-# }
-custom_actions: Dict[str, Dict[str, Tuple[Tuple[str, ...], Tuple[str, ...], bool]]] = {}
-
-
-# For an explanation of how these type-hints work see:
-# https://mypy.readthedocs.io/en/stable/generics.html#declaring-decorators
-#
-# The goal here is that functions which get decorated will retain their types.
-__F = TypeVar("__F", bound=Callable[..., Any])
-
-
-def register_custom_action(
- cls_names: Union[str, Tuple[str, ...]],
- mandatory: Tuple[str, ...] = tuple(),
- optional: Tuple[str, ...] = tuple(),
- custom_action: Optional[str] = None,
-) -> Callable[[__F], __F]:
- def wrap(f: __F) -> __F:
- @functools.wraps(f)
- def wrapped_f(*args: Any, **kwargs: Any) -> Any:
- return f(*args, **kwargs)
-
- # in_obj defines whether the method belongs to the obj or the manager
- in_obj = True
- if isinstance(cls_names, tuple):
- classes = cls_names
- else:
- classes = (cls_names,)
-
- for cls_name in classes:
- final_name = cls_name
- if cls_name.endswith("Manager"):
- final_name = cls_name.replace("Manager", "")
- in_obj = False
- if final_name not in custom_actions:
- custom_actions[final_name] = {}
-
- action = custom_action or f.__name__.replace("_", "-")
- custom_actions[final_name][action] = (mandatory, optional, in_obj)
-
- return cast(__F, wrapped_f)
-
- return wrap
-
-
-def die(msg: str, e: Optional[Exception] = None) -> None:
- if e:
- msg = "%s (%s)" % (msg, e)
- sys.stderr.write(msg + "\n")
- sys.exit(1)
-
-
-def what_to_cls(what: str, namespace: ModuleType) -> Type[RESTObject]:
- classes = CaseInsensitiveDict(namespace.__dict__)
- lowercase_class = what.replace("-", "")
-
- return classes[lowercase_class]
-
-
-def cls_to_what(cls: RESTObject) -> str:
- dasherized_uppercase = camel_upperlower_regex.sub(r"\1-\2", cls.__name__)
- dasherized_lowercase = camel_lowerupper_regex.sub(r"\1-\2", dasherized_uppercase)
- return dasherized_lowercase.lower()
-
-
-def _get_base_parser(add_help: bool = True) -> argparse.ArgumentParser:
- parser = argparse.ArgumentParser(
- add_help=add_help, description="GitLab API Command Line Interface"
- )
- parser.add_argument("--version", help="Display the version.", action="store_true")
- parser.add_argument(
- "-v",
- "--verbose",
- "--fancy",
- help="Verbose mode (legacy format only)",
- action="store_true",
- )
- parser.add_argument(
- "-d", "--debug", help="Debug mode (display HTTP requests)", action="store_true"
- )
- parser.add_argument(
- "-c",
- "--config-file",
- action="append",
- help="Configuration file to use. Can be used multiple times.",
- )
- parser.add_argument(
- "-g",
- "--gitlab",
- help=(
- "Which configuration section should "
- "be used. If not defined, the default selection "
- "will be used."
- ),
- required=False,
- )
- parser.add_argument(
- "-o",
- "--output",
- help="Output format (v4 only): json|legacy|yaml",
- required=False,
- choices=["json", "legacy", "yaml"],
- default="legacy",
- )
- parser.add_argument(
- "-f",
- "--fields",
- help=(
- "Fields to display in the output (comma "
- "separated). Not used with legacy output"
- ),
- required=False,
- )
-
- return parser
-
-
-def _get_parser() -> argparse.ArgumentParser:
- # NOTE: We must delay import of gitlab.v4.cli until now or
- # otherwise it will cause circular import errors
- import gitlab.v4.cli
-
- parser = _get_base_parser()
- return gitlab.v4.cli.extend_parser(parser)
-
-
-def _parse_value(v: Any) -> Any:
- if isinstance(v, str) and v.startswith("@"):
- # If the user-provided value starts with @, we try to read the file
- # path provided after @ as the real value. Exit on any error.
- try:
- with open(v[1:]) as fl:
- return fl.read()
- except Exception as e:
- sys.stderr.write("%s\n" % e)
- sys.exit(1)
-
- return v
-
-
-def docs() -> argparse.ArgumentParser:
- """
- Provide a statically generated parser for sphinx only, so we don't need
- to provide dummy gitlab config for readthedocs.
- """
- if "sphinx" not in sys.modules:
- sys.exit("Docs parser is only intended for build_sphinx")
-
- return _get_parser()
-
-
-def main() -> None:
- if "--version" in sys.argv:
- print(gitlab.__version__)
- sys.exit(0)
-
- parser = _get_base_parser(add_help=False)
-
- # This first parsing step is used to find the gitlab config to use, and
- # load the propermodule (v3 or v4) accordingly. At that point we don't have
- # any subparser setup
- (options, _) = parser.parse_known_args(sys.argv)
- try:
- config = gitlab.config.GitlabConfigParser(options.gitlab, options.config_file)
- except gitlab.config.ConfigError as e:
- if "--help" in sys.argv or "-h" in sys.argv:
- parser.print_help()
- sys.exit(0)
- sys.exit(e)
- # We only support v4 API at this time
- if config.api_version not in ("4",):
- raise ModuleNotFoundError(name="gitlab.v%s.cli" % config.api_version)
-
- # Now we build the entire set of subcommands and do the complete parsing
- parser = _get_parser()
- try:
- import argcomplete # type: ignore
-
- argcomplete.autocomplete(parser)
- except Exception:
- pass
- args = parser.parse_args()
-
- config_files = args.config_file
- gitlab_id = args.gitlab
- verbose = args.verbose
- output = args.output
- fields = []
- if args.fields:
- fields = [x.strip() for x in args.fields.split(",")]
- debug = args.debug
- action = args.whaction
- what = args.what
-
- args_dict = vars(args)
- # Remove CLI behavior-related args
- for item in (
- "gitlab",
- "config_file",
- "verbose",
- "debug",
- "what",
- "whaction",
- "version",
- "output",
- ):
- args_dict.pop(item)
- args_dict = {k: _parse_value(v) for k, v in args_dict.items() if v is not None}
-
- try:
- gl = gitlab.Gitlab.from_config(gitlab_id, config_files)
- if gl.private_token or gl.oauth_token or gl.job_token:
- gl.auth()
- except Exception as e:
- die(str(e))
-
- if debug:
- gl.enable_debug()
-
- gitlab.v4.cli.run(gl, what, action, args_dict, verbose, output, fields)
diff --git a/gitlab/client.py b/gitlab/client.py
deleted file mode 100644
index 8bec64f..0000000
--- a/gitlab/client.py
+++ /dev/null
@@ -1,1011 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013-2017 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-"""Wrapper for the GitLab API."""
-
-import time
-from typing import Any, cast, Dict, List, Optional, Tuple, TYPE_CHECKING, Union
-
-import requests
-import requests.utils
-from requests_toolbelt.multipart.encoder import MultipartEncoder # type: ignore
-
-import gitlab.config
-import gitlab.const
-import gitlab.exceptions
-from gitlab import utils
-
-REDIRECT_MSG = (
- "python-gitlab detected a {status_code} ({reason!r}) redirection. You must update "
- "your GitLab URL to the correct URL to avoid issues. The redirection was from: "
- "{source!r} to {target!r}"
-)
-
-
-class Gitlab(object):
- """Represents a GitLab server connection.
-
- Args:
- url (str): The URL of the GitLab server (defaults to https://gitlab.com).
- private_token (str): The user private token
- oauth_token (str): An oauth token
- job_token (str): A CI job token
- ssl_verify (bool|str): Whether SSL certificates should be validated. If
- the value is a string, it is the path to a CA file used for
- certificate validation.
- timeout (float): Timeout to use for requests to the GitLab server.
- http_username (str): Username for HTTP authentication
- http_password (str): Password for HTTP authentication
- api_version (str): Gitlab API version to use (support for 4 only)
- pagination (str): Can be set to 'keyset' to use keyset pagination
- order_by (str): Set order_by globally
- user_agent (str): A custom user agent to use for making HTTP requests.
- retry_transient_errors (bool): Whether to retry after 500, 502, 503, or
- 504 responses. Defaults to False.
- """
-
- def __init__(
- self,
- url: Optional[str] = None,
- private_token: Optional[str] = None,
- oauth_token: Optional[str] = None,
- job_token: Optional[str] = None,
- ssl_verify: Union[bool, str] = True,
- http_username: Optional[str] = None,
- http_password: Optional[str] = None,
- timeout: Optional[float] = None,
- api_version: str = "4",
- session: Optional[requests.Session] = None,
- per_page: Optional[int] = None,
- pagination: Optional[str] = None,
- order_by: Optional[str] = None,
- user_agent: str = gitlab.const.USER_AGENT,
- retry_transient_errors: bool = False,
- ) -> None:
-
- self._api_version = str(api_version)
- self._server_version: Optional[str] = None
- self._server_revision: Optional[str] = None
- self._base_url = self._get_base_url(url)
- self._url = "%s/api/v%s" % (self._base_url, api_version)
- #: Timeout to use for requests to gitlab server
- self.timeout = timeout
- self.retry_transient_errors = retry_transient_errors
- #: Headers that will be used in request to GitLab
- self.headers = {"User-Agent": user_agent}
-
- #: Whether SSL certificates should be validated
- self.ssl_verify = ssl_verify
-
- self.private_token = private_token
- self.http_username = http_username
- self.http_password = http_password
- self.oauth_token = oauth_token
- self.job_token = job_token
- self._set_auth_info()
-
- #: Create a session object for requests
- self.session = session or requests.Session()
-
- self.per_page = per_page
- self.pagination = pagination
- self.order_by = order_by
-
- # We only support v4 API at this time
- if self._api_version not in ("4",):
- raise ModuleNotFoundError(name="gitlab.v%s.objects" % self._api_version)
- # NOTE: We must delay import of gitlab.v4.objects until now or
- # otherwise it will cause circular import errors
- import gitlab.v4.objects
-
- objects = gitlab.v4.objects
- self._objects = objects
-
- self.broadcastmessages = objects.BroadcastMessageManager(self)
- """See :class:`~gitlab.v4.objects.BroadcastMessageManager`"""
- self.deploykeys = objects.DeployKeyManager(self)
- """See :class:`~gitlab.v4.objects.DeployKeyManager`"""
- self.deploytokens = objects.DeployTokenManager(self)
- """See :class:`~gitlab.v4.objects.DeployTokenManager`"""
- self.geonodes = objects.GeoNodeManager(self)
- """See :class:`~gitlab.v4.objects.GeoNodeManager`"""
- self.gitlabciymls = objects.GitlabciymlManager(self)
- """See :class:`~gitlab.v4.objects.GitlabciymlManager`"""
- self.gitignores = objects.GitignoreManager(self)
- """See :class:`~gitlab.v4.objects.GitignoreManager`"""
- self.groups = objects.GroupManager(self)
- """See :class:`~gitlab.v4.objects.GroupManager`"""
- self.hooks = objects.HookManager(self)
- """See :class:`~gitlab.v4.objects.HookManager`"""
- self.issues = objects.IssueManager(self)
- """See :class:`~gitlab.v4.objects.IssueManager`"""
- self.issues_statistics = objects.IssuesStatisticsManager(self)
- """See :class:`~gitlab.v4.objects.IssuesStatisticsManager`"""
- self.keys = objects.KeyManager(self)
- """See :class:`~gitlab.v4.objects.KeyManager`"""
- self.ldapgroups = objects.LDAPGroupManager(self)
- """See :class:`~gitlab.v4.objects.LDAPGroupManager`"""
- self.licenses = objects.LicenseManager(self)
- """See :class:`~gitlab.v4.objects.LicenseManager`"""
- self.namespaces = objects.NamespaceManager(self)
- """See :class:`~gitlab.v4.objects.NamespaceManager`"""
- self.mergerequests = objects.MergeRequestManager(self)
- """See :class:`~gitlab.v4.objects.MergeRequestManager`"""
- self.notificationsettings = objects.NotificationSettingsManager(self)
- """See :class:`~gitlab.v4.objects.NotificationSettingsManager`"""
- self.projects = objects.ProjectManager(self)
- """See :class:`~gitlab.v4.objects.ProjectManager`"""
- self.runners = objects.RunnerManager(self)
- """See :class:`~gitlab.v4.objects.RunnerManager`"""
- self.settings = objects.ApplicationSettingsManager(self)
- """See :class:`~gitlab.v4.objects.ApplicationSettingsManager`"""
- self.appearance = objects.ApplicationAppearanceManager(self)
- """See :class:`~gitlab.v4.objects.ApplicationAppearanceManager`"""
- self.sidekiq = objects.SidekiqManager(self)
- """See :class:`~gitlab.v4.objects.SidekiqManager`"""
- self.snippets = objects.SnippetManager(self)
- """See :class:`~gitlab.v4.objects.SnippetManager`"""
- self.users = objects.UserManager(self)
- """See :class:`~gitlab.v4.objects.UserManager`"""
- self.todos = objects.TodoManager(self)
- """See :class:`~gitlab.v4.objects.TodoManager`"""
- self.dockerfiles = objects.DockerfileManager(self)
- """See :class:`~gitlab.v4.objects.DockerfileManager`"""
- self.events = objects.EventManager(self)
- """See :class:`~gitlab.v4.objects.EventManager`"""
- self.audit_events = objects.AuditEventManager(self)
- """See :class:`~gitlab.v4.objects.AuditEventManager`"""
- self.features = objects.FeatureManager(self)
- """See :class:`~gitlab.v4.objects.FeatureManager`"""
- self.pagesdomains = objects.PagesDomainManager(self)
- """See :class:`~gitlab.v4.objects.PagesDomainManager`"""
- self.user_activities = objects.UserActivitiesManager(self)
- """See :class:`~gitlab.v4.objects.UserActivitiesManager`"""
- self.applications = objects.ApplicationManager(self)
- """See :class:`~gitlab.v4.objects.ApplicationManager`"""
- self.variables = objects.VariableManager(self)
- """See :class:`~gitlab.v4.objects.VariableManager`"""
- self.personal_access_tokens = objects.PersonalAccessTokenManager(self)
- """See :class:`~gitlab.v4.objects.PersonalAccessTokenManager`"""
-
- def __enter__(self) -> "Gitlab":
- return self
-
- def __exit__(self, *args: Any) -> None:
- self.session.close()
-
- def __getstate__(self) -> Dict[str, Any]:
- state = self.__dict__.copy()
- state.pop("_objects")
- return state
-
- def __setstate__(self, state: Dict[str, Any]) -> None:
- self.__dict__.update(state)
- # We only support v4 API at this time
- if self._api_version not in ("4",):
- raise ModuleNotFoundError(name="gitlab.v%s.objects" % self._api_version)
- # NOTE: We must delay import of gitlab.v4.objects until now or
- # otherwise it will cause circular import errors
- import gitlab.v4.objects
-
- self._objects = gitlab.v4.objects
-
- @property
- def url(self) -> str:
- """The user-provided server URL."""
- return self._base_url
-
- @property
- def api_url(self) -> str:
- """The computed API base URL."""
- return self._url
-
- @property
- def api_version(self) -> str:
- """The API version used (4 only)."""
- return self._api_version
-
- @classmethod
- def from_config(
- cls, gitlab_id: Optional[str] = None, config_files: Optional[List[str]] = None
- ) -> "Gitlab":
- """Create a Gitlab connection from configuration files.
-
- Args:
- gitlab_id (str): ID of the configuration section.
- config_files list[str]: List of paths to configuration files.
-
- Returns:
- (gitlab.Gitlab): A Gitlab connection.
-
- Raises:
- gitlab.config.GitlabDataError: If the configuration is not correct.
- """
- config = gitlab.config.GitlabConfigParser(
- gitlab_id=gitlab_id, config_files=config_files
- )
- return cls(
- config.url,
- private_token=config.private_token,
- oauth_token=config.oauth_token,
- job_token=config.job_token,
- ssl_verify=config.ssl_verify,
- timeout=config.timeout,
- http_username=config.http_username,
- http_password=config.http_password,
- api_version=config.api_version,
- per_page=config.per_page,
- pagination=config.pagination,
- order_by=config.order_by,
- user_agent=config.user_agent,
- retry_transient_errors=config.retry_transient_errors,
- )
-
- def auth(self) -> None:
- """Performs an authentication using private token.
-
- The `user` attribute will hold a `gitlab.objects.CurrentUser` object on
- success.
- """
- self.user = self._objects.CurrentUserManager(self).get()
-
- def version(self) -> Tuple[str, str]:
- """Returns the version and revision of the gitlab server.
-
- Note that self.version and self.revision will be set on the gitlab
- object.
-
- Returns:
- tuple (str, str): The server version and server revision.
- ('unknown', 'unknwown') if the server doesn't
- perform as expected.
- """
- if self._server_version is None:
- try:
- data = self.http_get("/version")
- if isinstance(data, dict):
- self._server_version = data["version"]
- self._server_revision = data["revision"]
- else:
- self._server_version = "unknown"
- self._server_revision = "unknown"
- except Exception:
- self._server_version = "unknown"
- self._server_revision = "unknown"
-
- return cast(str, self._server_version), cast(str, self._server_revision)
-
- @gitlab.exceptions.on_http_error(gitlab.exceptions.GitlabVerifyError)
- def lint(self, content: str, **kwargs: Any) -> Tuple[bool, List[str]]:
- """Validate a gitlab CI configuration.
-
- Args:
- content (txt): The .gitlab-ci.yml content
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabVerifyError: If the validation could not be done
-
- Returns:
- tuple: (True, []) if the file is valid, (False, errors(list))
- otherwise
- """
- post_data = {"content": content}
- data = self.http_post("/ci/lint", post_data=post_data, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(data, requests.Response)
- return (data["status"] == "valid", data["errors"])
-
- @gitlab.exceptions.on_http_error(gitlab.exceptions.GitlabMarkdownError)
- def markdown(
- self, text: str, gfm: bool = False, project: Optional[str] = None, **kwargs: Any
- ) -> str:
- """Render an arbitrary Markdown document.
-
- Args:
- text (str): The markdown text to render
- gfm (bool): Render text using GitLab Flavored Markdown. Default is
- False
- project (str): Full path of a project used a context when `gfm` is
- True
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabMarkdownError: If the server cannot perform the request
-
- Returns:
- str: The HTML rendering of the markdown text.
- """
- post_data = {"text": text, "gfm": gfm}
- if project is not None:
- post_data["project"] = project
- data = self.http_post("/markdown", post_data=post_data, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(data, requests.Response)
- return data["html"]
-
- @gitlab.exceptions.on_http_error(gitlab.exceptions.GitlabLicenseError)
- def get_license(self, **kwargs: Any) -> Dict[str, Any]:
- """Retrieve information about the current license.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server cannot perform the request
-
- Returns:
- dict: The current license information
- """
- result = self.http_get("/license", **kwargs)
- if isinstance(result, dict):
- return result
- return {}
-
- @gitlab.exceptions.on_http_error(gitlab.exceptions.GitlabLicenseError)
- def set_license(self, license: str, **kwargs: Any) -> Dict[str, Any]:
- """Add a new license.
-
- Args:
- license (str): The license string
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabPostError: If the server cannot perform the request
-
- Returns:
- dict: The new license information
- """
- data = {"license": license}
- result = self.http_post("/license", post_data=data, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(result, requests.Response)
- return result
-
- def _set_auth_info(self) -> None:
- tokens = [
- token
- for token in [self.private_token, self.oauth_token, self.job_token]
- if token
- ]
- if len(tokens) > 1:
- raise ValueError(
- "Only one of private_token, oauth_token or job_token should "
- "be defined"
- )
- if (self.http_username and not self.http_password) or (
- not self.http_username and self.http_password
- ):
- raise ValueError(
- "Both http_username and http_password should " "be defined"
- )
- if self.oauth_token and self.http_username:
- raise ValueError(
- "Only one of oauth authentication or http "
- "authentication should be defined"
- )
-
- self._http_auth = None
- if self.private_token:
- self.headers.pop("Authorization", None)
- self.headers["PRIVATE-TOKEN"] = self.private_token
- self.headers.pop("JOB-TOKEN", None)
-
- if self.oauth_token:
- self.headers["Authorization"] = "Bearer %s" % self.oauth_token
- self.headers.pop("PRIVATE-TOKEN", None)
- self.headers.pop("JOB-TOKEN", None)
-
- if self.job_token:
- self.headers.pop("Authorization", None)
- self.headers.pop("PRIVATE-TOKEN", None)
- self.headers["JOB-TOKEN"] = self.job_token
-
- if self.http_username:
- self._http_auth = requests.auth.HTTPBasicAuth(
- self.http_username, self.http_password
- )
-
- def enable_debug(self) -> None:
- import logging
- from http.client import HTTPConnection # noqa
-
- HTTPConnection.debuglevel = 1 # type: ignore
- logging.basicConfig()
- logging.getLogger().setLevel(logging.DEBUG)
- requests_log = logging.getLogger("requests.packages.urllib3")
- requests_log.setLevel(logging.DEBUG)
- requests_log.propagate = True
-
- def _get_session_opts(self) -> Dict[str, Any]:
- return {
- "headers": self.headers.copy(),
- "auth": self._http_auth,
- "timeout": self.timeout,
- "verify": self.ssl_verify,
- }
-
- def _get_base_url(self, url: Optional[str] = None) -> str:
- """Return the base URL with the trailing slash stripped.
- If the URL is a Falsy value, return the default URL.
- Returns:
- str: The base URL
- """
- if not url:
- return gitlab.const.DEFAULT_URL
-
- return url.rstrip("/")
-
- def _build_url(self, path: str) -> str:
- """Returns the full url from path.
-
- If path is already a url, return it unchanged. If it's a path, append
- it to the stored url.
-
- Returns:
- str: The full URL
- """
- if path.startswith("http://") or path.startswith("https://"):
- return path
- else:
- return "%s%s" % (self._url, path)
-
- def _check_redirects(self, result: requests.Response) -> None:
- # Check the requests history to detect 301/302 redirections.
- # If the initial verb is POST or PUT, the redirected request will use a
- # GET request, leading to unwanted behaviour.
- # If we detect a redirection with a POST or a PUT request, we
- # raise an exception with a useful error message.
- if not result.history:
- return
-
- for item in result.history:
- if item.status_code not in (301, 302):
- continue
- # GET methods can be redirected without issue
- if item.request.method == "GET":
- continue
- target = item.headers.get("location")
- raise gitlab.exceptions.RedirectError(
- REDIRECT_MSG.format(
- status_code=item.status_code,
- reason=item.reason,
- source=item.url,
- target=target,
- )
- )
-
- def _prepare_send_data(
- self,
- files: Optional[Dict[str, Any]] = None,
- post_data: Optional[Dict[str, Any]] = None,
- raw: bool = False,
- ) -> Tuple[
- Optional[Dict[str, Any]],
- Optional[Union[Dict[str, Any], MultipartEncoder]],
- str,
- ]:
- if files:
- if post_data is None:
- post_data = {}
- else:
- # booleans does not exists for data (neither for MultipartEncoder):
- # cast to string int to avoid: 'bool' object has no attribute 'encode'
- for k, v in post_data.items():
- if isinstance(v, bool):
- post_data[k] = str(int(v))
- post_data["file"] = files.get("file")
- post_data["avatar"] = files.get("avatar")
-
- data = MultipartEncoder(post_data)
- return (None, data, data.content_type)
-
- if raw and post_data:
- return (None, post_data, "application/octet-stream")
-
- return (post_data, None, "application/json")
-
- def http_request(
- self,
- verb: str,
- path: str,
- query_data: Optional[Dict[str, Any]] = None,
- post_data: Optional[Dict[str, Any]] = None,
- raw: bool = False,
- streamed: bool = False,
- files: Optional[Dict[str, Any]] = None,
- timeout: Optional[float] = None,
- obey_rate_limit: bool = True,
- max_retries: int = 10,
- **kwargs: Any,
- ) -> requests.Response:
- """Make an HTTP request to the Gitlab server.
-
- Args:
- verb (str): The HTTP method to call ('get', 'post', 'put',
- 'delete')
- path (str): Path or full URL to query ('/projects' or
- 'http://whatever/v4/api/projecs')
- query_data (dict): Data to send as query parameters
- post_data (dict): Data to send in the body (will be converted to
- json by default)
- raw (bool): If True, do not convert post_data to json
- streamed (bool): Whether the data should be streamed
- files (dict): The files to send to the server
- timeout (float): The timeout, in seconds, for the request
- obey_rate_limit (bool): Whether to obey 429 Too Many Request
- responses. Defaults to True.
- max_retries (int): Max retries after 429 or transient errors,
- set to -1 to retry forever. Defaults to 10.
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- A requests result object.
-
- Raises:
- GitlabHttpError: When the return code is not 2xx
- """
- query_data = query_data or {}
- url = self._build_url(path)
-
- params: Dict[str, Any] = {}
- utils.copy_dict(params, query_data)
-
- # Deal with kwargs: by default a user uses kwargs to send data to the
- # gitlab server, but this generates problems (python keyword conflicts
- # and python-gitlab/gitlab conflicts).
- # So we provide a `query_parameters` key: if it's there we use its dict
- # value as arguments for the gitlab server, and ignore the other
- # arguments, except pagination ones (per_page and page)
- if "query_parameters" in kwargs:
- utils.copy_dict(params, kwargs["query_parameters"])
- for arg in ("per_page", "page"):
- if arg in kwargs:
- params[arg] = kwargs[arg]
- else:
- utils.copy_dict(params, kwargs)
-
- opts = self._get_session_opts()
-
- verify = opts.pop("verify")
- opts_timeout = opts.pop("timeout")
- # If timeout was passed into kwargs, allow it to override the default
- if timeout is None:
- timeout = opts_timeout
-
- # We need to deal with json vs. data when uploading files
- json, data, content_type = self._prepare_send_data(files, post_data, raw)
- opts["headers"]["Content-type"] = content_type
-
- # Requests assumes that `.` should not be encoded as %2E and will make
- # changes to urls using this encoding. Using a prepped request we can
- # get the desired behavior.
- # The Requests behavior is right but it seems that web servers don't
- # always agree with this decision (this is the case with a default
- # gitlab installation)
- req = requests.Request(verb, url, json=json, data=data, params=params, **opts)
- prepped = self.session.prepare_request(req)
- if TYPE_CHECKING:
- assert prepped.url is not None
- prepped.url = utils.sanitized_url(prepped.url)
- settings = self.session.merge_environment_settings(
- prepped.url, {}, streamed, verify, None
- )
-
- cur_retries = 0
- while True:
- result = self.session.send(prepped, timeout=timeout, **settings)
-
- self._check_redirects(result)
-
- if 200 <= result.status_code < 300:
- return result
-
- retry_transient_errors = kwargs.get(
- "retry_transient_errors", self.retry_transient_errors
- )
- if (429 == result.status_code and obey_rate_limit) or (
- result.status_code in [500, 502, 503, 504] and retry_transient_errors
- ):
- if max_retries == -1 or cur_retries < max_retries:
- wait_time = 2 ** cur_retries * 0.1
- if "Retry-After" in result.headers:
- wait_time = int(result.headers["Retry-After"])
- cur_retries += 1
- time.sleep(wait_time)
- continue
-
- error_message = result.content
- try:
- error_json = result.json()
- for k in ("message", "error"):
- if k in error_json:
- error_message = error_json[k]
- except (KeyError, ValueError, TypeError):
- pass
-
- if result.status_code == 401:
- raise gitlab.exceptions.GitlabAuthenticationError(
- response_code=result.status_code,
- error_message=error_message,
- response_body=result.content,
- )
-
- raise gitlab.exceptions.GitlabHttpError(
- response_code=result.status_code,
- error_message=error_message,
- response_body=result.content,
- )
-
- def http_get(
- self,
- path: str,
- query_data: Optional[Dict[str, Any]] = None,
- streamed: bool = False,
- raw: bool = False,
- **kwargs: Any,
- ) -> Union[Dict[str, Any], requests.Response]:
- """Make a GET request to the Gitlab server.
-
- Args:
- path (str): Path or full URL to query ('/projects' or
- 'http://whatever/v4/api/projecs')
- query_data (dict): Data to send as query parameters
- streamed (bool): Whether the data should be streamed
- raw (bool): If True do not try to parse the output as json
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- A requests result object is streamed is True or the content type is
- not json.
- The parsed json data otherwise.
-
- Raises:
- GitlabHttpError: When the return code is not 2xx
- GitlabParsingError: If the json data could not be parsed
- """
- query_data = query_data or {}
- result = self.http_request(
- "get", path, query_data=query_data, streamed=streamed, **kwargs
- )
-
- if (
- result.headers["Content-Type"] == "application/json"
- and not streamed
- and not raw
- ):
- try:
- return result.json()
- except Exception as e:
- raise gitlab.exceptions.GitlabParsingError(
- error_message="Failed to parse the server message"
- ) from e
- else:
- return result
-
- def http_list(
- self,
- path: str,
- query_data: Optional[Dict[str, Any]] = None,
- as_list: Optional[bool] = None,
- **kwargs: Any,
- ) -> Union["GitlabList", List[Dict[str, Any]]]:
- """Make a GET request to the Gitlab server for list-oriented queries.
-
- Args:
- path (str): Path or full URL to query ('/projects' or
- 'http://whatever/v4/api/projects')
- query_data (dict): Data to send as query parameters
- **kwargs: Extra options to send to the server (e.g. sudo, page,
- per_page)
-
- Returns:
- list: A list of the objects returned by the server. If `as_list` is
- False and no pagination-related arguments (`page`, `per_page`,
- `all`) are defined then a GitlabList object (generator) is returned
- instead. This object will make API calls when needed to fetch the
- next items from the server.
-
- Raises:
- GitlabHttpError: When the return code is not 2xx
- GitlabParsingError: If the json data could not be parsed
- """
- query_data = query_data or {}
-
- # In case we want to change the default behavior at some point
- as_list = True if as_list is None else as_list
-
- get_all = kwargs.pop("all", False)
- url = self._build_url(path)
-
- page = kwargs.get("page")
-
- if get_all is True and as_list is True:
- return list(GitlabList(self, url, query_data, **kwargs))
-
- if page or as_list is True:
- # pagination requested, we return a list
- return list(GitlabList(self, url, query_data, get_next=False, **kwargs))
-
- # No pagination, generator requested
- return GitlabList(self, url, query_data, **kwargs)
-
- def http_post(
- self,
- path: str,
- query_data: Optional[Dict[str, Any]] = None,
- post_data: Optional[Dict[str, Any]] = None,
- raw: bool = False,
- files: Optional[Dict[str, Any]] = None,
- **kwargs: Any,
- ) -> Union[Dict[str, Any], requests.Response]:
- """Make a POST request to the Gitlab server.
-
- Args:
- path (str): Path or full URL to query ('/projects' or
- 'http://whatever/v4/api/projecs')
- query_data (dict): Data to send as query parameters
- post_data (dict): Data to send in the body (will be converted to
- json by default)
- raw (bool): If True, do not convert post_data to json
- files (dict): The files to send to the server
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- The parsed json returned by the server if json is return, else the
- raw content
-
- Raises:
- GitlabHttpError: When the return code is not 2xx
- GitlabParsingError: If the json data could not be parsed
- """
- query_data = query_data or {}
- post_data = post_data or {}
-
- result = self.http_request(
- "post",
- path,
- query_data=query_data,
- post_data=post_data,
- files=files,
- **kwargs,
- )
- try:
- if result.headers.get("Content-Type", None) == "application/json":
- return result.json()
- except Exception as e:
- raise gitlab.exceptions.GitlabParsingError(
- error_message="Failed to parse the server message"
- ) from e
- return result
-
- def http_put(
- self,
- path: str,
- query_data: Optional[Dict[str, Any]] = None,
- post_data: Optional[Dict[str, Any]] = None,
- raw: bool = False,
- files: Optional[Dict[str, Any]] = None,
- **kwargs: Any,
- ) -> Union[Dict[str, Any], requests.Response]:
- """Make a PUT request to the Gitlab server.
-
- Args:
- path (str): Path or full URL to query ('/projects' or
- 'http://whatever/v4/api/projecs')
- query_data (dict): Data to send as query parameters
- post_data (dict): Data to send in the body (will be converted to
- json by default)
- raw (bool): If True, do not convert post_data to json
- files (dict): The files to send to the server
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- The parsed json returned by the server.
-
- Raises:
- GitlabHttpError: When the return code is not 2xx
- GitlabParsingError: If the json data could not be parsed
- """
- query_data = query_data or {}
- post_data = post_data or {}
-
- result = self.http_request(
- "put",
- path,
- query_data=query_data,
- post_data=post_data,
- files=files,
- raw=raw,
- **kwargs,
- )
- try:
- return result.json()
- except Exception as e:
- raise gitlab.exceptions.GitlabParsingError(
- error_message="Failed to parse the server message"
- ) from e
-
- def http_delete(self, path: str, **kwargs: Any) -> requests.Response:
- """Make a DELETE request to the Gitlab server.
-
- Args:
- path (str): Path or full URL to query ('/projects' or
- 'http://whatever/v4/api/projecs')
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- The requests object.
-
- Raises:
- GitlabHttpError: When the return code is not 2xx
- """
- return self.http_request("delete", path, **kwargs)
-
- @gitlab.exceptions.on_http_error(gitlab.exceptions.GitlabSearchError)
- def search(
- self, scope: str, search: str, **kwargs: Any
- ) -> Union["GitlabList", List[Dict[str, Any]]]:
- """Search GitLab resources matching the provided string.'
-
- Args:
- scope (str): Scope of the search
- search (str): Search string
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabSearchError: If the server failed to perform the request
-
- Returns:
- GitlabList: A list of dicts describing the resources found.
- """
- data = {"scope": scope, "search": search}
- return self.http_list("/search", query_data=data, **kwargs)
-
-
-class GitlabList(object):
- """Generator representing a list of remote objects.
-
- The object handles the links returned by a query to the API, and will call
- the API again when needed.
- """
-
- def __init__(
- self,
- gl: Gitlab,
- url: str,
- query_data: Dict[str, Any],
- get_next: bool = True,
- **kwargs: Any,
- ) -> None:
- self._gl = gl
-
- # Preserve kwargs for subsequent queries
- self._kwargs = kwargs.copy()
-
- self._query(url, query_data, **self._kwargs)
- self._get_next = get_next
-
- # Remove query_parameters from kwargs, which are saved via the `next` URL
- self._kwargs.pop("query_parameters", None)
-
- def _query(
- self, url: str, query_data: Optional[Dict[str, Any]] = None, **kwargs: Any
- ) -> None:
- query_data = query_data or {}
- result = self._gl.http_request("get", url, query_data=query_data, **kwargs)
- try:
- links = result.links
- if links:
- next_url = links["next"]["url"]
- else:
- next_url = requests.utils.parse_header_links(result.headers["links"])[
- 0
- ]["url"]
- self._next_url = next_url
- except KeyError:
- self._next_url = None
- self._current_page: Optional[Union[str, int]] = result.headers.get("X-Page")
- self._prev_page: Optional[Union[str, int]] = result.headers.get("X-Prev-Page")
- self._next_page: Optional[Union[str, int]] = result.headers.get("X-Next-Page")
- self._per_page: Optional[Union[str, int]] = result.headers.get("X-Per-Page")
- self._total_pages: Optional[Union[str, int]] = result.headers.get(
- "X-Total-Pages"
- )
- self._total: Optional[Union[str, int]] = result.headers.get("X-Total")
-
- try:
- self._data: List[Dict[str, Any]] = result.json()
- except Exception as e:
- raise gitlab.exceptions.GitlabParsingError(
- error_message="Failed to parse the server message"
- ) from e
-
- self._current = 0
-
- @property
- def current_page(self) -> int:
- """The current page number."""
- if TYPE_CHECKING:
- assert self._current_page is not None
- return int(self._current_page)
-
- @property
- def prev_page(self) -> Optional[int]:
- """The previous page number.
-
- If None, the current page is the first.
- """
- return int(self._prev_page) if self._prev_page else None
-
- @property
- def next_page(self) -> Optional[int]:
- """The next page number.
-
- If None, the current page is the last.
- """
- return int(self._next_page) if self._next_page else None
-
- @property
- def per_page(self) -> int:
- """The number of items per page."""
- if TYPE_CHECKING:
- assert self._per_page is not None
- return int(self._per_page)
-
- @property
- def total_pages(self) -> int:
- """The total number of pages."""
- if TYPE_CHECKING:
- assert self._total_pages is not None
- return int(self._total_pages)
-
- @property
- def total(self) -> int:
- """The total number of items."""
- if TYPE_CHECKING:
- assert self._total is not None
- return int(self._total)
-
- def __iter__(self) -> "GitlabList":
- return self
-
- def __len__(self) -> int:
- if self._total is None:
- return 0
- return int(self._total)
-
- def __next__(self) -> Dict[str, Any]:
- return self.next()
-
- def next(self) -> Dict[str, Any]:
- try:
- item = self._data[self._current]
- self._current += 1
- return item
- except IndexError:
- pass
-
- if self._next_url and self._get_next is True:
- self._query(self._next_url, **self._kwargs)
- return self.next()
-
- raise StopIteration
diff --git a/gitlab/config.py b/gitlab/config.py
deleted file mode 100644
index ba14468..0000000
--- a/gitlab/config.py
+++ /dev/null
@@ -1,249 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013-2017 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-import configparser
-import os
-import shlex
-import subprocess
-from os.path import expanduser, expandvars
-from typing import List, Optional, Union
-
-from gitlab.const import USER_AGENT
-
-
-def _env_config() -> List[str]:
- if "PYTHON_GITLAB_CFG" in os.environ:
- return [os.environ["PYTHON_GITLAB_CFG"]]
- return []
-
-
-_DEFAULT_FILES: List[str] = _env_config() + [
- "/etc/python-gitlab.cfg",
- os.path.expanduser("~/.python-gitlab.cfg"),
-]
-
-HELPER_PREFIX = "helper:"
-
-HELPER_ATTRIBUTES = ["job_token", "http_password", "private_token", "oauth_token"]
-
-
-class ConfigError(Exception):
- pass
-
-
-class GitlabIDError(ConfigError):
- pass
-
-
-class GitlabDataError(ConfigError):
- pass
-
-
-class GitlabConfigMissingError(ConfigError):
- pass
-
-
-class GitlabConfigHelperError(ConfigError):
- pass
-
-
-class GitlabConfigParser(object):
- def __init__(
- self, gitlab_id: Optional[str] = None, config_files: Optional[List[str]] = None
- ) -> None:
- self.gitlab_id = gitlab_id
- _files = config_files or _DEFAULT_FILES
- file_exist = False
- for file in _files:
- if os.path.exists(file):
- file_exist = True
- if not file_exist:
- raise GitlabConfigMissingError(
- "Config file not found. \nPlease create one in "
- "one of the following locations: {} \nor "
- "specify a config file using the '-c' parameter.".format(
- ", ".join(_DEFAULT_FILES)
- )
- )
-
- self._config = configparser.ConfigParser()
- self._config.read(_files)
-
- if self.gitlab_id is None:
- try:
- self.gitlab_id = self._config.get("global", "default")
- except Exception as e:
- raise GitlabIDError(
- "Impossible to get the gitlab id (not specified in config file)"
- ) from e
-
- try:
- self.url = self._config.get(self.gitlab_id, "url")
- except Exception as e:
- raise GitlabDataError(
- "Impossible to get gitlab informations from "
- "configuration (%s)" % self.gitlab_id
- ) from e
-
- self.ssl_verify: Union[bool, str] = True
- try:
- self.ssl_verify = self._config.getboolean("global", "ssl_verify")
- except ValueError:
- # Value Error means the option exists but isn't a boolean.
- # Get as a string instead as it should then be a local path to a
- # CA bundle.
- try:
- self.ssl_verify = self._config.get("global", "ssl_verify")
- except Exception:
- pass
- except Exception:
- pass
- try:
- self.ssl_verify = self._config.getboolean(self.gitlab_id, "ssl_verify")
- except ValueError:
- # Value Error means the option exists but isn't a boolean.
- # Get as a string instead as it should then be a local path to a
- # CA bundle.
- try:
- self.ssl_verify = self._config.get(self.gitlab_id, "ssl_verify")
- except Exception:
- pass
- except Exception:
- pass
-
- self.timeout = 60
- try:
- self.timeout = self._config.getint("global", "timeout")
- except Exception:
- pass
- try:
- self.timeout = self._config.getint(self.gitlab_id, "timeout")
- except Exception:
- pass
-
- self.private_token = None
- try:
- self.private_token = self._config.get(self.gitlab_id, "private_token")
- except Exception:
- pass
-
- self.oauth_token = None
- try:
- self.oauth_token = self._config.get(self.gitlab_id, "oauth_token")
- except Exception:
- pass
-
- self.job_token = None
- try:
- self.job_token = self._config.get(self.gitlab_id, "job_token")
- except Exception:
- pass
-
- self.http_username = None
- self.http_password = None
- try:
- self.http_username = self._config.get(self.gitlab_id, "http_username")
- self.http_password = self._config.get(self.gitlab_id, "http_password")
- except Exception:
- pass
-
- self._get_values_from_helper()
-
- self.api_version = "4"
- try:
- self.api_version = self._config.get("global", "api_version")
- except Exception:
- pass
- try:
- self.api_version = self._config.get(self.gitlab_id, "api_version")
- except Exception:
- pass
- if self.api_version not in ("4",):
- raise GitlabDataError("Unsupported API version: %s" % self.api_version)
-
- self.per_page = None
- for section in ["global", self.gitlab_id]:
- try:
- self.per_page = self._config.getint(section, "per_page")
- except Exception:
- pass
- if self.per_page is not None and not 0 <= self.per_page <= 100:
- raise GitlabDataError("Unsupported per_page number: %s" % self.per_page)
-
- self.pagination = None
- try:
- self.pagination = self._config.get(self.gitlab_id, "pagination")
- except Exception:
- pass
-
- self.order_by = None
- try:
- self.order_by = self._config.get(self.gitlab_id, "order_by")
- except Exception:
- pass
-
- self.user_agent = USER_AGENT
- try:
- self.user_agent = self._config.get("global", "user_agent")
- except Exception:
- pass
- try:
- self.user_agent = self._config.get(self.gitlab_id, "user_agent")
- except Exception:
- pass
-
- self.retry_transient_errors = False
- try:
- self.retry_transient_errors = self._config.getboolean(
- "global", "retry_transient_errors"
- )
- except Exception:
- pass
- try:
- self.retry_transient_errors = self._config.getboolean(
- self.gitlab_id, "retry_transient_errors"
- )
- except Exception:
- pass
-
- def _get_values_from_helper(self) -> None:
- """Update attributes that may get values from an external helper program"""
- for attr in HELPER_ATTRIBUTES:
- value = getattr(self, attr)
- if not isinstance(value, str):
- continue
-
- if not value.lower().strip().startswith(HELPER_PREFIX):
- continue
-
- helper = value[len(HELPER_PREFIX) :].strip()
- commmand = [expanduser(expandvars(token)) for token in shlex.split(helper)]
-
- try:
- value = (
- subprocess.check_output(commmand, stderr=subprocess.PIPE)
- .decode("utf-8")
- .strip()
- )
- except subprocess.CalledProcessError as e:
- stderr = e.stderr.decode().strip()
- raise GitlabConfigHelperError(
- f"Failed to read {attr} value from helper "
- f"for {self.gitlab_id}:\n{stderr}"
- ) from e
-
- setattr(self, attr, value)
diff --git a/gitlab/const.py b/gitlab/const.py
deleted file mode 100644
index c57423e..0000000
--- a/gitlab/const.py
+++ /dev/null
@@ -1,58 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2016-2017 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-from gitlab.__version__ import __title__, __version__
-
-DEFAULT_URL: str = "https://gitlab.com"
-
-NO_ACCESS: int = 0
-MINIMAL_ACCESS: int = 5
-GUEST_ACCESS: int = 10
-REPORTER_ACCESS: int = 20
-DEVELOPER_ACCESS: int = 30
-MAINTAINER_ACCESS: int = 40
-OWNER_ACCESS: int = 50
-
-VISIBILITY_PRIVATE: str = "private"
-VISIBILITY_INTERNAL: str = "internal"
-VISIBILITY_PUBLIC: str = "public"
-
-NOTIFICATION_LEVEL_DISABLED: str = "disabled"
-NOTIFICATION_LEVEL_PARTICIPATING: str = "participating"
-NOTIFICATION_LEVEL_WATCH: str = "watch"
-NOTIFICATION_LEVEL_GLOBAL: str = "global"
-NOTIFICATION_LEVEL_MENTION: str = "mention"
-NOTIFICATION_LEVEL_CUSTOM: str = "custom"
-
-# Search scopes
-# all scopes (global, group and project)
-SEARCH_SCOPE_PROJECTS: str = "projects"
-SEARCH_SCOPE_ISSUES: str = "issues"
-SEARCH_SCOPE_MERGE_REQUESTS: str = "merge_requests"
-SEARCH_SCOPE_MILESTONES: str = "milestones"
-SEARCH_SCOPE_WIKI_BLOBS: str = "wiki_blobs"
-SEARCH_SCOPE_COMMITS: str = "commits"
-SEARCH_SCOPE_BLOBS: str = "blobs"
-SEARCH_SCOPE_USERS: str = "users"
-
-# specific global scope
-SEARCH_SCOPE_GLOBAL_SNIPPET_TITLES: str = "snippet_titles"
-
-# specific project scope
-SEARCH_SCOPE_PROJECT_NOTES: str = "notes"
-
-USER_AGENT: str = "{}/{}".format(__title__, __version__)
diff --git a/gitlab/exceptions.py b/gitlab/exceptions.py
deleted file mode 100644
index 6f2d4c4..0000000
--- a/gitlab/exceptions.py
+++ /dev/null
@@ -1,310 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013-2017 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-import functools
-from typing import Any, Callable, cast, Optional, Type, TYPE_CHECKING, TypeVar, Union
-
-
-class GitlabError(Exception):
- def __init__(
- self,
- error_message: Union[str, bytes] = "",
- response_code: Optional[int] = None,
- response_body: Optional[bytes] = None,
- ) -> None:
-
- Exception.__init__(self, error_message)
- # Http status code
- self.response_code = response_code
- # Full http response
- self.response_body = response_body
- # Parsed error message from gitlab
- try:
- # if we receive str/bytes we try to convert to unicode/str to have
- # consistent message types (see #616)
- if TYPE_CHECKING:
- assert isinstance(error_message, bytes)
- self.error_message = error_message.decode()
- except Exception:
- if TYPE_CHECKING:
- assert isinstance(error_message, str)
- self.error_message = error_message
-
- def __str__(self) -> str:
- if self.response_code is not None:
- return "{0}: {1}".format(self.response_code, self.error_message)
- else:
- return "{0}".format(self.error_message)
-
-
-class GitlabAuthenticationError(GitlabError):
- pass
-
-
-class RedirectError(GitlabError):
- pass
-
-
-class GitlabParsingError(GitlabError):
- pass
-
-
-class GitlabConnectionError(GitlabError):
- pass
-
-
-class GitlabOperationError(GitlabError):
- pass
-
-
-class GitlabHttpError(GitlabError):
- pass
-
-
-class GitlabListError(GitlabOperationError):
- pass
-
-
-class GitlabGetError(GitlabOperationError):
- pass
-
-
-class GitlabCreateError(GitlabOperationError):
- pass
-
-
-class GitlabUpdateError(GitlabOperationError):
- pass
-
-
-class GitlabDeleteError(GitlabOperationError):
- pass
-
-
-class GitlabSetError(GitlabOperationError):
- pass
-
-
-class GitlabProtectError(GitlabOperationError):
- pass
-
-
-class GitlabTransferProjectError(GitlabOperationError):
- pass
-
-
-class GitlabProjectDeployKeyError(GitlabOperationError):
- pass
-
-
-class GitlabCancelError(GitlabOperationError):
- pass
-
-
-class GitlabPipelineCancelError(GitlabCancelError):
- pass
-
-
-class GitlabRetryError(GitlabOperationError):
- pass
-
-
-class GitlabBuildCancelError(GitlabCancelError):
- pass
-
-
-class GitlabBuildRetryError(GitlabRetryError):
- pass
-
-
-class GitlabBuildPlayError(GitlabRetryError):
- pass
-
-
-class GitlabBuildEraseError(GitlabRetryError):
- pass
-
-
-class GitlabJobCancelError(GitlabCancelError):
- pass
-
-
-class GitlabJobRetryError(GitlabRetryError):
- pass
-
-
-class GitlabJobPlayError(GitlabRetryError):
- pass
-
-
-class GitlabJobEraseError(GitlabRetryError):
- pass
-
-
-class GitlabPipelinePlayError(GitlabRetryError):
- pass
-
-
-class GitlabPipelineRetryError(GitlabRetryError):
- pass
-
-
-class GitlabBlockError(GitlabOperationError):
- pass
-
-
-class GitlabUnblockError(GitlabOperationError):
- pass
-
-
-class GitlabDeactivateError(GitlabOperationError):
- pass
-
-
-class GitlabActivateError(GitlabOperationError):
- pass
-
-
-class GitlabSubscribeError(GitlabOperationError):
- pass
-
-
-class GitlabUnsubscribeError(GitlabOperationError):
- pass
-
-
-class GitlabMRForbiddenError(GitlabOperationError):
- pass
-
-
-class GitlabMRApprovalError(GitlabOperationError):
- pass
-
-
-class GitlabMRRebaseError(GitlabOperationError):
- pass
-
-
-class GitlabMRClosedError(GitlabOperationError):
- pass
-
-
-class GitlabMROnBuildSuccessError(GitlabOperationError):
- pass
-
-
-class GitlabTodoError(GitlabOperationError):
- pass
-
-
-class GitlabTimeTrackingError(GitlabOperationError):
- pass
-
-
-class GitlabUploadError(GitlabOperationError):
- pass
-
-
-class GitlabAttachFileError(GitlabOperationError):
- pass
-
-
-class GitlabImportError(GitlabOperationError):
- pass
-
-
-class GitlabCherryPickError(GitlabOperationError):
- pass
-
-
-class GitlabHousekeepingError(GitlabOperationError):
- pass
-
-
-class GitlabOwnershipError(GitlabOperationError):
- pass
-
-
-class GitlabSearchError(GitlabOperationError):
- pass
-
-
-class GitlabStopError(GitlabOperationError):
- pass
-
-
-class GitlabMarkdownError(GitlabOperationError):
- pass
-
-
-class GitlabVerifyError(GitlabOperationError):
- pass
-
-
-class GitlabRenderError(GitlabOperationError):
- pass
-
-
-class GitlabRepairError(GitlabOperationError):
- pass
-
-
-class GitlabRevertError(GitlabOperationError):
- pass
-
-
-class GitlabLicenseError(GitlabOperationError):
- pass
-
-
-class GitlabFollowError(GitlabOperationError):
- pass
-
-
-class GitlabUnfollowError(GitlabOperationError):
- pass
-
-
-# For an explanation of how these type-hints work see:
-# https://mypy.readthedocs.io/en/stable/generics.html#declaring-decorators
-#
-# The goal here is that functions which get decorated will retain their types.
-__F = TypeVar("__F", bound=Callable[..., Any])
-
-
-def on_http_error(error: Type[Exception]) -> Callable[[__F], __F]:
- """Manage GitlabHttpError exceptions.
-
- This decorator function can be used to catch GitlabHttpError exceptions
- raise specialized exceptions instead.
-
- Args:
- error(Exception): The exception type to raise -- must inherit from
- GitlabError
- """
-
- def wrap(f: __F) -> __F:
- @functools.wraps(f)
- def wrapped_f(*args: Any, **kwargs: Any) -> Any:
- try:
- return f(*args, **kwargs)
- except GitlabHttpError as e:
- raise error(e.error_message, e.response_code, e.response_body) from e
-
- return cast(__F, wrapped_f)
-
- return wrap
diff --git a/gitlab/mixins.py b/gitlab/mixins.py
deleted file mode 100644
index 0c2cd94..0000000
--- a/gitlab/mixins.py
+++ /dev/null
@@ -1,928 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013-2017 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-from types import ModuleType
-from typing import (
- Any,
- Callable,
- Dict,
- List,
- Optional,
- Tuple,
- Type,
- TYPE_CHECKING,
- Union,
-)
-
-import requests
-
-import gitlab
-from gitlab import base, cli
-from gitlab import exceptions as exc
-from gitlab import types as g_types
-from gitlab import utils
-
-__all__ = [
- "GetMixin",
- "GetWithoutIdMixin",
- "RefreshMixin",
- "ListMixin",
- "RetrieveMixin",
- "CreateMixin",
- "UpdateMixin",
- "SetMixin",
- "DeleteMixin",
- "CRUDMixin",
- "NoUpdateMixin",
- "SaveMixin",
- "ObjectDeleteMixin",
- "UserAgentDetailMixin",
- "AccessRequestMixin",
- "DownloadMixin",
- "SubscribableMixin",
- "TodoMixin",
- "TimeTrackingMixin",
- "ParticipantsMixin",
- "BadgeRenderMixin",
-]
-
-if TYPE_CHECKING:
- # When running mypy we use these as the base classes
- _RestManagerBase = base.RESTManager
- _RestObjectBase = base.RESTObject
-else:
- _RestManagerBase = object
- _RestObjectBase = object
-
-
-class GetMixin(_RestManagerBase):
- _computed_path: Optional[str]
- _from_parent_attrs: Dict[str, Any]
- _obj_cls: Optional[Type[base.RESTObject]]
- _optional_get_attrs: Tuple[str, ...] = ()
- _parent: Optional[base.RESTObject]
- _parent_attrs: Dict[str, Any]
- _path: Optional[str]
- gitlab: gitlab.Gitlab
-
- @exc.on_http_error(exc.GitlabGetError)
- def get(
- self, id: Union[str, int], lazy: bool = False, **kwargs: Any
- ) -> base.RESTObject:
- """Retrieve a single object.
-
- Args:
- id (int or str): ID of the object to retrieve
- lazy (bool): If True, don't request the server, but create a
- shallow object giving access to the managers. This is
- useful if you want to avoid useless calls to the API.
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- object: The generated RESTObject.
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server cannot perform the request
- """
- if not isinstance(id, int):
- id = utils.clean_str_id(id)
- path = "%s/%s" % (self.path, id)
- if TYPE_CHECKING:
- assert self._obj_cls is not None
- if lazy is True:
- if TYPE_CHECKING:
- assert self._obj_cls._id_attr is not None
- return self._obj_cls(self, {self._obj_cls._id_attr: id})
- server_data = self.gitlab.http_get(path, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(server_data, requests.Response)
- return self._obj_cls(self, server_data)
-
-
-class GetWithoutIdMixin(_RestManagerBase):
- _computed_path: Optional[str]
- _from_parent_attrs: Dict[str, Any]
- _obj_cls: Optional[Type[base.RESTObject]]
- _optional_get_attrs: Tuple[str, ...] = ()
- _parent: Optional[base.RESTObject]
- _parent_attrs: Dict[str, Any]
- _path: Optional[str]
- gitlab: gitlab.Gitlab
-
- @exc.on_http_error(exc.GitlabGetError)
- def get(
- self, id: Optional[Union[int, str]] = None, **kwargs: Any
- ) -> Optional[base.RESTObject]:
- """Retrieve a single object.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- object: The generated RESTObject
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server cannot perform the request
- """
- if TYPE_CHECKING:
- assert self.path is not None
- server_data = self.gitlab.http_get(self.path, **kwargs)
- if server_data is None:
- return None
- if TYPE_CHECKING:
- assert not isinstance(server_data, requests.Response)
- assert self._obj_cls is not None
- return self._obj_cls(self, server_data)
-
-
-class RefreshMixin(_RestObjectBase):
- _id_attr: Optional[str]
- _attrs: Dict[str, Any]
- _module: ModuleType
- _parent_attrs: Dict[str, Any]
- _updated_attrs: Dict[str, Any]
- manager: base.RESTManager
-
- @exc.on_http_error(exc.GitlabGetError)
- def refresh(self, **kwargs: Any) -> None:
- """Refresh a single object from server.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns None (updates the object)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server cannot perform the request
- """
- if self._id_attr:
- path = "%s/%s" % (self.manager.path, self.id)
- else:
- if TYPE_CHECKING:
- assert self.manager.path is not None
- path = self.manager.path
- server_data = self.manager.gitlab.http_get(path, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(server_data, requests.Response)
- self._update_attrs(server_data)
-
-
-class ListMixin(_RestManagerBase):
- _computed_path: Optional[str]
- _from_parent_attrs: Dict[str, Any]
- _list_filters: Tuple[str, ...] = ()
- _obj_cls: Optional[Type[base.RESTObject]]
- _parent: Optional[base.RESTObject]
- _parent_attrs: Dict[str, Any]
- _path: Optional[str]
- gitlab: gitlab.Gitlab
-
- @exc.on_http_error(exc.GitlabListError)
- def list(self, **kwargs: Any) -> Union[base.RESTObjectList, List[base.RESTObject]]:
- """Retrieve a list of objects.
-
- Args:
- all (bool): If True, return all the items, without pagination
- per_page (int): Number of items to retrieve per request
- page (int): ID of the page to return (starts with page 1)
- as_list (bool): If set to False and no pagination option is
- defined, return a generator instead of a list
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- list: The list of objects, or a generator if `as_list` is False
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the server cannot perform the request
- """
-
- # Duplicate data to avoid messing with what the user sent us
- data = kwargs.copy()
- if self.gitlab.per_page:
- data.setdefault("per_page", self.gitlab.per_page)
-
- # global keyset pagination
- if self.gitlab.pagination:
- data.setdefault("pagination", self.gitlab.pagination)
-
- if self.gitlab.order_by:
- data.setdefault("order_by", self.gitlab.order_by)
-
- # We get the attributes that need some special transformation
- if self._types:
- for attr_name, type_cls in self._types.items():
- if attr_name in data.keys():
- type_obj = type_cls(data[attr_name])
- data[attr_name] = type_obj.get_for_api()
-
- # Allow to overwrite the path, handy for custom listings
- path = data.pop("path", self.path)
-
- if TYPE_CHECKING:
- assert self._obj_cls is not None
- obj = self.gitlab.http_list(path, **data)
- if isinstance(obj, list):
- return [self._obj_cls(self, item) for item in obj]
- else:
- return base.RESTObjectList(self, self._obj_cls, obj)
-
-
-class RetrieveMixin(ListMixin, GetMixin):
- _computed_path: Optional[str]
- _from_parent_attrs: Dict[str, Any]
- _obj_cls: Optional[Type[base.RESTObject]]
- _parent: Optional[base.RESTObject]
- _parent_attrs: Dict[str, Any]
- _path: Optional[str]
- gitlab: gitlab.Gitlab
-
- pass
-
-
-class CreateMixin(_RestManagerBase):
- _computed_path: Optional[str]
- _from_parent_attrs: Dict[str, Any]
- _obj_cls: Optional[Type[base.RESTObject]]
- _parent: Optional[base.RESTObject]
- _parent_attrs: Dict[str, Any]
- _path: Optional[str]
- gitlab: gitlab.Gitlab
-
- def _check_missing_create_attrs(self, data: Dict[str, Any]) -> None:
- missing = []
- for attr in self._create_attrs.required:
- if attr not in data:
- missing.append(attr)
- continue
- if missing:
- raise AttributeError("Missing attributes: %s" % ", ".join(missing))
-
- @exc.on_http_error(exc.GitlabCreateError)
- def create(
- self, data: Optional[Dict[str, Any]] = None, **kwargs: Any
- ) -> base.RESTObject:
- """Create a new object.
-
- Args:
- data (dict): parameters to send to the server to create the
- resource
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- RESTObject: a new instance of the managed object class built with
- the data sent by the server
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server cannot perform the request
- """
- if data is None:
- data = {}
-
- self._check_missing_create_attrs(data)
- files = {}
-
- # We get the attributes that need some special transformation
- if self._types:
- # Duplicate data to avoid messing with what the user sent us
- data = data.copy()
- for attr_name, type_cls in self._types.items():
- if attr_name in data.keys():
- type_obj = type_cls(data[attr_name])
-
- # if the type if FileAttribute we need to pass the data as
- # file
- if isinstance(type_obj, g_types.FileAttribute):
- k = type_obj.get_file_name(attr_name)
- files[attr_name] = (k, data.pop(attr_name))
- else:
- data[attr_name] = type_obj.get_for_api()
-
- # Handle specific URL for creation
- path = kwargs.pop("path", self.path)
- server_data = self.gitlab.http_post(path, post_data=data, files=files, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(server_data, requests.Response)
- assert self._obj_cls is not None
- return self._obj_cls(self, server_data)
-
-
-class UpdateMixin(_RestManagerBase):
- _computed_path: Optional[str]
- _from_parent_attrs: Dict[str, Any]
- _obj_cls: Optional[Type[base.RESTObject]]
- _parent: Optional[base.RESTObject]
- _parent_attrs: Dict[str, Any]
- _path: Optional[str]
- _update_uses_post: bool = False
- gitlab: gitlab.Gitlab
-
- def _check_missing_update_attrs(self, data: Dict[str, Any]) -> None:
- if TYPE_CHECKING:
- assert self._obj_cls is not None
- # Remove the id field from the required list as it was previously moved
- # to the http path.
- required = tuple(
- [k for k in self._update_attrs.required if k != self._obj_cls._id_attr]
- )
- missing = []
- for attr in required:
- if attr not in data:
- missing.append(attr)
- continue
- if missing:
- raise AttributeError("Missing attributes: %s" % ", ".join(missing))
-
- def _get_update_method(
- self,
- ) -> Callable[..., Union[Dict[str, Any], requests.Response]]:
- """Return the HTTP method to use.
-
- Returns:
- object: http_put (default) or http_post
- """
- if self._update_uses_post:
- http_method = self.gitlab.http_post
- else:
- http_method = self.gitlab.http_put
- return http_method
-
- @exc.on_http_error(exc.GitlabUpdateError)
- def update(
- self,
- id: Optional[Union[str, int]] = None,
- new_data: Optional[Dict[str, Any]] = None,
- **kwargs: Any
- ) -> Dict[str, Any]:
- """Update an object on the server.
-
- Args:
- id: ID of the object to update (can be None if not required)
- new_data: the update data for the object
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- dict: The new object data (*not* a RESTObject)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabUpdateError: If the server cannot perform the request
- """
- new_data = new_data or {}
-
- if id is None:
- path = self.path
- else:
- path = "%s/%s" % (self.path, id)
-
- self._check_missing_update_attrs(new_data)
- files = {}
-
- # We get the attributes that need some special transformation
- if self._types:
- # Duplicate data to avoid messing with what the user sent us
- new_data = new_data.copy()
- for attr_name, type_cls in self._types.items():
- if attr_name in new_data.keys():
- type_obj = type_cls(new_data[attr_name])
-
- # if the type if FileAttribute we need to pass the data as
- # file
- if isinstance(type_obj, g_types.FileAttribute):
- k = type_obj.get_file_name(attr_name)
- files[attr_name] = (k, new_data.pop(attr_name))
- else:
- new_data[attr_name] = type_obj.get_for_api()
-
- http_method = self._get_update_method()
- result = http_method(path, post_data=new_data, files=files, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(result, requests.Response)
- return result
-
-
-class SetMixin(_RestManagerBase):
- _computed_path: Optional[str]
- _from_parent_attrs: Dict[str, Any]
- _obj_cls: Optional[Type[base.RESTObject]]
- _parent: Optional[base.RESTObject]
- _parent_attrs: Dict[str, Any]
- _path: Optional[str]
- gitlab: gitlab.Gitlab
-
- @exc.on_http_error(exc.GitlabSetError)
- def set(self, key: str, value: str, **kwargs: Any) -> base.RESTObject:
- """Create or update the object.
-
- Args:
- key (str): The key of the object to create/update
- value (str): The value to set for the object
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabSetError: If an error occurred
-
- Returns:
- obj: The created/updated attribute
- """
- path = "%s/%s" % (self.path, utils.clean_str_id(key))
- data = {"value": value}
- server_data = self.gitlab.http_put(path, post_data=data, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(server_data, requests.Response)
- assert self._obj_cls is not None
- return self._obj_cls(self, server_data)
-
-
-class DeleteMixin(_RestManagerBase):
- _computed_path: Optional[str]
- _from_parent_attrs: Dict[str, Any]
- _obj_cls: Optional[Type[base.RESTObject]]
- _parent: Optional[base.RESTObject]
- _parent_attrs: Dict[str, Any]
- _path: Optional[str]
- gitlab: gitlab.Gitlab
-
- @exc.on_http_error(exc.GitlabDeleteError)
- def delete(self, id: Union[str, int], **kwargs: Any) -> None:
- """Delete an object on the server.
-
- Args:
- id: ID of the object to delete
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabDeleteError: If the server cannot perform the request
- """
- if id is None:
- path = self.path
- else:
- if not isinstance(id, int):
- id = utils.clean_str_id(id)
- path = "%s/%s" % (self.path, id)
- self.gitlab.http_delete(path, **kwargs)
-
-
-class CRUDMixin(GetMixin, ListMixin, CreateMixin, UpdateMixin, DeleteMixin):
- _computed_path: Optional[str]
- _from_parent_attrs: Dict[str, Any]
- _obj_cls: Optional[Type[base.RESTObject]]
- _parent: Optional[base.RESTObject]
- _parent_attrs: Dict[str, Any]
- _path: Optional[str]
- gitlab: gitlab.Gitlab
-
- pass
-
-
-class NoUpdateMixin(GetMixin, ListMixin, CreateMixin, DeleteMixin):
- _computed_path: Optional[str]
- _from_parent_attrs: Dict[str, Any]
- _obj_cls: Optional[Type[base.RESTObject]]
- _parent: Optional[base.RESTObject]
- _parent_attrs: Dict[str, Any]
- _path: Optional[str]
- gitlab: gitlab.Gitlab
-
- pass
-
-
-class SaveMixin(_RestObjectBase):
- """Mixin for RESTObject's that can be updated."""
-
- _id_attr: Optional[str]
- _attrs: Dict[str, Any]
- _module: ModuleType
- _parent_attrs: Dict[str, Any]
- _updated_attrs: Dict[str, Any]
- manager: base.RESTManager
-
- def _get_updated_data(self) -> Dict[str, Any]:
- updated_data = {}
- for attr in self.manager._update_attrs.required:
- # Get everything required, no matter if it's been updated
- updated_data[attr] = getattr(self, attr)
- # Add the updated attributes
- updated_data.update(self._updated_attrs)
-
- return updated_data
-
- def save(self, **kwargs: Any) -> None:
- """Save the changes made to the object to the server.
-
- The object is updated to match what the server returns.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raise:
- GitlabAuthenticationError: If authentication is not correct
- GitlabUpdateError: If the server cannot perform the request
- """
- updated_data = self._get_updated_data()
- # Nothing to update. Server fails if sent an empty dict.
- if not updated_data:
- return
-
- # call the manager
- obj_id = self.get_id()
- if TYPE_CHECKING:
- assert isinstance(self.manager, UpdateMixin)
- server_data = self.manager.update(obj_id, updated_data, **kwargs)
- if server_data is not None:
- self._update_attrs(server_data)
-
-
-class ObjectDeleteMixin(_RestObjectBase):
- """Mixin for RESTObject's that can be deleted."""
-
- _id_attr: Optional[str]
- _attrs: Dict[str, Any]
- _module: ModuleType
- _parent_attrs: Dict[str, Any]
- _updated_attrs: Dict[str, Any]
- manager: base.RESTManager
-
- def delete(self, **kwargs: Any) -> None:
- """Delete the object from the server.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabDeleteError: If the server cannot perform the request
- """
- if TYPE_CHECKING:
- assert isinstance(self.manager, DeleteMixin)
- self.manager.delete(self.get_id(), **kwargs)
-
-
-class UserAgentDetailMixin(_RestObjectBase):
- _id_attr: Optional[str]
- _attrs: Dict[str, Any]
- _module: ModuleType
- _parent_attrs: Dict[str, Any]
- _updated_attrs: Dict[str, Any]
- manager: base.RESTManager
-
- @cli.register_custom_action(("Snippet", "ProjectSnippet", "ProjectIssue"))
- @exc.on_http_error(exc.GitlabGetError)
- def user_agent_detail(self, **kwargs: Any) -> Dict[str, Any]:
- """Get the user agent detail.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server cannot perform the request
- """
- path = "%s/%s/user_agent_detail" % (self.manager.path, self.get_id())
- result = self.manager.gitlab.http_get(path, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(result, requests.Response)
- return result
-
-
-class AccessRequestMixin(_RestObjectBase):
- _id_attr: Optional[str]
- _attrs: Dict[str, Any]
- _module: ModuleType
- _parent_attrs: Dict[str, Any]
- _updated_attrs: Dict[str, Any]
- manager: base.RESTManager
-
- @cli.register_custom_action(
- ("ProjectAccessRequest", "GroupAccessRequest"), tuple(), ("access_level",)
- )
- @exc.on_http_error(exc.GitlabUpdateError)
- def approve(
- self, access_level: int = gitlab.DEVELOPER_ACCESS, **kwargs: Any
- ) -> None:
- """Approve an access request.
-
- Args:
- access_level (int): The access level for the user
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabUpdateError: If the server fails to perform the request
- """
-
- path = "%s/%s/approve" % (self.manager.path, self.id)
- data = {"access_level": access_level}
- server_data = self.manager.gitlab.http_put(path, post_data=data, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(server_data, requests.Response)
- self._update_attrs(server_data)
-
-
-class DownloadMixin(_RestObjectBase):
- _id_attr: Optional[str]
- _attrs: Dict[str, Any]
- _module: ModuleType
- _parent_attrs: Dict[str, Any]
- _updated_attrs: Dict[str, Any]
- manager: base.RESTManager
-
- @cli.register_custom_action(("GroupExport", "ProjectExport"))
- @exc.on_http_error(exc.GitlabGetError)
- def download(
- self,
- streamed: bool = False,
- action: Optional[Callable] = None,
- chunk_size: int = 1024,
- **kwargs: Any
- ) -> Optional[bytes]:
- """Download the archive of a resource export.
-
- Args:
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment
- action (callable): Callable responsible of dealing with chunk of
- data
- chunk_size (int): Size of each chunk
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server failed to perform the request
-
- Returns:
- str: The blob content if streamed is False, None otherwise
- """
- path = "%s/download" % (self.manager.path)
- result = self.manager.gitlab.http_get(
- path, streamed=streamed, raw=True, **kwargs
- )
- if TYPE_CHECKING:
- assert isinstance(result, requests.Response)
- return utils.response_content(result, streamed, action, chunk_size)
-
-
-class SubscribableMixin(_RestObjectBase):
- _id_attr: Optional[str]
- _attrs: Dict[str, Any]
- _module: ModuleType
- _parent_attrs: Dict[str, Any]
- _updated_attrs: Dict[str, Any]
- manager: base.RESTManager
-
- @cli.register_custom_action(
- ("ProjectIssue", "ProjectMergeRequest", "ProjectLabel", "GroupLabel")
- )
- @exc.on_http_error(exc.GitlabSubscribeError)
- def subscribe(self, **kwargs: Any) -> None:
- """Subscribe to the object notifications.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabSubscribeError: If the subscription cannot be done
- """
- path = "%s/%s/subscribe" % (self.manager.path, self.get_id())
- server_data = self.manager.gitlab.http_post(path, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(server_data, requests.Response)
- self._update_attrs(server_data)
-
- @cli.register_custom_action(
- ("ProjectIssue", "ProjectMergeRequest", "ProjectLabel", "GroupLabel")
- )
- @exc.on_http_error(exc.GitlabUnsubscribeError)
- def unsubscribe(self, **kwargs: Any) -> None:
- """Unsubscribe from the object notifications.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabUnsubscribeError: If the unsubscription cannot be done
- """
- path = "%s/%s/unsubscribe" % (self.manager.path, self.get_id())
- server_data = self.manager.gitlab.http_post(path, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(server_data, requests.Response)
- self._update_attrs(server_data)
-
-
-class TodoMixin(_RestObjectBase):
- _id_attr: Optional[str]
- _attrs: Dict[str, Any]
- _module: ModuleType
- _parent_attrs: Dict[str, Any]
- _updated_attrs: Dict[str, Any]
- manager: base.RESTManager
-
- @cli.register_custom_action(("ProjectIssue", "ProjectMergeRequest"))
- @exc.on_http_error(exc.GitlabTodoError)
- def todo(self, **kwargs: Any) -> None:
- """Create a todo associated to the object.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabTodoError: If the todo cannot be set
- """
- path = "%s/%s/todo" % (self.manager.path, self.get_id())
- self.manager.gitlab.http_post(path, **kwargs)
-
-
-class TimeTrackingMixin(_RestObjectBase):
- _id_attr: Optional[str]
- _attrs: Dict[str, Any]
- _module: ModuleType
- _parent_attrs: Dict[str, Any]
- _updated_attrs: Dict[str, Any]
- manager: base.RESTManager
-
- @cli.register_custom_action(("ProjectIssue", "ProjectMergeRequest"))
- @exc.on_http_error(exc.GitlabTimeTrackingError)
- def time_stats(self, **kwargs: Any) -> Dict[str, Any]:
- """Get time stats for the object.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabTimeTrackingError: If the time tracking update cannot be done
- """
- # Use the existing time_stats attribute if it exist, otherwise make an
- # API call
- if "time_stats" in self.attributes:
- return self.attributes["time_stats"]
-
- path = "%s/%s/time_stats" % (self.manager.path, self.get_id())
- result = self.manager.gitlab.http_get(path, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(result, requests.Response)
- return result
-
- @cli.register_custom_action(("ProjectIssue", "ProjectMergeRequest"), ("duration",))
- @exc.on_http_error(exc.GitlabTimeTrackingError)
- def time_estimate(self, duration: str, **kwargs: Any) -> Dict[str, Any]:
- """Set an estimated time of work for the object.
-
- Args:
- duration (str): Duration in human format (e.g. 3h30)
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabTimeTrackingError: If the time tracking update cannot be done
- """
- path = "%s/%s/time_estimate" % (self.manager.path, self.get_id())
- data = {"duration": duration}
- result = self.manager.gitlab.http_post(path, post_data=data, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(result, requests.Response)
- return result
-
- @cli.register_custom_action(("ProjectIssue", "ProjectMergeRequest"))
- @exc.on_http_error(exc.GitlabTimeTrackingError)
- def reset_time_estimate(self, **kwargs: Any) -> Dict[str, Any]:
- """Resets estimated time for the object to 0 seconds.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabTimeTrackingError: If the time tracking update cannot be done
- """
- path = "%s/%s/reset_time_estimate" % (self.manager.path, self.get_id())
- result = self.manager.gitlab.http_post(path, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(result, requests.Response)
- return result
-
- @cli.register_custom_action(("ProjectIssue", "ProjectMergeRequest"), ("duration",))
- @exc.on_http_error(exc.GitlabTimeTrackingError)
- def add_spent_time(self, duration: str, **kwargs: Any) -> Dict[str, Any]:
- """Add time spent working on the object.
-
- Args:
- duration (str): Duration in human format (e.g. 3h30)
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabTimeTrackingError: If the time tracking update cannot be done
- """
- path = "%s/%s/add_spent_time" % (self.manager.path, self.get_id())
- data = {"duration": duration}
- result = self.manager.gitlab.http_post(path, post_data=data, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(result, requests.Response)
- return result
-
- @cli.register_custom_action(("ProjectIssue", "ProjectMergeRequest"))
- @exc.on_http_error(exc.GitlabTimeTrackingError)
- def reset_spent_time(self, **kwargs: Any) -> Dict[str, Any]:
- """Resets the time spent working on the object.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabTimeTrackingError: If the time tracking update cannot be done
- """
- path = "%s/%s/reset_spent_time" % (self.manager.path, self.get_id())
- result = self.manager.gitlab.http_post(path, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(result, requests.Response)
- return result
-
-
-class ParticipantsMixin(_RestObjectBase):
- _id_attr: Optional[str]
- _attrs: Dict[str, Any]
- _module: ModuleType
- _parent_attrs: Dict[str, Any]
- _updated_attrs: Dict[str, Any]
- manager: base.RESTManager
-
- @cli.register_custom_action(("ProjectMergeRequest", "ProjectIssue"))
- @exc.on_http_error(exc.GitlabListError)
- def participants(self, **kwargs: Any) -> Dict[str, Any]:
- """List the participants.
-
- Args:
- all (bool): If True, return all the items, without pagination
- per_page (int): Number of items to retrieve per request
- page (int): ID of the page to return (starts with page 1)
- as_list (bool): If set to False and no pagination option is
- defined, return a generator instead of a list
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the list could not be retrieved
-
- Returns:
- RESTObjectList: The list of participants
- """
-
- path = "%s/%s/participants" % (self.manager.path, self.get_id())
- result = self.manager.gitlab.http_get(path, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(result, requests.Response)
- return result
-
-
-class BadgeRenderMixin(_RestManagerBase):
- @cli.register_custom_action(
- ("GroupBadgeManager", "ProjectBadgeManager"), ("link_url", "image_url")
- )
- @exc.on_http_error(exc.GitlabRenderError)
- def render(self, link_url: str, image_url: str, **kwargs: Any) -> Dict[str, Any]:
- """Preview link_url and image_url after interpolation.
-
- Args:
- link_url (str): URL of the badge link
- image_url (str): URL of the badge image
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabRenderError: If the rendering failed
-
- Returns:
- dict: The rendering properties
- """
- path = "%s/render" % self.path
- data = {"link_url": link_url, "image_url": image_url}
- result = self.gitlab.http_get(path, data, **kwargs)
- if TYPE_CHECKING:
- assert not isinstance(result, requests.Response)
- return result
diff --git a/gitlab/py.typed b/gitlab/py.typed
deleted file mode 100644
index e69de29..0000000
--- a/gitlab/py.typed
+++ /dev/null
diff --git a/gitlab/types.py b/gitlab/types.py
deleted file mode 100644
index 22d51e7..0000000
--- a/gitlab/types.py
+++ /dev/null
@@ -1,64 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2018 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-from typing import Any, Optional, TYPE_CHECKING
-
-
-class GitlabAttribute(object):
- def __init__(self, value: Any = None) -> None:
- self._value = value
-
- def get(self) -> Any:
- return self._value
-
- def set_from_cli(self, cli_value: Any) -> None:
- self._value = cli_value
-
- def get_for_api(self) -> Any:
- return self._value
-
-
-class ListAttribute(GitlabAttribute):
- def set_from_cli(self, cli_value: str) -> None:
- if not cli_value.strip():
- self._value = []
- else:
- self._value = [item.strip() for item in cli_value.split(",")]
-
- def get_for_api(self) -> str:
- # Do not comma-split single value passed as string
- if isinstance(self._value, str):
- return self._value
-
- if TYPE_CHECKING:
- assert isinstance(self._value, list)
- return ",".join([str(x) for x in self._value])
-
-
-class LowercaseStringAttribute(GitlabAttribute):
- def get_for_api(self) -> str:
- return str(self._value).lower()
-
-
-class FileAttribute(GitlabAttribute):
- def get_file_name(self, attr_name: Optional[str] = None) -> Optional[str]:
- return attr_name
-
-
-class ImageAttribute(FileAttribute):
- def get_file_name(self, attr_name: Optional[str] = None) -> str:
- return "%s.png" % attr_name if attr_name else "image.png"
diff --git a/gitlab/utils.py b/gitlab/utils.py
deleted file mode 100644
index 91b3fb0..0000000
--- a/gitlab/utils.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2016-2017 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-from typing import Any, Callable, Dict, Optional
-from urllib.parse import quote, urlparse
-
-import requests
-
-
-class _StdoutStream(object):
- def __call__(self, chunk: Any) -> None:
- print(chunk)
-
-
-def response_content(
- response: requests.Response,
- streamed: bool,
- action: Optional[Callable],
- chunk_size: int,
-) -> Optional[bytes]:
- if streamed is False:
- return response.content
-
- if action is None:
- action = _StdoutStream()
-
- for chunk in response.iter_content(chunk_size=chunk_size):
- if chunk:
- action(chunk)
- return None
-
-
-def copy_dict(dest: Dict[str, Any], src: Dict[str, Any]) -> None:
- for k, v in src.items():
- if isinstance(v, dict):
- # Transform dict values to new attributes. For example:
- # custom_attributes: {'foo', 'bar'} =>
- # "custom_attributes['foo']": "bar"
- for dict_k, dict_v in v.items():
- dest["%s[%s]" % (k, dict_k)] = dict_v
- else:
- dest[k] = v
-
-
-def clean_str_id(id: str) -> str:
- return quote(id, safe="")
-
-
-def sanitized_url(url: str) -> str:
- parsed = urlparse(url)
- new_path = parsed.path.replace(".", "%2E")
- return parsed._replace(path=new_path).geturl()
-
-
-def remove_none_from_dict(data: Dict[str, Any]) -> Dict[str, Any]:
- return {k: v for k, v in data.items() if v is not None}
diff --git a/gitlab/v4/__init__.py b/gitlab/v4/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/gitlab/v4/__init__.py
+++ /dev/null
diff --git a/gitlab/v4/cli.py b/gitlab/v4/cli.py
deleted file mode 100644
index 6986552..0000000
--- a/gitlab/v4/cli.py
+++ /dev/null
@@ -1,500 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013-2017 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-import argparse
-import operator
-import sys
-from typing import Any, Dict, List, Optional, Type, TYPE_CHECKING, Union
-
-import gitlab
-import gitlab.base
-import gitlab.v4.objects
-from gitlab import cli
-
-
-class GitlabCLI(object):
- def __init__(
- self, gl: gitlab.Gitlab, what: str, action: str, args: Dict[str, str]
- ) -> None:
- self.cls: Type[gitlab.base.RESTObject] = cli.what_to_cls(
- what, namespace=gitlab.v4.objects
- )
- self.cls_name = self.cls.__name__
- self.what = what.replace("-", "_")
- self.action = action.lower()
- self.gl = gl
- self.args = args
- self.mgr_cls: Union[
- Type[gitlab.mixins.CreateMixin],
- Type[gitlab.mixins.DeleteMixin],
- Type[gitlab.mixins.GetMixin],
- Type[gitlab.mixins.GetWithoutIdMixin],
- Type[gitlab.mixins.ListMixin],
- Type[gitlab.mixins.UpdateMixin],
- ] = getattr(gitlab.v4.objects, self.cls.__name__ + "Manager")
- # We could do something smart, like splitting the manager name to find
- # parents, build the chain of managers to get to the final object.
- # Instead we do something ugly and efficient: interpolate variables in
- # the class _path attribute, and replace the value with the result.
- if TYPE_CHECKING:
- assert self.mgr_cls._path is not None
- self.mgr_cls._path = self.mgr_cls._path % self.args
- self.mgr = self.mgr_cls(gl)
-
- if self.mgr_cls._types:
- for attr_name, type_cls in self.mgr_cls._types.items():
- if attr_name in self.args.keys():
- obj = type_cls()
- obj.set_from_cli(self.args[attr_name])
- self.args[attr_name] = obj.get()
-
- def __call__(self) -> Any:
- # Check for a method that matches object + action
- method = "do_%s_%s" % (self.what, self.action)
- if hasattr(self, method):
- return getattr(self, method)()
-
- # Fallback to standard actions (get, list, create, ...)
- method = "do_%s" % self.action
- if hasattr(self, method):
- return getattr(self, method)()
-
- # Finally try to find custom methods
- return self.do_custom()
-
- def do_custom(self) -> Any:
- in_obj = cli.custom_actions[self.cls_name][self.action][2]
-
- # Get the object (lazy), then act
- if in_obj:
- data = {}
- if self.mgr._from_parent_attrs:
- for k in self.mgr._from_parent_attrs:
- data[k] = self.args[k]
- if not issubclass(self.cls, gitlab.mixins.GetWithoutIdMixin):
- if TYPE_CHECKING:
- assert isinstance(self.cls._id_attr, str)
- data[self.cls._id_attr] = self.args.pop(self.cls._id_attr)
- obj = self.cls(self.mgr, data)
- method_name = self.action.replace("-", "_")
- return getattr(obj, method_name)(**self.args)
- else:
- return getattr(self.mgr, self.action)(**self.args)
-
- def do_project_export_download(self) -> None:
- try:
- project = self.gl.projects.get(int(self.args["project_id"]), lazy=True)
- export_status = project.exports.get()
- if TYPE_CHECKING:
- assert export_status is not None
- data = export_status.download()
- sys.stdout.buffer.write(data)
-
- except Exception as e:
- cli.die("Impossible to download the export", e)
-
- def do_create(self) -> gitlab.base.RESTObject:
- if TYPE_CHECKING:
- assert isinstance(self.mgr, gitlab.mixins.CreateMixin)
- try:
- result = self.mgr.create(self.args)
- except Exception as e:
- cli.die("Impossible to create object", e)
- return result
-
- def do_list(
- self,
- ) -> Union[gitlab.base.RESTObjectList, List[gitlab.base.RESTObject]]:
- if TYPE_CHECKING:
- assert isinstance(self.mgr, gitlab.mixins.ListMixin)
- try:
- result = self.mgr.list(**self.args)
- except Exception as e:
- cli.die("Impossible to list objects", e)
- return result
-
- def do_get(self) -> Optional[gitlab.base.RESTObject]:
- if isinstance(self.mgr, gitlab.mixins.GetWithoutIdMixin):
- try:
- result = self.mgr.get(id=None, **self.args)
- except Exception as e:
- cli.die("Impossible to get object", e)
- return result
-
- if TYPE_CHECKING:
- assert isinstance(self.mgr, gitlab.mixins.GetMixin)
- assert isinstance(self.cls._id_attr, str)
-
- id = self.args.pop(self.cls._id_attr)
- try:
- result = self.mgr.get(id, lazy=False, **self.args)
- except Exception as e:
- cli.die("Impossible to get object", e)
- return result
-
- def do_delete(self) -> None:
- if TYPE_CHECKING:
- assert isinstance(self.mgr, gitlab.mixins.DeleteMixin)
- assert isinstance(self.cls._id_attr, str)
- id = self.args.pop(self.cls._id_attr)
- try:
- self.mgr.delete(id, **self.args)
- except Exception as e:
- cli.die("Impossible to destroy object", e)
-
- def do_update(self) -> Dict[str, Any]:
- if TYPE_CHECKING:
- assert isinstance(self.mgr, gitlab.mixins.UpdateMixin)
- if issubclass(self.mgr_cls, gitlab.mixins.GetWithoutIdMixin):
- id = None
- else:
- if TYPE_CHECKING:
- assert isinstance(self.cls._id_attr, str)
- id = self.args.pop(self.cls._id_attr)
-
- try:
- result = self.mgr.update(id, self.args)
- except Exception as e:
- cli.die("Impossible to update object", e)
- return result
-
-
-def _populate_sub_parser_by_class(
- cls: Type[gitlab.base.RESTObject], sub_parser: argparse._SubParsersAction
-) -> None:
- mgr_cls_name = cls.__name__ + "Manager"
- mgr_cls = getattr(gitlab.v4.objects, mgr_cls_name)
-
- for action_name in ["list", "get", "create", "update", "delete"]:
- if not hasattr(mgr_cls, action_name):
- continue
-
- sub_parser_action = sub_parser.add_parser(action_name)
- sub_parser_action.add_argument("--sudo", required=False)
- if mgr_cls._from_parent_attrs:
- for x in mgr_cls._from_parent_attrs:
- sub_parser_action.add_argument(
- "--%s" % x.replace("_", "-"), required=True
- )
-
- if action_name == "list":
- for x in mgr_cls._list_filters:
- sub_parser_action.add_argument(
- "--%s" % x.replace("_", "-"), required=False
- )
-
- sub_parser_action.add_argument("--page", required=False)
- sub_parser_action.add_argument("--per-page", required=False)
- sub_parser_action.add_argument("--all", required=False, action="store_true")
-
- if action_name == "delete":
- if cls._id_attr is not None:
- id_attr = cls._id_attr.replace("_", "-")
- sub_parser_action.add_argument("--%s" % id_attr, required=True)
-
- if action_name == "get":
- if not issubclass(cls, gitlab.mixins.GetWithoutIdMixin):
- if cls._id_attr is not None:
- id_attr = cls._id_attr.replace("_", "-")
- sub_parser_action.add_argument("--%s" % id_attr, required=True)
-
- for x in mgr_cls._optional_get_attrs:
- sub_parser_action.add_argument(
- "--%s" % x.replace("_", "-"), required=False
- )
-
- if action_name == "create":
- for x in mgr_cls._create_attrs.required:
- sub_parser_action.add_argument(
- "--%s" % x.replace("_", "-"), required=True
- )
- for x in mgr_cls._create_attrs.optional:
- sub_parser_action.add_argument(
- "--%s" % x.replace("_", "-"), required=False
- )
-
- if action_name == "update":
- if cls._id_attr is not None:
- id_attr = cls._id_attr.replace("_", "-")
- sub_parser_action.add_argument("--%s" % id_attr, required=True)
-
- for x in mgr_cls._update_attrs.required:
- if x != cls._id_attr:
- sub_parser_action.add_argument(
- "--%s" % x.replace("_", "-"), required=True
- )
-
- for x in mgr_cls._update_attrs.optional:
- if x != cls._id_attr:
- sub_parser_action.add_argument(
- "--%s" % x.replace("_", "-"), required=False
- )
-
- if cls.__name__ in cli.custom_actions:
- name = cls.__name__
- for action_name in cli.custom_actions[name]:
- sub_parser_action = sub_parser.add_parser(action_name)
- # Get the attributes for URL/path construction
- if mgr_cls._from_parent_attrs:
- for x in mgr_cls._from_parent_attrs:
- sub_parser_action.add_argument(
- "--%s" % x.replace("_", "-"), required=True
- )
- sub_parser_action.add_argument("--sudo", required=False)
-
- # We need to get the object somehow
- if not issubclass(cls, gitlab.mixins.GetWithoutIdMixin):
- if cls._id_attr is not None:
- id_attr = cls._id_attr.replace("_", "-")
- sub_parser_action.add_argument("--%s" % id_attr, required=True)
-
- required, optional, dummy = cli.custom_actions[name][action_name]
- [
- sub_parser_action.add_argument(
- "--%s" % x.replace("_", "-"), required=True
- )
- for x in required
- if x != cls._id_attr
- ]
- [
- sub_parser_action.add_argument(
- "--%s" % x.replace("_", "-"), required=False
- )
- for x in optional
- if x != cls._id_attr
- ]
-
- if mgr_cls.__name__ in cli.custom_actions:
- name = mgr_cls.__name__
- for action_name in cli.custom_actions[name]:
- sub_parser_action = sub_parser.add_parser(action_name)
- if mgr_cls._from_parent_attrs:
- for x in mgr_cls._from_parent_attrs:
- sub_parser_action.add_argument(
- "--%s" % x.replace("_", "-"), required=True
- )
- sub_parser_action.add_argument("--sudo", required=False)
-
- required, optional, dummy = cli.custom_actions[name][action_name]
- [
- sub_parser_action.add_argument(
- "--%s" % x.replace("_", "-"), required=True
- )
- for x in required
- if x != cls._id_attr
- ]
- [
- sub_parser_action.add_argument(
- "--%s" % x.replace("_", "-"), required=False
- )
- for x in optional
- if x != cls._id_attr
- ]
-
-
-def extend_parser(parser: argparse.ArgumentParser) -> argparse.ArgumentParser:
- subparsers = parser.add_subparsers(
- title="object", dest="what", help="Object to manipulate."
- )
- subparsers.required = True
-
- # populate argparse for all Gitlab Object
- classes = []
- for cls in gitlab.v4.objects.__dict__.values():
- if not isinstance(cls, type):
- continue
- if issubclass(cls, gitlab.base.RESTManager):
- if cls._obj_cls is not None:
- classes.append(cls._obj_cls)
- classes.sort(key=operator.attrgetter("__name__"))
-
- for cls in classes:
- arg_name = cli.cls_to_what(cls)
- object_group = subparsers.add_parser(arg_name)
-
- object_subparsers = object_group.add_subparsers(
- title="action", dest="whaction", help="Action to execute."
- )
- _populate_sub_parser_by_class(cls, object_subparsers)
- object_subparsers.required = True
-
- return parser
-
-
-def get_dict(
- obj: Union[str, gitlab.base.RESTObject], fields: List[str]
-) -> Union[str, Dict[str, Any]]:
- if isinstance(obj, str):
- return obj
-
- if fields:
- return {k: v for k, v in obj.attributes.items() if k in fields}
- return obj.attributes
-
-
-class JSONPrinter(object):
- def display(self, d: Union[str, Dict[str, Any]], **kwargs: Any) -> None:
- import json # noqa
-
- print(json.dumps(d))
-
- def display_list(
- self,
- data: List[Union[str, gitlab.base.RESTObject]],
- fields: List[str],
- **kwargs: Any
- ) -> None:
- import json # noqa
-
- print(json.dumps([get_dict(obj, fields) for obj in data]))
-
-
-class YAMLPrinter(object):
- def display(self, d: Union[str, Dict[str, Any]], **kwargs: Any) -> None:
- try:
- import yaml # noqa
-
- print(yaml.safe_dump(d, default_flow_style=False))
- except ImportError:
- exit(
- "PyYaml is not installed.\n"
- "Install it with `pip install PyYaml` "
- "to use the yaml output feature"
- )
-
- def display_list(
- self,
- data: List[Union[str, gitlab.base.RESTObject]],
- fields: List[str],
- **kwargs: Any
- ) -> None:
- try:
- import yaml # noqa
-
- print(
- yaml.safe_dump(
- [get_dict(obj, fields) for obj in data], default_flow_style=False
- )
- )
- except ImportError:
- exit(
- "PyYaml is not installed.\n"
- "Install it with `pip install PyYaml` "
- "to use the yaml output feature"
- )
-
-
-class LegacyPrinter(object):
- def display(self, d: Union[str, Dict[str, Any]], **kwargs: Any) -> None:
- verbose = kwargs.get("verbose", False)
- padding = kwargs.get("padding", 0)
- obj: Optional[Union[Dict[str, Any], gitlab.base.RESTObject]] = kwargs.get("obj")
- if TYPE_CHECKING:
- assert obj is not None
-
- def display_dict(d: Dict[str, Any], padding: int) -> None:
- for k in sorted(d.keys()):
- v = d[k]
- if isinstance(v, dict):
- print("%s%s:" % (" " * padding, k.replace("_", "-")))
- new_padding = padding + 2
- self.display(v, verbose=True, padding=new_padding, obj=v)
- continue
- print("%s%s: %s" % (" " * padding, k.replace("_", "-"), v))
-
- if verbose:
- if isinstance(obj, dict):
- display_dict(obj, padding)
- return
-
- # not a dict, we assume it's a RESTObject
- if obj._id_attr:
- id = getattr(obj, obj._id_attr, None)
- print("%s: %s" % (obj._id_attr, id))
- attrs = obj.attributes
- if obj._id_attr:
- attrs.pop(obj._id_attr)
- display_dict(attrs, padding)
-
- else:
- if TYPE_CHECKING:
- assert isinstance(obj, gitlab.base.RESTObject)
- if obj._id_attr:
- id = getattr(obj, obj._id_attr)
- print("%s: %s" % (obj._id_attr.replace("_", "-"), id))
- if obj._short_print_attr:
- value = getattr(obj, obj._short_print_attr) or "None"
- value = value.replace("\r", "").replace("\n", " ")
- # If the attribute is a note (ProjectCommitComment) then we do
- # some modifications to fit everything on one line
- line = "%s: %s" % (obj._short_print_attr, value)
- # ellipsize long lines (comments)
- if len(line) > 79:
- line = line[:76] + "..."
- print(line)
-
- def display_list(
- self,
- data: List[Union[str, gitlab.base.RESTObject]],
- fields: List[str],
- **kwargs: Any
- ) -> None:
- verbose = kwargs.get("verbose", False)
- for obj in data:
- if isinstance(obj, gitlab.base.RESTObject):
- self.display(get_dict(obj, fields), verbose=verbose, obj=obj)
- else:
- print(obj)
- print("")
-
-
-PRINTERS: Dict[
- str, Union[Type[JSONPrinter], Type[LegacyPrinter], Type[YAMLPrinter]]
-] = {
- "json": JSONPrinter,
- "legacy": LegacyPrinter,
- "yaml": YAMLPrinter,
-}
-
-
-def run(
- gl: gitlab.Gitlab,
- what: str,
- action: str,
- args: Dict[str, Any],
- verbose: bool,
- output: str,
- fields: List[str],
-) -> None:
- g_cli = GitlabCLI(gl=gl, what=what, action=action, args=args)
- data = g_cli()
-
- printer: Union[JSONPrinter, LegacyPrinter, YAMLPrinter] = PRINTERS[output]()
-
- if isinstance(data, dict):
- printer.display(data, verbose=True, obj=data)
- elif isinstance(data, list):
- printer.display_list(data, fields, verbose=verbose)
- elif isinstance(data, gitlab.base.RESTObject):
- printer.display(get_dict(data, fields), verbose=verbose, obj=data)
- elif isinstance(data, str):
- print(data)
- elif isinstance(data, bytes):
- sys.stdout.buffer.write(data)
- elif hasattr(data, "decode"):
- print(data.decode())
diff --git a/gitlab/v4/objects/__init__.py b/gitlab/v4/objects/__init__.py
deleted file mode 100644
index c2ff4fb..0000000
--- a/gitlab/v4/objects/__init__.py
+++ /dev/null
@@ -1,77 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2013-2017 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-from .access_requests import *
-from .appearance import *
-from .applications import *
-from .audit_events import *
-from .award_emojis import *
-from .badges import *
-from .boards import *
-from .branches import *
-from .broadcast_messages import *
-from .clusters import *
-from .commits import *
-from .container_registry import *
-from .custom_attributes import *
-from .deploy_keys import *
-from .deploy_tokens import *
-from .deployments import *
-from .discussions import *
-from .environments import *
-from .epics import *
-from .events import *
-from .export_import import *
-from .features import *
-from .files import *
-from .geo_nodes import *
-from .groups import *
-from .hooks import *
-from .issues import *
-from .jobs import *
-from .keys import *
-from .labels import *
-from .ldap import *
-from .members import *
-from .merge_request_approvals import *
-from .merge_requests import *
-from .milestones import *
-from .namespaces import *
-from .notes import *
-from .notification_settings import *
-from .packages import *
-from .pages import *
-from .personal_access_tokens import *
-from .pipelines import *
-from .projects import *
-from .push_rules import *
-from .releases import *
-from .runners import *
-from .services import *
-from .settings import *
-from .sidekiq import *
-from .snippets import *
-from .statistics import *
-from .tags import *
-from .templates import *
-from .todos import *
-from .triggers import *
-from .users import *
-from .variables import *
-from .wikis import *
-
-__all__ = [name for name in dir() if not name.startswith("_")]
diff --git a/gitlab/v4/objects/access_requests.py b/gitlab/v4/objects/access_requests.py
deleted file mode 100644
index 4e3328a..0000000
--- a/gitlab/v4/objects/access_requests.py
+++ /dev/null
@@ -1,35 +0,0 @@
-from gitlab.base import RESTManager, RESTObject
-from gitlab.mixins import (
- AccessRequestMixin,
- CreateMixin,
- DeleteMixin,
- ListMixin,
- ObjectDeleteMixin,
-)
-
-__all__ = [
- "GroupAccessRequest",
- "GroupAccessRequestManager",
- "ProjectAccessRequest",
- "ProjectAccessRequestManager",
-]
-
-
-class GroupAccessRequest(AccessRequestMixin, ObjectDeleteMixin, RESTObject):
- pass
-
-
-class GroupAccessRequestManager(ListMixin, CreateMixin, DeleteMixin, RESTManager):
- _path = "/groups/%(group_id)s/access_requests"
- _obj_cls = GroupAccessRequest
- _from_parent_attrs = {"group_id": "id"}
-
-
-class ProjectAccessRequest(AccessRequestMixin, ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectAccessRequestManager(ListMixin, CreateMixin, DeleteMixin, RESTManager):
- _path = "/projects/%(project_id)s/access_requests"
- _obj_cls = ProjectAccessRequest
- _from_parent_attrs = {"project_id": "id"}
diff --git a/gitlab/v4/objects/appearance.py b/gitlab/v4/objects/appearance.py
deleted file mode 100644
index a34398e..0000000
--- a/gitlab/v4/objects/appearance.py
+++ /dev/null
@@ -1,52 +0,0 @@
-from gitlab import exceptions as exc
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import GetWithoutIdMixin, SaveMixin, UpdateMixin
-
-__all__ = [
- "ApplicationAppearance",
- "ApplicationAppearanceManager",
-]
-
-
-class ApplicationAppearance(SaveMixin, RESTObject):
- _id_attr = None
-
-
-class ApplicationAppearanceManager(GetWithoutIdMixin, UpdateMixin, RESTManager):
- _path = "/application/appearance"
- _obj_cls = ApplicationAppearance
- _update_attrs = RequiredOptional(
- optional=(
- "title",
- "description",
- "logo",
- "header_logo",
- "favicon",
- "new_project_guidelines",
- "header_message",
- "footer_message",
- "message_background_color",
- "message_font_color",
- "email_header_and_footer_enabled",
- ),
- )
-
- @exc.on_http_error(exc.GitlabUpdateError)
- def update(self, id=None, new_data=None, **kwargs):
- """Update an object on the server.
-
- Args:
- id: ID of the object to update (can be None if not required)
- new_data: the update data for the object
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- dict: The new object data (*not* a RESTObject)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabUpdateError: If the server cannot perform the request
- """
- new_data = new_data or {}
- data = new_data.copy()
- super(ApplicationAppearanceManager, self).update(id, data, **kwargs)
diff --git a/gitlab/v4/objects/applications.py b/gitlab/v4/objects/applications.py
deleted file mode 100644
index c91dee1..0000000
--- a/gitlab/v4/objects/applications.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CreateMixin, DeleteMixin, ListMixin, ObjectDeleteMixin
-
-__all__ = [
- "Application",
- "ApplicationManager",
-]
-
-
-class Application(ObjectDeleteMixin, RESTObject):
- _url = "/applications"
- _short_print_attr = "name"
-
-
-class ApplicationManager(ListMixin, CreateMixin, DeleteMixin, RESTManager):
- _path = "/applications"
- _obj_cls = Application
- _create_attrs = RequiredOptional(
- required=("name", "redirect_uri", "scopes"), optional=("confidential",)
- )
diff --git a/gitlab/v4/objects/audit_events.py b/gitlab/v4/objects/audit_events.py
deleted file mode 100644
index 20ea116..0000000
--- a/gitlab/v4/objects/audit_events.py
+++ /dev/null
@@ -1,57 +0,0 @@
-"""
-GitLab API:
-https://docs.gitlab.com/ee/api/audit_events.html
-"""
-from gitlab.base import RESTManager, RESTObject
-from gitlab.mixins import RetrieveMixin
-
-__all__ = [
- "AuditEvent",
- "AuditEventManager",
- "GroupAuditEvent",
- "GroupAuditEventManager",
- "ProjectAuditEvent",
- "ProjectAuditEventManager",
- "ProjectAudit",
- "ProjectAuditManager",
-]
-
-
-class AuditEvent(RESTObject):
- _id_attr = "id"
-
-
-class AuditEventManager(RetrieveMixin, RESTManager):
- _path = "/audit_events"
- _obj_cls = AuditEvent
- _list_filters = ("created_after", "created_before", "entity_type", "entity_id")
-
-
-class GroupAuditEvent(RESTObject):
- _id_attr = "id"
-
-
-class GroupAuditEventManager(RetrieveMixin, RESTManager):
- _path = "/groups/%(group_id)s/audit_events"
- _obj_cls = GroupAuditEvent
- _from_parent_attrs = {"group_id": "id"}
- _list_filters = ("created_after", "created_before")
-
-
-class ProjectAuditEvent(RESTObject):
- _id_attr = "id"
-
-
-class ProjectAuditEventManager(RetrieveMixin, RESTManager):
- _path = "/projects/%(project_id)s/audit_events"
- _obj_cls = ProjectAuditEvent
- _from_parent_attrs = {"project_id": "id"}
- _list_filters = ("created_after", "created_before")
-
-
-class ProjectAudit(ProjectAuditEvent):
- pass
-
-
-class ProjectAuditManager(ProjectAuditEventManager):
- pass
diff --git a/gitlab/v4/objects/award_emojis.py b/gitlab/v4/objects/award_emojis.py
deleted file mode 100644
index 1a7aecd..0000000
--- a/gitlab/v4/objects/award_emojis.py
+++ /dev/null
@@ -1,103 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import NoUpdateMixin, ObjectDeleteMixin
-
-__all__ = [
- "ProjectIssueAwardEmoji",
- "ProjectIssueAwardEmojiManager",
- "ProjectIssueNoteAwardEmoji",
- "ProjectIssueNoteAwardEmojiManager",
- "ProjectMergeRequestAwardEmoji",
- "ProjectMergeRequestAwardEmojiManager",
- "ProjectMergeRequestNoteAwardEmoji",
- "ProjectMergeRequestNoteAwardEmojiManager",
- "ProjectSnippetAwardEmoji",
- "ProjectSnippetAwardEmojiManager",
- "ProjectSnippetNoteAwardEmoji",
- "ProjectSnippetNoteAwardEmojiManager",
-]
-
-
-class ProjectIssueAwardEmoji(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectIssueAwardEmojiManager(NoUpdateMixin, RESTManager):
- _path = "/projects/%(project_id)s/issues/%(issue_iid)s/award_emoji"
- _obj_cls = ProjectIssueAwardEmoji
- _from_parent_attrs = {"project_id": "project_id", "issue_iid": "iid"}
- _create_attrs = RequiredOptional(required=("name",))
-
-
-class ProjectIssueNoteAwardEmoji(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectIssueNoteAwardEmojiManager(NoUpdateMixin, RESTManager):
- _path = (
- "/projects/%(project_id)s/issues/%(issue_iid)s" "/notes/%(note_id)s/award_emoji"
- )
- _obj_cls = ProjectIssueNoteAwardEmoji
- _from_parent_attrs = {
- "project_id": "project_id",
- "issue_iid": "issue_iid",
- "note_id": "id",
- }
- _create_attrs = RequiredOptional(required=("name",))
-
-
-class ProjectMergeRequestAwardEmoji(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectMergeRequestAwardEmojiManager(NoUpdateMixin, RESTManager):
- _path = "/projects/%(project_id)s/merge_requests/%(mr_iid)s/award_emoji"
- _obj_cls = ProjectMergeRequestAwardEmoji
- _from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}
- _create_attrs = RequiredOptional(required=("name",))
-
-
-class ProjectMergeRequestNoteAwardEmoji(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectMergeRequestNoteAwardEmojiManager(NoUpdateMixin, RESTManager):
- _path = (
- "/projects/%(project_id)s/merge_requests/%(mr_iid)s"
- "/notes/%(note_id)s/award_emoji"
- )
- _obj_cls = ProjectMergeRequestNoteAwardEmoji
- _from_parent_attrs = {
- "project_id": "project_id",
- "mr_iid": "mr_iid",
- "note_id": "id",
- }
- _create_attrs = RequiredOptional(required=("name",))
-
-
-class ProjectSnippetAwardEmoji(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectSnippetAwardEmojiManager(NoUpdateMixin, RESTManager):
- _path = "/projects/%(project_id)s/snippets/%(snippet_id)s/award_emoji"
- _obj_cls = ProjectSnippetAwardEmoji
- _from_parent_attrs = {"project_id": "project_id", "snippet_id": "id"}
- _create_attrs = RequiredOptional(required=("name",))
-
-
-class ProjectSnippetNoteAwardEmoji(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectSnippetNoteAwardEmojiManager(NoUpdateMixin, RESTManager):
- _path = (
- "/projects/%(project_id)s/snippets/%(snippet_id)s"
- "/notes/%(note_id)s/award_emoji"
- )
- _obj_cls = ProjectSnippetNoteAwardEmoji
- _from_parent_attrs = {
- "project_id": "project_id",
- "snippet_id": "snippet_id",
- "note_id": "id",
- }
- _create_attrs = RequiredOptional(required=("name",))
diff --git a/gitlab/v4/objects/badges.py b/gitlab/v4/objects/badges.py
deleted file mode 100644
index 198f6ea..0000000
--- a/gitlab/v4/objects/badges.py
+++ /dev/null
@@ -1,33 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import BadgeRenderMixin, CRUDMixin, ObjectDeleteMixin, SaveMixin
-
-__all__ = [
- "GroupBadge",
- "GroupBadgeManager",
- "ProjectBadge",
- "ProjectBadgeManager",
-]
-
-
-class GroupBadge(SaveMixin, ObjectDeleteMixin, RESTObject):
- pass
-
-
-class GroupBadgeManager(BadgeRenderMixin, CRUDMixin, RESTManager):
- _path = "/groups/%(group_id)s/badges"
- _obj_cls = GroupBadge
- _from_parent_attrs = {"group_id": "id"}
- _create_attrs = RequiredOptional(required=("link_url", "image_url"))
- _update_attrs = RequiredOptional(optional=("link_url", "image_url"))
-
-
-class ProjectBadge(SaveMixin, ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectBadgeManager(BadgeRenderMixin, CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/badges"
- _obj_cls = ProjectBadge
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(required=("link_url", "image_url"))
- _update_attrs = RequiredOptional(optional=("link_url", "image_url"))
diff --git a/gitlab/v4/objects/boards.py b/gitlab/v4/objects/boards.py
deleted file mode 100644
index 8b2959d..0000000
--- a/gitlab/v4/objects/boards.py
+++ /dev/null
@@ -1,59 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CRUDMixin, ObjectDeleteMixin, SaveMixin
-
-__all__ = [
- "GroupBoardList",
- "GroupBoardListManager",
- "GroupBoard",
- "GroupBoardManager",
- "ProjectBoardList",
- "ProjectBoardListManager",
- "ProjectBoard",
- "ProjectBoardManager",
-]
-
-
-class GroupBoardList(SaveMixin, ObjectDeleteMixin, RESTObject):
- pass
-
-
-class GroupBoardListManager(CRUDMixin, RESTManager):
- _path = "/groups/%(group_id)s/boards/%(board_id)s/lists"
- _obj_cls = GroupBoardList
- _from_parent_attrs = {"group_id": "group_id", "board_id": "id"}
- _create_attrs = RequiredOptional(required=("label_id",))
- _update_attrs = RequiredOptional(required=("position",))
-
-
-class GroupBoard(SaveMixin, ObjectDeleteMixin, RESTObject):
- lists: GroupBoardListManager
-
-
-class GroupBoardManager(CRUDMixin, RESTManager):
- _path = "/groups/%(group_id)s/boards"
- _obj_cls = GroupBoard
- _from_parent_attrs = {"group_id": "id"}
- _create_attrs = RequiredOptional(required=("name",))
-
-
-class ProjectBoardList(SaveMixin, ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectBoardListManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/boards/%(board_id)s/lists"
- _obj_cls = ProjectBoardList
- _from_parent_attrs = {"project_id": "project_id", "board_id": "id"}
- _create_attrs = RequiredOptional(required=("label_id",))
- _update_attrs = RequiredOptional(required=("position",))
-
-
-class ProjectBoard(SaveMixin, ObjectDeleteMixin, RESTObject):
- lists: ProjectBoardListManager
-
-
-class ProjectBoardManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/boards"
- _obj_cls = ProjectBoard
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(required=("name",))
diff --git a/gitlab/v4/objects/branches.py b/gitlab/v4/objects/branches.py
deleted file mode 100644
index 5bd8442..0000000
--- a/gitlab/v4/objects/branches.py
+++ /dev/null
@@ -1,42 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import NoUpdateMixin, ObjectDeleteMixin
-
-__all__ = [
- "ProjectBranch",
- "ProjectBranchManager",
- "ProjectProtectedBranch",
- "ProjectProtectedBranchManager",
-]
-
-
-class ProjectBranch(ObjectDeleteMixin, RESTObject):
- _id_attr = "name"
-
-
-class ProjectBranchManager(NoUpdateMixin, RESTManager):
- _path = "/projects/%(project_id)s/repository/branches"
- _obj_cls = ProjectBranch
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(required=("branch", "ref"))
-
-
-class ProjectProtectedBranch(ObjectDeleteMixin, RESTObject):
- _id_attr = "name"
-
-
-class ProjectProtectedBranchManager(NoUpdateMixin, RESTManager):
- _path = "/projects/%(project_id)s/protected_branches"
- _obj_cls = ProjectProtectedBranch
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("name",),
- optional=(
- "push_access_level",
- "merge_access_level",
- "unprotect_access_level",
- "allowed_to_push",
- "allowed_to_merge",
- "allowed_to_unprotect",
- "code_owner_approval_required",
- ),
- )
diff --git a/gitlab/v4/objects/broadcast_messages.py b/gitlab/v4/objects/broadcast_messages.py
deleted file mode 100644
index 7784997..0000000
--- a/gitlab/v4/objects/broadcast_messages.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CRUDMixin, ObjectDeleteMixin, SaveMixin
-
-__all__ = [
- "BroadcastMessage",
- "BroadcastMessageManager",
-]
-
-
-class BroadcastMessage(SaveMixin, ObjectDeleteMixin, RESTObject):
- pass
-
-
-class BroadcastMessageManager(CRUDMixin, RESTManager):
- _path = "/broadcast_messages"
- _obj_cls = BroadcastMessage
-
- _create_attrs = RequiredOptional(
- required=("message",), optional=("starts_at", "ends_at", "color", "font")
- )
- _update_attrs = RequiredOptional(
- optional=("message", "starts_at", "ends_at", "color", "font")
- )
diff --git a/gitlab/v4/objects/clusters.py b/gitlab/v4/objects/clusters.py
deleted file mode 100644
index 10ff202..0000000
--- a/gitlab/v4/objects/clusters.py
+++ /dev/null
@@ -1,98 +0,0 @@
-from gitlab import exceptions as exc
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CreateMixin, CRUDMixin, ObjectDeleteMixin, SaveMixin
-
-__all__ = [
- "GroupCluster",
- "GroupClusterManager",
- "ProjectCluster",
- "ProjectClusterManager",
-]
-
-
-class GroupCluster(SaveMixin, ObjectDeleteMixin, RESTObject):
- pass
-
-
-class GroupClusterManager(CRUDMixin, RESTManager):
- _path = "/groups/%(group_id)s/clusters"
- _obj_cls = GroupCluster
- _from_parent_attrs = {"group_id": "id"}
- _create_attrs = RequiredOptional(
- required=("name", "platform_kubernetes_attributes"),
- optional=("domain", "enabled", "managed", "environment_scope"),
- )
- _update_attrs = RequiredOptional(
- optional=(
- "name",
- "domain",
- "management_project_id",
- "platform_kubernetes_attributes",
- "environment_scope",
- ),
- )
-
- @exc.on_http_error(exc.GitlabStopError)
- def create(self, data, **kwargs):
- """Create a new object.
-
- Args:
- data (dict): Parameters to send to the server to create the
- resource
- **kwargs: Extra options to send to the server (e.g. sudo or
- 'ref_name', 'stage', 'name', 'all')
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server cannot perform the request
-
- Returns:
- RESTObject: A new instance of the manage object class build with
- the data sent by the server
- """
- path = "%s/user" % (self.path)
- return CreateMixin.create(self, data, path=path, **kwargs)
-
-
-class ProjectCluster(SaveMixin, ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectClusterManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/clusters"
- _obj_cls = ProjectCluster
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("name", "platform_kubernetes_attributes"),
- optional=("domain", "enabled", "managed", "environment_scope"),
- )
- _update_attrs = RequiredOptional(
- optional=(
- "name",
- "domain",
- "management_project_id",
- "platform_kubernetes_attributes",
- "environment_scope",
- ),
- )
-
- @exc.on_http_error(exc.GitlabStopError)
- def create(self, data, **kwargs):
- """Create a new object.
-
- Args:
- data (dict): Parameters to send to the server to create the
- resource
- **kwargs: Extra options to send to the server (e.g. sudo or
- 'ref_name', 'stage', 'name', 'all')
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server cannot perform the request
-
- Returns:
- RESTObject: A new instance of the manage object class build with
- the data sent by the server
- """
- path = "%s/user" % (self.path)
- return CreateMixin.create(self, data, path=path, **kwargs)
diff --git a/gitlab/v4/objects/commits.py b/gitlab/v4/objects/commits.py
deleted file mode 100644
index 05b55b0..0000000
--- a/gitlab/v4/objects/commits.py
+++ /dev/null
@@ -1,200 +0,0 @@
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CreateMixin, ListMixin, RefreshMixin, RetrieveMixin
-
-from .discussions import ProjectCommitDiscussionManager # noqa: F401
-
-__all__ = [
- "ProjectCommit",
- "ProjectCommitManager",
- "ProjectCommitComment",
- "ProjectCommitCommentManager",
- "ProjectCommitStatus",
- "ProjectCommitStatusManager",
-]
-
-
-class ProjectCommit(RESTObject):
- _short_print_attr = "title"
-
- comments: "ProjectCommitCommentManager"
- discussions: ProjectCommitDiscussionManager
- statuses: "ProjectCommitStatusManager"
-
- @cli.register_custom_action("ProjectCommit")
- @exc.on_http_error(exc.GitlabGetError)
- def diff(self, **kwargs):
- """Generate the commit diff.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the diff could not be retrieved
-
- Returns:
- list: The changes done in this commit
- """
- path = "%s/%s/diff" % (self.manager.path, self.get_id())
- return self.manager.gitlab.http_get(path, **kwargs)
-
- @cli.register_custom_action("ProjectCommit", ("branch",))
- @exc.on_http_error(exc.GitlabCherryPickError)
- def cherry_pick(self, branch, **kwargs):
- """Cherry-pick a commit into a branch.
-
- Args:
- branch (str): Name of target branch
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCherryPickError: If the cherry-pick could not be performed
- """
- path = "%s/%s/cherry_pick" % (self.manager.path, self.get_id())
- post_data = {"branch": branch}
- self.manager.gitlab.http_post(path, post_data=post_data, **kwargs)
-
- @cli.register_custom_action("ProjectCommit", optional=("type",))
- @exc.on_http_error(exc.GitlabGetError)
- def refs(self, type="all", **kwargs):
- """List the references the commit is pushed to.
-
- Args:
- type (str): The scope of references ('branch', 'tag' or 'all')
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the references could not be retrieved
-
- Returns:
- list: The references the commit is pushed to.
- """
- path = "%s/%s/refs" % (self.manager.path, self.get_id())
- data = {"type": type}
- return self.manager.gitlab.http_get(path, query_data=data, **kwargs)
-
- @cli.register_custom_action("ProjectCommit")
- @exc.on_http_error(exc.GitlabGetError)
- def merge_requests(self, **kwargs):
- """List the merge requests related to the commit.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the references could not be retrieved
-
- Returns:
- list: The merge requests related to the commit.
- """
- path = "%s/%s/merge_requests" % (self.manager.path, self.get_id())
- return self.manager.gitlab.http_get(path, **kwargs)
-
- @cli.register_custom_action("ProjectCommit", ("branch",))
- @exc.on_http_error(exc.GitlabRevertError)
- def revert(self, branch, **kwargs):
- """Revert a commit on a given branch.
-
- Args:
- branch (str): Name of target branch
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabRevertError: If the revert could not be performed
-
- Returns:
- dict: The new commit data (*not* a RESTObject)
- """
- path = "%s/%s/revert" % (self.manager.path, self.get_id())
- post_data = {"branch": branch}
- return self.manager.gitlab.http_post(path, post_data=post_data, **kwargs)
-
- @cli.register_custom_action("ProjectCommit")
- @exc.on_http_error(exc.GitlabGetError)
- def signature(self, **kwargs):
- """Get the signature of the commit.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the signature could not be retrieved
-
- Returns:
- dict: The commit's signature data
- """
- path = "%s/%s/signature" % (self.manager.path, self.get_id())
- return self.manager.gitlab.http_get(path, **kwargs)
-
-
-class ProjectCommitManager(RetrieveMixin, CreateMixin, RESTManager):
- _path = "/projects/%(project_id)s/repository/commits"
- _obj_cls = ProjectCommit
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("branch", "commit_message", "actions"),
- optional=("author_email", "author_name"),
- )
-
-
-class ProjectCommitComment(RESTObject):
- _id_attr = None
- _short_print_attr = "note"
-
-
-class ProjectCommitCommentManager(ListMixin, CreateMixin, RESTManager):
- _path = "/projects/%(project_id)s/repository/commits/%(commit_id)s" "/comments"
- _obj_cls = ProjectCommitComment
- _from_parent_attrs = {"project_id": "project_id", "commit_id": "id"}
- _create_attrs = RequiredOptional(
- required=("note",), optional=("path", "line", "line_type")
- )
-
-
-class ProjectCommitStatus(RefreshMixin, RESTObject):
- pass
-
-
-class ProjectCommitStatusManager(ListMixin, CreateMixin, RESTManager):
- _path = "/projects/%(project_id)s/repository/commits/%(commit_id)s" "/statuses"
- _obj_cls = ProjectCommitStatus
- _from_parent_attrs = {"project_id": "project_id", "commit_id": "id"}
- _create_attrs = RequiredOptional(
- required=("state",),
- optional=("description", "name", "context", "ref", "target_url", "coverage"),
- )
-
- @exc.on_http_error(exc.GitlabCreateError)
- def create(self, data, **kwargs):
- """Create a new object.
-
- Args:
- data (dict): Parameters to send to the server to create the
- resource
- **kwargs: Extra options to send to the server (e.g. sudo or
- 'ref_name', 'stage', 'name', 'all')
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server cannot perform the request
-
- Returns:
- RESTObject: A new instance of the manage object class build with
- the data sent by the server
- """
- # project_id and commit_id are in the data dict when using the CLI, but
- # they are missing when using only the API
- # See #511
- base_path = "/projects/%(project_id)s/statuses/%(commit_id)s"
- if "project_id" in data and "commit_id" in data:
- path = base_path % data
- else:
- path = self._compute_path(base_path)
- return CreateMixin.create(self, data, path=path, **kwargs)
diff --git a/gitlab/v4/objects/container_registry.py b/gitlab/v4/objects/container_registry.py
deleted file mode 100644
index ce03d35..0000000
--- a/gitlab/v4/objects/container_registry.py
+++ /dev/null
@@ -1,58 +0,0 @@
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab.base import RESTManager, RESTObject
-from gitlab.mixins import DeleteMixin, ListMixin, ObjectDeleteMixin, RetrieveMixin
-
-__all__ = [
- "ProjectRegistryRepository",
- "ProjectRegistryRepositoryManager",
- "ProjectRegistryTag",
- "ProjectRegistryTagManager",
-]
-
-
-class ProjectRegistryRepository(ObjectDeleteMixin, RESTObject):
- tags: "ProjectRegistryTagManager"
-
-
-class ProjectRegistryRepositoryManager(DeleteMixin, ListMixin, RESTManager):
- _path = "/projects/%(project_id)s/registry/repositories"
- _obj_cls = ProjectRegistryRepository
- _from_parent_attrs = {"project_id": "id"}
-
-
-class ProjectRegistryTag(ObjectDeleteMixin, RESTObject):
- _id_attr = "name"
-
-
-class ProjectRegistryTagManager(DeleteMixin, RetrieveMixin, RESTManager):
- _obj_cls = ProjectRegistryTag
- _from_parent_attrs = {"project_id": "project_id", "repository_id": "id"}
- _path = "/projects/%(project_id)s/registry/repositories/%(repository_id)s/tags"
-
- @cli.register_custom_action(
- "ProjectRegistryTagManager",
- ("name_regex_delete",),
- optional=("keep_n", "name_regex_keep", "older_than"),
- )
- @exc.on_http_error(exc.GitlabDeleteError)
- def delete_in_bulk(self, name_regex_delete, **kwargs):
- """Delete Tag in bulk
-
- Args:
- name_regex_delete (string): The regex of the name to delete. To delete all
- tags specify .*.
- keep_n (integer): The amount of latest tags of given name to keep.
- name_regex_keep (string): The regex of the name to keep. This value
- overrides any matches from name_regex.
- older_than (string): Tags to delete that are older than the given time,
- written in human readable form 1h, 1d, 1month.
- **kwargs: Extra options to send to the server (e.g. sudo)
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabDeleteError: If the server cannot perform the request
- """
- valid_attrs = ["keep_n", "name_regex_keep", "older_than"]
- data = {"name_regex_delete": name_regex_delete}
- data.update({k: v for k, v in kwargs.items() if k in valid_attrs})
- self.gitlab.http_delete(self.path, query_data=data, **kwargs)
diff --git a/gitlab/v4/objects/custom_attributes.py b/gitlab/v4/objects/custom_attributes.py
deleted file mode 100644
index 48296ca..0000000
--- a/gitlab/v4/objects/custom_attributes.py
+++ /dev/null
@@ -1,41 +0,0 @@
-from gitlab.base import RESTManager, RESTObject
-from gitlab.mixins import DeleteMixin, ObjectDeleteMixin, RetrieveMixin, SetMixin
-
-__all__ = [
- "GroupCustomAttribute",
- "GroupCustomAttributeManager",
- "ProjectCustomAttribute",
- "ProjectCustomAttributeManager",
- "UserCustomAttribute",
- "UserCustomAttributeManager",
-]
-
-
-class GroupCustomAttribute(ObjectDeleteMixin, RESTObject):
- _id_attr = "key"
-
-
-class GroupCustomAttributeManager(RetrieveMixin, SetMixin, DeleteMixin, RESTManager):
- _path = "/groups/%(group_id)s/custom_attributes"
- _obj_cls = GroupCustomAttribute
- _from_parent_attrs = {"group_id": "id"}
-
-
-class ProjectCustomAttribute(ObjectDeleteMixin, RESTObject):
- _id_attr = "key"
-
-
-class ProjectCustomAttributeManager(RetrieveMixin, SetMixin, DeleteMixin, RESTManager):
- _path = "/projects/%(project_id)s/custom_attributes"
- _obj_cls = ProjectCustomAttribute
- _from_parent_attrs = {"project_id": "id"}
-
-
-class UserCustomAttribute(ObjectDeleteMixin, RESTObject):
- _id_attr = "key"
-
-
-class UserCustomAttributeManager(RetrieveMixin, SetMixin, DeleteMixin, RESTManager):
- _path = "/users/%(user_id)s/custom_attributes"
- _obj_cls = UserCustomAttribute
- _from_parent_attrs = {"user_id": "id"}
diff --git a/gitlab/v4/objects/deploy_keys.py b/gitlab/v4/objects/deploy_keys.py
deleted file mode 100644
index cf0507d..0000000
--- a/gitlab/v4/objects/deploy_keys.py
+++ /dev/null
@@ -1,48 +0,0 @@
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CRUDMixin, ListMixin, ObjectDeleteMixin, SaveMixin
-
-__all__ = [
- "DeployKey",
- "DeployKeyManager",
- "ProjectKey",
- "ProjectKeyManager",
-]
-
-
-class DeployKey(RESTObject):
- pass
-
-
-class DeployKeyManager(ListMixin, RESTManager):
- _path = "/deploy_keys"
- _obj_cls = DeployKey
-
-
-class ProjectKey(SaveMixin, ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectKeyManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/deploy_keys"
- _obj_cls = ProjectKey
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(required=("title", "key"), optional=("can_push",))
- _update_attrs = RequiredOptional(optional=("title", "can_push"))
-
- @cli.register_custom_action("ProjectKeyManager", ("key_id",))
- @exc.on_http_error(exc.GitlabProjectDeployKeyError)
- def enable(self, key_id, **kwargs):
- """Enable a deploy key for a project.
-
- Args:
- key_id (int): The ID of the key to enable
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabProjectDeployKeyError: If the key could not be enabled
- """
- path = "%s/%s/enable" % (self.path, key_id)
- self.gitlab.http_post(path, **kwargs)
diff --git a/gitlab/v4/objects/deploy_tokens.py b/gitlab/v4/objects/deploy_tokens.py
deleted file mode 100644
index c6ba0d6..0000000
--- a/gitlab/v4/objects/deploy_tokens.py
+++ /dev/null
@@ -1,63 +0,0 @@
-from gitlab import types
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CreateMixin, DeleteMixin, ListMixin, ObjectDeleteMixin
-
-__all__ = [
- "DeployToken",
- "DeployTokenManager",
- "GroupDeployToken",
- "GroupDeployTokenManager",
- "ProjectDeployToken",
- "ProjectDeployTokenManager",
-]
-
-
-class DeployToken(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class DeployTokenManager(ListMixin, RESTManager):
- _path = "/deploy_tokens"
- _obj_cls = DeployToken
-
-
-class GroupDeployToken(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class GroupDeployTokenManager(ListMixin, CreateMixin, DeleteMixin, RESTManager):
- _path = "/groups/%(group_id)s/deploy_tokens"
- _from_parent_attrs = {"group_id": "id"}
- _obj_cls = GroupDeployToken
- _create_attrs = RequiredOptional(
- required=(
- "name",
- "scopes",
- ),
- optional=(
- "expires_at",
- "username",
- ),
- )
- _types = {"scopes": types.ListAttribute}
-
-
-class ProjectDeployToken(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectDeployTokenManager(ListMixin, CreateMixin, DeleteMixin, RESTManager):
- _path = "/projects/%(project_id)s/deploy_tokens"
- _from_parent_attrs = {"project_id": "id"}
- _obj_cls = ProjectDeployToken
- _create_attrs = RequiredOptional(
- required=(
- "name",
- "scopes",
- ),
- optional=(
- "expires_at",
- "username",
- ),
- )
- _types = {"scopes": types.ListAttribute}
diff --git a/gitlab/v4/objects/deployments.py b/gitlab/v4/objects/deployments.py
deleted file mode 100644
index 11c60d1..0000000
--- a/gitlab/v4/objects/deployments.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CreateMixin, RetrieveMixin, SaveMixin, UpdateMixin
-
-from .merge_requests import ProjectDeploymentMergeRequestManager # noqa: F401
-
-__all__ = [
- "ProjectDeployment",
- "ProjectDeploymentManager",
-]
-
-
-class ProjectDeployment(SaveMixin, RESTObject):
- mergerequests: ProjectDeploymentMergeRequestManager
-
-
-class ProjectDeploymentManager(RetrieveMixin, CreateMixin, UpdateMixin, RESTManager):
- _path = "/projects/%(project_id)s/deployments"
- _obj_cls = ProjectDeployment
- _from_parent_attrs = {"project_id": "id"}
- _list_filters = (
- "order_by",
- "sort",
- "updated_after",
- "updated_before",
- "environment",
- "status",
- )
- _create_attrs = RequiredOptional(
- required=("sha", "ref", "tag", "status", "environment")
- )
diff --git a/gitlab/v4/objects/discussions.py b/gitlab/v4/objects/discussions.py
deleted file mode 100644
index ae7a4d5..0000000
--- a/gitlab/v4/objects/discussions.py
+++ /dev/null
@@ -1,69 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CreateMixin, RetrieveMixin, SaveMixin, UpdateMixin
-
-from .notes import ( # noqa: F401
- ProjectCommitDiscussionNoteManager,
- ProjectIssueDiscussionNoteManager,
- ProjectMergeRequestDiscussionNoteManager,
- ProjectSnippetDiscussionNoteManager,
-)
-
-__all__ = [
- "ProjectCommitDiscussion",
- "ProjectCommitDiscussionManager",
- "ProjectIssueDiscussion",
- "ProjectIssueDiscussionManager",
- "ProjectMergeRequestDiscussion",
- "ProjectMergeRequestDiscussionManager",
- "ProjectSnippetDiscussion",
- "ProjectSnippetDiscussionManager",
-]
-
-
-class ProjectCommitDiscussion(RESTObject):
- notes: ProjectCommitDiscussionNoteManager
-
-
-class ProjectCommitDiscussionManager(RetrieveMixin, CreateMixin, RESTManager):
- _path = "/projects/%(project_id)s/repository/commits/%(commit_id)s/" "discussions"
- _obj_cls = ProjectCommitDiscussion
- _from_parent_attrs = {"project_id": "project_id", "commit_id": "id"}
- _create_attrs = RequiredOptional(required=("body",), optional=("created_at",))
-
-
-class ProjectIssueDiscussion(RESTObject):
- notes: ProjectIssueDiscussionNoteManager
-
-
-class ProjectIssueDiscussionManager(RetrieveMixin, CreateMixin, RESTManager):
- _path = "/projects/%(project_id)s/issues/%(issue_iid)s/discussions"
- _obj_cls = ProjectIssueDiscussion
- _from_parent_attrs = {"project_id": "project_id", "issue_iid": "iid"}
- _create_attrs = RequiredOptional(required=("body",), optional=("created_at",))
-
-
-class ProjectMergeRequestDiscussion(SaveMixin, RESTObject):
- notes: ProjectMergeRequestDiscussionNoteManager
-
-
-class ProjectMergeRequestDiscussionManager(
- RetrieveMixin, CreateMixin, UpdateMixin, RESTManager
-):
- _path = "/projects/%(project_id)s/merge_requests/%(mr_iid)s/discussions"
- _obj_cls = ProjectMergeRequestDiscussion
- _from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}
- _create_attrs = RequiredOptional(
- required=("body",), optional=("created_at", "position")
- )
- _update_attrs = RequiredOptional(required=("resolved",))
-
-
-class ProjectSnippetDiscussion(RESTObject):
- notes: ProjectSnippetDiscussionNoteManager
-
-
-class ProjectSnippetDiscussionManager(RetrieveMixin, CreateMixin, RESTManager):
- _path = "/projects/%(project_id)s/snippets/%(snippet_id)s/discussions"
- _obj_cls = ProjectSnippetDiscussion
- _from_parent_attrs = {"project_id": "project_id", "snippet_id": "id"}
- _create_attrs = RequiredOptional(required=("body",), optional=("created_at",))
diff --git a/gitlab/v4/objects/environments.py b/gitlab/v4/objects/environments.py
deleted file mode 100644
index e318da8..0000000
--- a/gitlab/v4/objects/environments.py
+++ /dev/null
@@ -1,43 +0,0 @@
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import (
- CreateMixin,
- DeleteMixin,
- ObjectDeleteMixin,
- RetrieveMixin,
- SaveMixin,
- UpdateMixin,
-)
-
-__all__ = [
- "ProjectEnvironment",
- "ProjectEnvironmentManager",
-]
-
-
-class ProjectEnvironment(SaveMixin, ObjectDeleteMixin, RESTObject):
- @cli.register_custom_action("ProjectEnvironment")
- @exc.on_http_error(exc.GitlabStopError)
- def stop(self, **kwargs):
- """Stop the environment.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabStopError: If the operation failed
- """
- path = "%s/%s/stop" % (self.manager.path, self.get_id())
- self.manager.gitlab.http_post(path, **kwargs)
-
-
-class ProjectEnvironmentManager(
- RetrieveMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager
-):
- _path = "/projects/%(project_id)s/environments"
- _obj_cls = ProjectEnvironment
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(required=("name",), optional=("external_url",))
- _update_attrs = RequiredOptional(optional=("name", "external_url"))
diff --git a/gitlab/v4/objects/epics.py b/gitlab/v4/objects/epics.py
deleted file mode 100644
index 90dc6ad..0000000
--- a/gitlab/v4/objects/epics.py
+++ /dev/null
@@ -1,104 +0,0 @@
-from gitlab import exceptions as exc
-from gitlab import types
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import (
- CreateMixin,
- CRUDMixin,
- DeleteMixin,
- ListMixin,
- ObjectDeleteMixin,
- SaveMixin,
- UpdateMixin,
-)
-
-from .events import GroupEpicResourceLabelEventManager # noqa: F401
-
-__all__ = [
- "GroupEpic",
- "GroupEpicManager",
- "GroupEpicIssue",
- "GroupEpicIssueManager",
-]
-
-
-class GroupEpic(ObjectDeleteMixin, SaveMixin, RESTObject):
- _id_attr = "iid"
-
- issues: "GroupEpicIssueManager"
- resourcelabelevents: GroupEpicResourceLabelEventManager
-
-
-class GroupEpicManager(CRUDMixin, RESTManager):
- _path = "/groups/%(group_id)s/epics"
- _obj_cls = GroupEpic
- _from_parent_attrs = {"group_id": "id"}
- _list_filters = ("author_id", "labels", "order_by", "sort", "search")
- _create_attrs = RequiredOptional(
- required=("title",),
- optional=("labels", "description", "start_date", "end_date"),
- )
- _update_attrs = RequiredOptional(
- optional=("title", "labels", "description", "start_date", "end_date"),
- )
- _types = {"labels": types.ListAttribute}
-
-
-class GroupEpicIssue(ObjectDeleteMixin, SaveMixin, RESTObject):
- _id_attr = "epic_issue_id"
-
- def save(self, **kwargs):
- """Save the changes made to the object to the server.
-
- The object is updated to match what the server returns.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raise:
- GitlabAuthenticationError: If authentication is not correct
- GitlabUpdateError: If the server cannot perform the request
- """
- updated_data = self._get_updated_data()
- # Nothing to update. Server fails if sent an empty dict.
- if not updated_data:
- return
-
- # call the manager
- obj_id = self.get_id()
- self.manager.update(obj_id, updated_data, **kwargs)
-
-
-class GroupEpicIssueManager(
- ListMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager
-):
- _path = "/groups/%(group_id)s/epics/%(epic_iid)s/issues"
- _obj_cls = GroupEpicIssue
- _from_parent_attrs = {"group_id": "group_id", "epic_iid": "iid"}
- _create_attrs = RequiredOptional(required=("issue_id",))
- _update_attrs = RequiredOptional(optional=("move_before_id", "move_after_id"))
-
- @exc.on_http_error(exc.GitlabCreateError)
- def create(self, data, **kwargs):
- """Create a new object.
-
- Args:
- data (dict): Parameters to send to the server to create the
- resource
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server cannot perform the request
-
- Returns:
- RESTObject: A new instance of the manage object class build with
- the data sent by the server
- """
- CreateMixin._check_missing_create_attrs(self, data)
- path = "%s/%s" % (self.path, data.pop("issue_id"))
- server_data = self.gitlab.http_post(path, **kwargs)
- # The epic_issue_id attribute doesn't exist when creating the resource,
- # but is used everywhere elese. Let's create it to be consistent client
- # side
- server_data["epic_issue_id"] = server_data["id"]
- return self._obj_cls(self, server_data)
diff --git a/gitlab/v4/objects/events.py b/gitlab/v4/objects/events.py
deleted file mode 100644
index 8772e8d..0000000
--- a/gitlab/v4/objects/events.py
+++ /dev/null
@@ -1,130 +0,0 @@
-from gitlab.base import RESTManager, RESTObject
-from gitlab.mixins import ListMixin, RetrieveMixin
-
-__all__ = [
- "Event",
- "EventManager",
- "GroupEpicResourceLabelEvent",
- "GroupEpicResourceLabelEventManager",
- "ProjectEvent",
- "ProjectEventManager",
- "ProjectIssueResourceLabelEvent",
- "ProjectIssueResourceLabelEventManager",
- "ProjectIssueResourceMilestoneEvent",
- "ProjectIssueResourceMilestoneEventManager",
- "ProjectIssueResourceStateEvent",
- "ProjectIssueResourceStateEventManager",
- "ProjectMergeRequestResourceLabelEvent",
- "ProjectMergeRequestResourceLabelEventManager",
- "ProjectMergeRequestResourceMilestoneEvent",
- "ProjectMergeRequestResourceMilestoneEventManager",
- "ProjectMergeRequestResourceStateEvent",
- "ProjectMergeRequestResourceStateEventManager",
- "UserEvent",
- "UserEventManager",
-]
-
-
-class Event(RESTObject):
- _id_attr = None
- _short_print_attr = "target_title"
-
-
-class EventManager(ListMixin, RESTManager):
- _path = "/events"
- _obj_cls = Event
- _list_filters = ("action", "target_type", "before", "after", "sort")
-
-
-class GroupEpicResourceLabelEvent(RESTObject):
- pass
-
-
-class GroupEpicResourceLabelEventManager(RetrieveMixin, RESTManager):
- _path = "/groups/%(group_id)s/epics/%(epic_id)s/resource_label_events"
- _obj_cls = GroupEpicResourceLabelEvent
- _from_parent_attrs = {"group_id": "group_id", "epic_id": "id"}
-
-
-class ProjectEvent(Event):
- pass
-
-
-class ProjectEventManager(EventManager):
- _path = "/projects/%(project_id)s/events"
- _obj_cls = ProjectEvent
- _from_parent_attrs = {"project_id": "id"}
-
-
-class ProjectIssueResourceLabelEvent(RESTObject):
- pass
-
-
-class ProjectIssueResourceLabelEventManager(RetrieveMixin, RESTManager):
- _path = "/projects/%(project_id)s/issues/%(issue_iid)s" "/resource_label_events"
- _obj_cls = ProjectIssueResourceLabelEvent
- _from_parent_attrs = {"project_id": "project_id", "issue_iid": "iid"}
-
-
-class ProjectIssueResourceMilestoneEvent(RESTObject):
- pass
-
-
-class ProjectIssueResourceMilestoneEventManager(RetrieveMixin, RESTManager):
- _path = "/projects/%(project_id)s/issues/%(issue_iid)s/resource_milestone_events"
- _obj_cls = ProjectIssueResourceMilestoneEvent
- _from_parent_attrs = {"project_id": "project_id", "issue_iid": "iid"}
-
-
-class ProjectIssueResourceStateEvent(RESTObject):
- pass
-
-
-class ProjectIssueResourceStateEventManager(RetrieveMixin, RESTManager):
- _path = "/projects/%(project_id)s/issues/%(issue_iid)s/resource_state_events"
- _obj_cls = ProjectIssueResourceStateEvent
- _from_parent_attrs = {"project_id": "project_id", "issue_iid": "iid"}
-
-
-class ProjectMergeRequestResourceLabelEvent(RESTObject):
- pass
-
-
-class ProjectMergeRequestResourceLabelEventManager(RetrieveMixin, RESTManager):
- _path = (
- "/projects/%(project_id)s/merge_requests/%(mr_iid)s" "/resource_label_events"
- )
- _obj_cls = ProjectMergeRequestResourceLabelEvent
- _from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}
-
-
-class ProjectMergeRequestResourceMilestoneEvent(RESTObject):
- pass
-
-
-class ProjectMergeRequestResourceMilestoneEventManager(RetrieveMixin, RESTManager):
- _path = (
- "/projects/%(project_id)s/merge_requests/%(mr_iid)s/resource_milestone_events"
- )
- _obj_cls = ProjectMergeRequestResourceMilestoneEvent
- _from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}
-
-
-class ProjectMergeRequestResourceStateEvent(RESTObject):
- pass
-
-
-class ProjectMergeRequestResourceStateEventManager(RetrieveMixin, RESTManager):
- _path = "/projects/%(project_id)s/merge_requests/%(mr_iid)s/resource_state_events"
- _obj_cls = ProjectMergeRequestResourceStateEvent
- _from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}
-
-
-class UserEvent(Event):
- pass
-
-
-class UserEventManager(EventManager):
- _path = "/users/%(user_id)s/events"
- _obj_cls = UserEvent
- _from_parent_attrs = {"user_id": "id"}
diff --git a/gitlab/v4/objects/export_import.py b/gitlab/v4/objects/export_import.py
deleted file mode 100644
index ec4532a..0000000
--- a/gitlab/v4/objects/export_import.py
+++ /dev/null
@@ -1,54 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CreateMixin, DownloadMixin, GetWithoutIdMixin, RefreshMixin
-
-__all__ = [
- "GroupExport",
- "GroupExportManager",
- "GroupImport",
- "GroupImportManager",
- "ProjectExport",
- "ProjectExportManager",
- "ProjectImport",
- "ProjectImportManager",
-]
-
-
-class GroupExport(DownloadMixin, RESTObject):
- _id_attr = None
-
-
-class GroupExportManager(GetWithoutIdMixin, CreateMixin, RESTManager):
- _path = "/groups/%(group_id)s/export"
- _obj_cls = GroupExport
- _from_parent_attrs = {"group_id": "id"}
-
-
-class GroupImport(RESTObject):
- _id_attr = None
-
-
-class GroupImportManager(GetWithoutIdMixin, RESTManager):
- _path = "/groups/%(group_id)s/import"
- _obj_cls = GroupImport
- _from_parent_attrs = {"group_id": "id"}
-
-
-class ProjectExport(DownloadMixin, RefreshMixin, RESTObject):
- _id_attr = None
-
-
-class ProjectExportManager(GetWithoutIdMixin, CreateMixin, RESTManager):
- _path = "/projects/%(project_id)s/export"
- _obj_cls = ProjectExport
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(optional=("description",))
-
-
-class ProjectImport(RefreshMixin, RESTObject):
- _id_attr = None
-
-
-class ProjectImportManager(GetWithoutIdMixin, RESTManager):
- _path = "/projects/%(project_id)s/import"
- _obj_cls = ProjectImport
- _from_parent_attrs = {"project_id": "id"}
diff --git a/gitlab/v4/objects/features.py b/gitlab/v4/objects/features.py
deleted file mode 100644
index f4117c8..0000000
--- a/gitlab/v4/objects/features.py
+++ /dev/null
@@ -1,59 +0,0 @@
-from gitlab import exceptions as exc
-from gitlab import utils
-from gitlab.base import RESTManager, RESTObject
-from gitlab.mixins import DeleteMixin, ListMixin, ObjectDeleteMixin
-
-__all__ = [
- "Feature",
- "FeatureManager",
-]
-
-
-class Feature(ObjectDeleteMixin, RESTObject):
- _id_attr = "name"
-
-
-class FeatureManager(ListMixin, DeleteMixin, RESTManager):
- _path = "/features/"
- _obj_cls = Feature
-
- @exc.on_http_error(exc.GitlabSetError)
- def set(
- self,
- name,
- value,
- feature_group=None,
- user=None,
- group=None,
- project=None,
- **kwargs
- ):
- """Create or update the object.
-
- Args:
- name (str): The value to set for the object
- value (bool/int): The value to set for the object
- feature_group (str): A feature group name
- user (str): A GitLab username
- group (str): A GitLab group
- project (str): A GitLab project in form group/project
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabSetError: If an error occurred
-
- Returns:
- obj: The created/updated attribute
- """
- path = "%s/%s" % (self.path, name.replace("/", "%2F"))
- data = {
- "value": value,
- "feature_group": feature_group,
- "user": user,
- "group": group,
- "project": project,
- }
- data = utils.remove_none_from_dict(data)
- server_data = self.gitlab.http_post(path, post_data=data, **kwargs)
- return self._obj_cls(self, server_data)
diff --git a/gitlab/v4/objects/files.py b/gitlab/v4/objects/files.py
deleted file mode 100644
index ff45478..0000000
--- a/gitlab/v4/objects/files.py
+++ /dev/null
@@ -1,228 +0,0 @@
-import base64
-
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab import utils
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import (
- CreateMixin,
- DeleteMixin,
- GetMixin,
- ObjectDeleteMixin,
- SaveMixin,
- UpdateMixin,
-)
-
-__all__ = [
- "ProjectFile",
- "ProjectFileManager",
-]
-
-
-class ProjectFile(SaveMixin, ObjectDeleteMixin, RESTObject):
- _id_attr = "file_path"
- _short_print_attr = "file_path"
-
- def decode(self) -> bytes:
- """Returns the decoded content of the file.
-
- Returns:
- (bytes): the decoded content.
- """
- return base64.b64decode(self.content)
-
- def save(self, branch, commit_message, **kwargs):
- """Save the changes made to the file to the server.
-
- The object is updated to match what the server returns.
-
- Args:
- branch (str): Branch in which the file will be updated
- commit_message (str): Message to send with the commit
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabUpdateError: If the server cannot perform the request
- """
- self.branch = branch
- self.commit_message = commit_message
- self.file_path = self.file_path.replace("/", "%2F")
- super(ProjectFile, self).save(**kwargs)
-
- def delete(self, branch, commit_message, **kwargs):
- """Delete the file from the server.
-
- Args:
- branch (str): Branch from which the file will be removed
- commit_message (str): Commit message for the deletion
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabDeleteError: If the server cannot perform the request
- """
- file_path = self.get_id().replace("/", "%2F")
- self.manager.delete(file_path, branch, commit_message, **kwargs)
-
-
-class ProjectFileManager(GetMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager):
- _path = "/projects/%(project_id)s/repository/files"
- _obj_cls = ProjectFile
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("file_path", "branch", "content", "commit_message"),
- optional=("encoding", "author_email", "author_name"),
- )
- _update_attrs = RequiredOptional(
- required=("file_path", "branch", "content", "commit_message"),
- optional=("encoding", "author_email", "author_name"),
- )
-
- @cli.register_custom_action("ProjectFileManager", ("file_path", "ref"))
- def get(self, file_path, ref, **kwargs):
- """Retrieve a single file.
-
- Args:
- file_path (str): Path of the file to retrieve
- ref (str): Name of the branch, tag or commit
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the file could not be retrieved
-
- Returns:
- object: The generated RESTObject
- """
- return GetMixin.get(self, file_path, ref=ref, **kwargs)
-
- @cli.register_custom_action(
- "ProjectFileManager",
- ("file_path", "branch", "content", "commit_message"),
- ("encoding", "author_email", "author_name"),
- )
- @exc.on_http_error(exc.GitlabCreateError)
- def create(self, data, **kwargs):
- """Create a new object.
-
- Args:
- data (dict): parameters to send to the server to create the
- resource
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- RESTObject: a new instance of the managed object class built with
- the data sent by the server
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server cannot perform the request
- """
-
- self._check_missing_create_attrs(data)
- new_data = data.copy()
- file_path = new_data.pop("file_path").replace("/", "%2F")
- path = "%s/%s" % (self.path, file_path)
- server_data = self.gitlab.http_post(path, post_data=new_data, **kwargs)
- return self._obj_cls(self, server_data)
-
- @exc.on_http_error(exc.GitlabUpdateError)
- def update(self, file_path, new_data=None, **kwargs):
- """Update an object on the server.
-
- Args:
- id: ID of the object to update (can be None if not required)
- new_data: the update data for the object
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- dict: The new object data (*not* a RESTObject)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabUpdateError: If the server cannot perform the request
- """
- new_data = new_data or {}
- data = new_data.copy()
- file_path = file_path.replace("/", "%2F")
- data["file_path"] = file_path
- path = "%s/%s" % (self.path, file_path)
- self._check_missing_update_attrs(data)
- return self.gitlab.http_put(path, post_data=data, **kwargs)
-
- @cli.register_custom_action(
- "ProjectFileManager", ("file_path", "branch", "commit_message")
- )
- @exc.on_http_error(exc.GitlabDeleteError)
- def delete(self, file_path, branch, commit_message, **kwargs):
- """Delete a file on the server.
-
- Args:
- file_path (str): Path of the file to remove
- branch (str): Branch from which the file will be removed
- commit_message (str): Commit message for the deletion
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabDeleteError: If the server cannot perform the request
- """
- path = "%s/%s" % (self.path, file_path.replace("/", "%2F"))
- data = {"branch": branch, "commit_message": commit_message}
- self.gitlab.http_delete(path, query_data=data, **kwargs)
-
- @cli.register_custom_action("ProjectFileManager", ("file_path", "ref"))
- @exc.on_http_error(exc.GitlabGetError)
- def raw(
- self, file_path, ref, streamed=False, action=None, chunk_size=1024, **kwargs
- ):
- """Return the content of a file for a commit.
-
- Args:
- ref (str): ID of the commit
- filepath (str): Path of the file to return
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment
- action (callable): Callable responsible of dealing with chunk of
- data
- chunk_size (int): Size of each chunk
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the file could not be retrieved
-
- Returns:
- str: The file content
- """
- file_path = file_path.replace("/", "%2F").replace(".", "%2E")
- path = "%s/%s/raw" % (self.path, file_path)
- query_data = {"ref": ref}
- result = self.gitlab.http_get(
- path, query_data=query_data, streamed=streamed, raw=True, **kwargs
- )
- return utils.response_content(result, streamed, action, chunk_size)
-
- @cli.register_custom_action("ProjectFileManager", ("file_path", "ref"))
- @exc.on_http_error(exc.GitlabListError)
- def blame(self, file_path, ref, **kwargs):
- """Return the content of a file for a commit.
-
- Args:
- file_path (str): Path of the file to retrieve
- ref (str): Name of the branch, tag or commit
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the server failed to perform the request
-
- Returns:
- list(blame): a list of commits/lines matching the file
- """
- file_path = file_path.replace("/", "%2F").replace(".", "%2E")
- path = "%s/%s/blame" % (self.path, file_path)
- query_data = {"ref": ref}
- return self.gitlab.http_list(path, query_data, **kwargs)
diff --git a/gitlab/v4/objects/geo_nodes.py b/gitlab/v4/objects/geo_nodes.py
deleted file mode 100644
index 16fc783..0000000
--- a/gitlab/v4/objects/geo_nodes.py
+++ /dev/null
@@ -1,93 +0,0 @@
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import (
- DeleteMixin,
- ObjectDeleteMixin,
- RetrieveMixin,
- SaveMixin,
- UpdateMixin,
-)
-
-__all__ = [
- "GeoNode",
- "GeoNodeManager",
-]
-
-
-class GeoNode(SaveMixin, ObjectDeleteMixin, RESTObject):
- @cli.register_custom_action("GeoNode")
- @exc.on_http_error(exc.GitlabRepairError)
- def repair(self, **kwargs):
- """Repair the OAuth authentication of the geo node.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabRepairError: If the server failed to perform the request
- """
- path = "/geo_nodes/%s/repair" % self.get_id()
- server_data = self.manager.gitlab.http_post(path, **kwargs)
- self._update_attrs(server_data)
-
- @cli.register_custom_action("GeoNode")
- @exc.on_http_error(exc.GitlabGetError)
- def status(self, **kwargs):
- """Get the status of the geo node.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server failed to perform the request
-
- Returns:
- dict: The status of the geo node
- """
- path = "/geo_nodes/%s/status" % self.get_id()
- return self.manager.gitlab.http_get(path, **kwargs)
-
-
-class GeoNodeManager(RetrieveMixin, UpdateMixin, DeleteMixin, RESTManager):
- _path = "/geo_nodes"
- _obj_cls = GeoNode
- _update_attrs = RequiredOptional(
- optional=("enabled", "url", "files_max_capacity", "repos_max_capacity"),
- )
-
- @cli.register_custom_action("GeoNodeManager")
- @exc.on_http_error(exc.GitlabGetError)
- def status(self, **kwargs):
- """Get the status of all the geo nodes.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server failed to perform the request
-
- Returns:
- list: The status of all the geo nodes
- """
- return self.gitlab.http_list("/geo_nodes/status", **kwargs)
-
- @cli.register_custom_action("GeoNodeManager")
- @exc.on_http_error(exc.GitlabGetError)
- def current_failures(self, **kwargs):
- """Get the list of failures on the current geo node.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server failed to perform the request
-
- Returns:
- list: The list of failures
- """
- return self.gitlab.http_list("/geo_nodes/current/failures", **kwargs)
diff --git a/gitlab/v4/objects/groups.py b/gitlab/v4/objects/groups.py
deleted file mode 100644
index b675a39..0000000
--- a/gitlab/v4/objects/groups.py
+++ /dev/null
@@ -1,334 +0,0 @@
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab import types
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CRUDMixin, ListMixin, ObjectDeleteMixin, SaveMixin
-
-from .access_requests import GroupAccessRequestManager # noqa: F401
-from .audit_events import GroupAuditEventManager # noqa: F401
-from .badges import GroupBadgeManager # noqa: F401
-from .boards import GroupBoardManager # noqa: F401
-from .clusters import GroupClusterManager # noqa: F401
-from .custom_attributes import GroupCustomAttributeManager # noqa: F401
-from .deploy_tokens import GroupDeployTokenManager # noqa: F401
-from .epics import GroupEpicManager # noqa: F401
-from .export_import import GroupExportManager, GroupImportManager # noqa: F401
-from .hooks import GroupHookManager # noqa: F401
-from .issues import GroupIssueManager # noqa: F401
-from .labels import GroupLabelManager # noqa: F401
-from .members import ( # noqa: F401
- GroupBillableMemberManager,
- GroupMemberAllManager,
- GroupMemberManager,
-)
-from .merge_requests import GroupMergeRequestManager # noqa: F401
-from .milestones import GroupMilestoneManager # noqa: F401
-from .notification_settings import GroupNotificationSettingsManager # noqa: F401
-from .packages import GroupPackageManager # noqa: F401
-from .projects import GroupProjectManager # noqa: F401
-from .runners import GroupRunnerManager # noqa: F401
-from .statistics import GroupIssuesStatisticsManager # noqa: F401
-from .variables import GroupVariableManager # noqa: F401
-from .wikis import GroupWikiManager # noqa: F401
-
-__all__ = [
- "Group",
- "GroupManager",
- "GroupDescendantGroup",
- "GroupDescendantGroupManager",
- "GroupSubgroup",
- "GroupSubgroupManager",
-]
-
-
-class Group(SaveMixin, ObjectDeleteMixin, RESTObject):
- _short_print_attr = "name"
-
- accessrequests: GroupAccessRequestManager
- audit_events: GroupAuditEventManager
- badges: GroupBadgeManager
- billable_members: GroupBillableMemberManager
- boards: GroupBoardManager
- clusters: GroupClusterManager
- customattributes: GroupCustomAttributeManager
- deploytokens: GroupDeployTokenManager
- descendant_groups: "GroupDescendantGroupManager"
- epics: GroupEpicManager
- exports: GroupExportManager
- hooks: GroupHookManager
- imports: GroupImportManager
- issues: GroupIssueManager
- issues_statistics: GroupIssuesStatisticsManager
- labels: GroupLabelManager
- members: GroupMemberManager
- members_all: GroupMemberAllManager
- mergerequests: GroupMergeRequestManager
- milestones: GroupMilestoneManager
- notificationsettings: GroupNotificationSettingsManager
- packages: GroupPackageManager
- projects: GroupProjectManager
- runners: GroupRunnerManager
- subgroups: "GroupSubgroupManager"
- variables: GroupVariableManager
- wikis: GroupWikiManager
-
- @cli.register_custom_action("Group", ("project_id",))
- @exc.on_http_error(exc.GitlabTransferProjectError)
- def transfer_project(self, project_id, **kwargs):
- """Transfer a project to this group.
-
- Args:
- to_project_id (int): ID of the project to transfer
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabTransferProjectError: If the project could not be transferred
- """
- path = "/groups/%s/projects/%s" % (self.id, project_id)
- self.manager.gitlab.http_post(path, **kwargs)
-
- @cli.register_custom_action("Group", ("scope", "search"))
- @exc.on_http_error(exc.GitlabSearchError)
- def search(self, scope, search, **kwargs):
- """Search the group resources matching the provided string.'
-
- Args:
- scope (str): Scope of the search
- search (str): Search string
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabSearchError: If the server failed to perform the request
-
- Returns:
- GitlabList: A list of dicts describing the resources found.
- """
- data = {"scope": scope, "search": search}
- path = "/groups/%s/search" % self.get_id()
- return self.manager.gitlab.http_list(path, query_data=data, **kwargs)
-
- @cli.register_custom_action("Group", ("cn", "group_access", "provider"))
- @exc.on_http_error(exc.GitlabCreateError)
- def add_ldap_group_link(self, cn, group_access, provider, **kwargs):
- """Add an LDAP group link.
-
- Args:
- cn (str): CN of the LDAP group
- group_access (int): Minimum access level for members of the LDAP
- group
- provider (str): LDAP provider for the LDAP group
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server cannot perform the request
- """
- path = "/groups/%s/ldap_group_links" % self.get_id()
- data = {"cn": cn, "group_access": group_access, "provider": provider}
- self.manager.gitlab.http_post(path, post_data=data, **kwargs)
-
- @cli.register_custom_action("Group", ("cn",), ("provider",))
- @exc.on_http_error(exc.GitlabDeleteError)
- def delete_ldap_group_link(self, cn, provider=None, **kwargs):
- """Delete an LDAP group link.
-
- Args:
- cn (str): CN of the LDAP group
- provider (str): LDAP provider for the LDAP group
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabDeleteError: If the server cannot perform the request
- """
- path = "/groups/%s/ldap_group_links" % self.get_id()
- if provider is not None:
- path += "/%s" % provider
- path += "/%s" % cn
- self.manager.gitlab.http_delete(path)
-
- @cli.register_custom_action("Group")
- @exc.on_http_error(exc.GitlabCreateError)
- def ldap_sync(self, **kwargs):
- """Sync LDAP groups.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server cannot perform the request
- """
- path = "/groups/%s/ldap_sync" % self.get_id()
- self.manager.gitlab.http_post(path, **kwargs)
-
- @cli.register_custom_action("Group", ("group_id", "group_access"), ("expires_at",))
- @exc.on_http_error(exc.GitlabCreateError)
- def share(self, group_id, group_access, expires_at=None, **kwargs):
- """Share the group with a group.
-
- Args:
- group_id (int): ID of the group.
- group_access (int): Access level for the group.
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server failed to perform the request
- """
- path = "/groups/%s/share" % self.get_id()
- data = {
- "group_id": group_id,
- "group_access": group_access,
- "expires_at": expires_at,
- }
- self.manager.gitlab.http_post(path, post_data=data, **kwargs)
-
- @cli.register_custom_action("Group", ("group_id",))
- @exc.on_http_error(exc.GitlabDeleteError)
- def unshare(self, group_id, **kwargs):
- """Delete a shared group link within a group.
-
- Args:
- group_id (int): ID of the group.
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabDeleteError: If the server failed to perform the request
- """
- path = "/groups/%s/share/%s" % (self.get_id(), group_id)
- self.manager.gitlab.http_delete(path, **kwargs)
-
-
-class GroupManager(CRUDMixin, RESTManager):
- _path = "/groups"
- _obj_cls = Group
- _list_filters = (
- "skip_groups",
- "all_available",
- "search",
- "order_by",
- "sort",
- "statistics",
- "owned",
- "with_custom_attributes",
- "min_access_level",
- "top_level_only",
- )
- _create_attrs = RequiredOptional(
- required=("name", "path"),
- optional=(
- "description",
- "membership_lock",
- "visibility",
- "share_with_group_lock",
- "require_two_factor_authentication",
- "two_factor_grace_period",
- "project_creation_level",
- "auto_devops_enabled",
- "subgroup_creation_level",
- "emails_disabled",
- "avatar",
- "mentions_disabled",
- "lfs_enabled",
- "request_access_enabled",
- "parent_id",
- "default_branch_protection",
- "shared_runners_minutes_limit",
- "extra_shared_runners_minutes_limit",
- ),
- )
- _update_attrs = RequiredOptional(
- optional=(
- "name",
- "path",
- "description",
- "membership_lock",
- "share_with_group_lock",
- "visibility",
- "require_two_factor_authentication",
- "two_factor_grace_period",
- "project_creation_level",
- "auto_devops_enabled",
- "subgroup_creation_level",
- "emails_disabled",
- "avatar",
- "mentions_disabled",
- "lfs_enabled",
- "request_access_enabled",
- "default_branch_protection",
- "file_template_project_id",
- "shared_runners_minutes_limit",
- "extra_shared_runners_minutes_limit",
- "prevent_forking_outside_group",
- "shared_runners_setting",
- ),
- )
- _types = {"avatar": types.ImageAttribute, "skip_groups": types.ListAttribute}
-
- @exc.on_http_error(exc.GitlabImportError)
- def import_group(self, file, path, name, parent_id=None, **kwargs):
- """Import a group from an archive file.
-
- Args:
- file: Data or file object containing the group
- path (str): The path for the new group to be imported.
- name (str): The name for the new group.
- parent_id (str): ID of a parent group that the group will
- be imported into.
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabImportError: If the server failed to perform the request
-
- Returns:
- dict: A representation of the import status.
- """
- files = {"file": ("file.tar.gz", file, "application/octet-stream")}
- data = {"path": path, "name": name}
- if parent_id is not None:
- data["parent_id"] = parent_id
-
- return self.gitlab.http_post(
- "/groups/import", post_data=data, files=files, **kwargs
- )
-
-
-class GroupSubgroup(RESTObject):
- pass
-
-
-class GroupSubgroupManager(ListMixin, RESTManager):
- _path = "/groups/%(group_id)s/subgroups"
- _obj_cls = GroupSubgroup
- _from_parent_attrs = {"group_id": "id"}
- _list_filters = (
- "skip_groups",
- "all_available",
- "search",
- "order_by",
- "sort",
- "statistics",
- "owned",
- "with_custom_attributes",
- "min_access_level",
- )
- _types = {"skip_groups": types.ListAttribute}
-
-
-class GroupDescendantGroup(RESTObject):
- pass
-
-
-class GroupDescendantGroupManager(GroupSubgroupManager):
- """
- This manager inherits from GroupSubgroupManager as descendant groups
- share all attributes with subgroups, except the path and object class.
- """
-
- _path = "/groups/%(group_id)s/descendant_groups"
- _obj_cls = GroupDescendantGroup
diff --git a/gitlab/v4/objects/hooks.py b/gitlab/v4/objects/hooks.py
deleted file mode 100644
index 428fd76..0000000
--- a/gitlab/v4/objects/hooks.py
+++ /dev/null
@@ -1,114 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CRUDMixin, NoUpdateMixin, ObjectDeleteMixin, SaveMixin
-
-__all__ = [
- "Hook",
- "HookManager",
- "ProjectHook",
- "ProjectHookManager",
- "GroupHook",
- "GroupHookManager",
-]
-
-
-class Hook(ObjectDeleteMixin, RESTObject):
- _url = "/hooks"
- _short_print_attr = "url"
-
-
-class HookManager(NoUpdateMixin, RESTManager):
- _path = "/hooks"
- _obj_cls = Hook
- _create_attrs = RequiredOptional(required=("url",))
-
-
-class ProjectHook(SaveMixin, ObjectDeleteMixin, RESTObject):
- _short_print_attr = "url"
-
-
-class ProjectHookManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/hooks"
- _obj_cls = ProjectHook
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("url",),
- optional=(
- "push_events",
- "issues_events",
- "confidential_issues_events",
- "merge_requests_events",
- "tag_push_events",
- "note_events",
- "job_events",
- "pipeline_events",
- "wiki_page_events",
- "enable_ssl_verification",
- "token",
- ),
- )
- _update_attrs = RequiredOptional(
- required=("url",),
- optional=(
- "push_events",
- "issues_events",
- "confidential_issues_events",
- "merge_requests_events",
- "tag_push_events",
- "note_events",
- "job_events",
- "pipeline_events",
- "wiki_events",
- "enable_ssl_verification",
- "token",
- ),
- )
-
-
-class GroupHook(SaveMixin, ObjectDeleteMixin, RESTObject):
- _short_print_attr = "url"
-
-
-class GroupHookManager(CRUDMixin, RESTManager):
- _path = "/groups/%(group_id)s/hooks"
- _obj_cls = GroupHook
- _from_parent_attrs = {"group_id": "id"}
- _create_attrs = RequiredOptional(
- required=("url",),
- optional=(
- "push_events",
- "issues_events",
- "confidential_issues_events",
- "merge_requests_events",
- "tag_push_events",
- "note_events",
- "confidential_note_events",
- "job_events",
- "pipeline_events",
- "wiki_page_events",
- "deployment_events",
- "releases_events",
- "subgroup_events",
- "enable_ssl_verification",
- "token",
- ),
- )
- _update_attrs = RequiredOptional(
- required=("url",),
- optional=(
- "push_events",
- "issues_events",
- "confidential_issues_events",
- "merge_requests_events",
- "tag_push_events",
- "note_events",
- "confidential_note_events",
- "job_events",
- "pipeline_events",
- "wiki_page_events",
- "deployment_events",
- "releases_events",
- "subgroup_events",
- "enable_ssl_verification",
- "token",
- ),
- )
diff --git a/gitlab/v4/objects/issues.py b/gitlab/v4/objects/issues.py
deleted file mode 100644
index 9272908..0000000
--- a/gitlab/v4/objects/issues.py
+++ /dev/null
@@ -1,256 +0,0 @@
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab import types
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import (
- CreateMixin,
- CRUDMixin,
- DeleteMixin,
- ListMixin,
- ObjectDeleteMixin,
- ParticipantsMixin,
- RetrieveMixin,
- SaveMixin,
- SubscribableMixin,
- TimeTrackingMixin,
- TodoMixin,
- UserAgentDetailMixin,
-)
-
-from .award_emojis import ProjectIssueAwardEmojiManager # noqa: F401
-from .discussions import ProjectIssueDiscussionManager # noqa: F401
-from .events import ( # noqa: F401
- ProjectIssueResourceLabelEventManager,
- ProjectIssueResourceMilestoneEventManager,
- ProjectIssueResourceStateEventManager,
-)
-from .notes import ProjectIssueNoteManager # noqa: F401
-
-__all__ = [
- "Issue",
- "IssueManager",
- "GroupIssue",
- "GroupIssueManager",
- "ProjectIssue",
- "ProjectIssueManager",
- "ProjectIssueLink",
- "ProjectIssueLinkManager",
-]
-
-
-class Issue(RESTObject):
- _url = "/issues"
- _short_print_attr = "title"
-
-
-class IssueManager(RetrieveMixin, RESTManager):
- _path = "/issues"
- _obj_cls = Issue
- _list_filters = (
- "state",
- "labels",
- "milestone",
- "scope",
- "author_id",
- "assignee_id",
- "my_reaction_emoji",
- "iids",
- "order_by",
- "sort",
- "search",
- "created_after",
- "created_before",
- "updated_after",
- "updated_before",
- )
- _types = {"iids": types.ListAttribute, "labels": types.ListAttribute}
-
-
-class GroupIssue(RESTObject):
- pass
-
-
-class GroupIssueManager(ListMixin, RESTManager):
- _path = "/groups/%(group_id)s/issues"
- _obj_cls = GroupIssue
- _from_parent_attrs = {"group_id": "id"}
- _list_filters = (
- "state",
- "labels",
- "milestone",
- "order_by",
- "sort",
- "iids",
- "author_id",
- "assignee_id",
- "my_reaction_emoji",
- "search",
- "created_after",
- "created_before",
- "updated_after",
- "updated_before",
- )
- _types = {"iids": types.ListAttribute, "labels": types.ListAttribute}
-
-
-class ProjectIssue(
- UserAgentDetailMixin,
- SubscribableMixin,
- TodoMixin,
- TimeTrackingMixin,
- ParticipantsMixin,
- SaveMixin,
- ObjectDeleteMixin,
- RESTObject,
-):
- _short_print_attr = "title"
- _id_attr = "iid"
-
- awardemojis: ProjectIssueAwardEmojiManager
- discussions: ProjectIssueDiscussionManager
- links: "ProjectIssueLinkManager"
- notes: ProjectIssueNoteManager
- resourcelabelevents: ProjectIssueResourceLabelEventManager
- resourcemilestoneevents: ProjectIssueResourceMilestoneEventManager
- resourcestateevents: ProjectIssueResourceStateEventManager
-
- @cli.register_custom_action("ProjectIssue", ("to_project_id",))
- @exc.on_http_error(exc.GitlabUpdateError)
- def move(self, to_project_id, **kwargs):
- """Move the issue to another project.
-
- Args:
- to_project_id(int): ID of the target project
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabUpdateError: If the issue could not be moved
- """
- path = "%s/%s/move" % (self.manager.path, self.get_id())
- data = {"to_project_id": to_project_id}
- server_data = self.manager.gitlab.http_post(path, post_data=data, **kwargs)
- self._update_attrs(server_data)
-
- @cli.register_custom_action("ProjectIssue")
- @exc.on_http_error(exc.GitlabGetError)
- def related_merge_requests(self, **kwargs):
- """List merge requests related to the issue.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetErrot: If the merge requests could not be retrieved
-
- Returns:
- list: The list of merge requests.
- """
- path = "%s/%s/related_merge_requests" % (self.manager.path, self.get_id())
- return self.manager.gitlab.http_get(path, **kwargs)
-
- @cli.register_custom_action("ProjectIssue")
- @exc.on_http_error(exc.GitlabGetError)
- def closed_by(self, **kwargs):
- """List merge requests that will close the issue when merged.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetErrot: If the merge requests could not be retrieved
-
- Returns:
- list: The list of merge requests.
- """
- path = "%s/%s/closed_by" % (self.manager.path, self.get_id())
- return self.manager.gitlab.http_get(path, **kwargs)
-
-
-class ProjectIssueManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/issues"
- _obj_cls = ProjectIssue
- _from_parent_attrs = {"project_id": "id"}
- _list_filters = (
- "iids",
- "state",
- "labels",
- "milestone",
- "scope",
- "author_id",
- "assignee_id",
- "my_reaction_emoji",
- "order_by",
- "sort",
- "search",
- "created_after",
- "created_before",
- "updated_after",
- "updated_before",
- )
- _create_attrs = RequiredOptional(
- required=("title",),
- optional=(
- "description",
- "confidential",
- "assignee_ids",
- "assignee_id",
- "milestone_id",
- "labels",
- "created_at",
- "due_date",
- "merge_request_to_resolve_discussions_of",
- "discussion_to_resolve",
- ),
- )
- _update_attrs = RequiredOptional(
- optional=(
- "title",
- "description",
- "confidential",
- "assignee_ids",
- "assignee_id",
- "milestone_id",
- "labels",
- "state_event",
- "updated_at",
- "due_date",
- "discussion_locked",
- ),
- )
- _types = {"iids": types.ListAttribute, "labels": types.ListAttribute}
-
-
-class ProjectIssueLink(ObjectDeleteMixin, RESTObject):
- _id_attr = "issue_link_id"
-
-
-class ProjectIssueLinkManager(ListMixin, CreateMixin, DeleteMixin, RESTManager):
- _path = "/projects/%(project_id)s/issues/%(issue_iid)s/links"
- _obj_cls = ProjectIssueLink
- _from_parent_attrs = {"project_id": "project_id", "issue_iid": "iid"}
- _create_attrs = RequiredOptional(required=("target_project_id", "target_issue_iid"))
-
- @exc.on_http_error(exc.GitlabCreateError)
- def create(self, data, **kwargs):
- """Create a new object.
-
- Args:
- data (dict): parameters to send to the server to create the
- resource
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- RESTObject, RESTObject: The source and target issues
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server cannot perform the request
- """
- self._check_missing_create_attrs(data)
- server_data = self.gitlab.http_post(self.path, post_data=data, **kwargs)
- source_issue = ProjectIssue(self._parent.manager, server_data["source_issue"])
- target_issue = ProjectIssue(self._parent.manager, server_data["target_issue"])
- return source_issue, target_issue
diff --git a/gitlab/v4/objects/jobs.py b/gitlab/v4/objects/jobs.py
deleted file mode 100644
index 2e7693d..0000000
--- a/gitlab/v4/objects/jobs.py
+++ /dev/null
@@ -1,190 +0,0 @@
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab import utils
-from gitlab.base import RESTManager, RESTObject
-from gitlab.mixins import RefreshMixin, RetrieveMixin
-
-__all__ = [
- "ProjectJob",
- "ProjectJobManager",
-]
-
-
-class ProjectJob(RefreshMixin, RESTObject):
- @cli.register_custom_action("ProjectJob")
- @exc.on_http_error(exc.GitlabJobCancelError)
- def cancel(self, **kwargs):
- """Cancel the job.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabJobCancelError: If the job could not be canceled
- """
- path = "%s/%s/cancel" % (self.manager.path, self.get_id())
- return self.manager.gitlab.http_post(path)
-
- @cli.register_custom_action("ProjectJob")
- @exc.on_http_error(exc.GitlabJobRetryError)
- def retry(self, **kwargs):
- """Retry the job.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabJobRetryError: If the job could not be retried
- """
- path = "%s/%s/retry" % (self.manager.path, self.get_id())
- return self.manager.gitlab.http_post(path)
-
- @cli.register_custom_action("ProjectJob")
- @exc.on_http_error(exc.GitlabJobPlayError)
- def play(self, **kwargs):
- """Trigger a job explicitly.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabJobPlayError: If the job could not be triggered
- """
- path = "%s/%s/play" % (self.manager.path, self.get_id())
- self.manager.gitlab.http_post(path)
-
- @cli.register_custom_action("ProjectJob")
- @exc.on_http_error(exc.GitlabJobEraseError)
- def erase(self, **kwargs):
- """Erase the job (remove job artifacts and trace).
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabJobEraseError: If the job could not be erased
- """
- path = "%s/%s/erase" % (self.manager.path, self.get_id())
- self.manager.gitlab.http_post(path)
-
- @cli.register_custom_action("ProjectJob")
- @exc.on_http_error(exc.GitlabCreateError)
- def keep_artifacts(self, **kwargs):
- """Prevent artifacts from being deleted when expiration is set.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the request could not be performed
- """
- path = "%s/%s/artifacts/keep" % (self.manager.path, self.get_id())
- self.manager.gitlab.http_post(path)
-
- @cli.register_custom_action("ProjectJob")
- @exc.on_http_error(exc.GitlabCreateError)
- def delete_artifacts(self, **kwargs):
- """Delete artifacts of a job.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabDeleteError: If the request could not be performed
- """
- path = "%s/%s/artifacts" % (self.manager.path, self.get_id())
- self.manager.gitlab.http_delete(path)
-
- @cli.register_custom_action("ProjectJob")
- @exc.on_http_error(exc.GitlabGetError)
- def artifacts(self, streamed=False, action=None, chunk_size=1024, **kwargs):
- """Get the job artifacts.
-
- Args:
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment
- action (callable): Callable responsible of dealing with chunk of
- data
- chunk_size (int): Size of each chunk
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the artifacts could not be retrieved
-
- Returns:
- str: The artifacts if `streamed` is False, None otherwise.
- """
- path = "%s/%s/artifacts" % (self.manager.path, self.get_id())
- result = self.manager.gitlab.http_get(
- path, streamed=streamed, raw=True, **kwargs
- )
- return utils.response_content(result, streamed, action, chunk_size)
-
- @cli.register_custom_action("ProjectJob")
- @exc.on_http_error(exc.GitlabGetError)
- def artifact(self, path, streamed=False, action=None, chunk_size=1024, **kwargs):
- """Get a single artifact file from within the job's artifacts archive.
-
- Args:
- path (str): Path of the artifact
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment
- action (callable): Callable responsible of dealing with chunk of
- data
- chunk_size (int): Size of each chunk
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the artifacts could not be retrieved
-
- Returns:
- str: The artifacts if `streamed` is False, None otherwise.
- """
- path = "%s/%s/artifacts/%s" % (self.manager.path, self.get_id(), path)
- result = self.manager.gitlab.http_get(
- path, streamed=streamed, raw=True, **kwargs
- )
- return utils.response_content(result, streamed, action, chunk_size)
-
- @cli.register_custom_action("ProjectJob")
- @exc.on_http_error(exc.GitlabGetError)
- def trace(self, streamed=False, action=None, chunk_size=1024, **kwargs):
- """Get the job trace.
-
- Args:
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment
- action (callable): Callable responsible of dealing with chunk of
- data
- chunk_size (int): Size of each chunk
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the artifacts could not be retrieved
-
- Returns:
- str: The trace
- """
- path = "%s/%s/trace" % (self.manager.path, self.get_id())
- result = self.manager.gitlab.http_get(
- path, streamed=streamed, raw=True, **kwargs
- )
- return utils.response_content(result, streamed, action, chunk_size)
-
-
-class ProjectJobManager(RetrieveMixin, RESTManager):
- _path = "/projects/%(project_id)s/jobs"
- _obj_cls = ProjectJob
- _from_parent_attrs = {"project_id": "id"}
diff --git a/gitlab/v4/objects/keys.py b/gitlab/v4/objects/keys.py
deleted file mode 100644
index 7f8fa0e..0000000
--- a/gitlab/v4/objects/keys.py
+++ /dev/null
@@ -1,26 +0,0 @@
-from gitlab.base import RESTManager, RESTObject
-from gitlab.mixins import GetMixin
-
-__all__ = [
- "Key",
- "KeyManager",
-]
-
-
-class Key(RESTObject):
- pass
-
-
-class KeyManager(GetMixin, RESTManager):
- _path = "/keys"
- _obj_cls = Key
-
- def get(self, id=None, **kwargs):
- if id is not None:
- return super(KeyManager, self).get(id, **kwargs)
-
- if "fingerprint" not in kwargs:
- raise AttributeError("Missing attribute: id or fingerprint")
-
- server_data = self.gitlab.http_get(self.path, **kwargs)
- return self._obj_cls(self, server_data)
diff --git a/gitlab/v4/objects/labels.py b/gitlab/v4/objects/labels.py
deleted file mode 100644
index 544c3cd..0000000
--- a/gitlab/v4/objects/labels.py
+++ /dev/null
@@ -1,149 +0,0 @@
-from gitlab import exceptions as exc
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import (
- CreateMixin,
- DeleteMixin,
- ListMixin,
- ObjectDeleteMixin,
- RetrieveMixin,
- SaveMixin,
- SubscribableMixin,
- UpdateMixin,
-)
-
-__all__ = [
- "GroupLabel",
- "GroupLabelManager",
- "ProjectLabel",
- "ProjectLabelManager",
-]
-
-
-class GroupLabel(SubscribableMixin, SaveMixin, ObjectDeleteMixin, RESTObject):
- _id_attr = "name"
-
- # Update without ID, but we need an ID to get from list.
- @exc.on_http_error(exc.GitlabUpdateError)
- def save(self, **kwargs):
- """Saves the changes made to the object to the server.
-
- The object is updated to match what the server returns.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct.
- GitlabUpdateError: If the server cannot perform the request.
- """
- updated_data = self._get_updated_data()
-
- # call the manager
- server_data = self.manager.update(None, updated_data, **kwargs)
- self._update_attrs(server_data)
-
-
-class GroupLabelManager(ListMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager):
- _path = "/groups/%(group_id)s/labels"
- _obj_cls = GroupLabel
- _from_parent_attrs = {"group_id": "id"}
- _create_attrs = RequiredOptional(
- required=("name", "color"), optional=("description", "priority")
- )
- _update_attrs = RequiredOptional(
- required=("name",), optional=("new_name", "color", "description", "priority")
- )
-
- # Update without ID.
- def update(self, name, new_data=None, **kwargs):
- """Update a Label on the server.
-
- Args:
- name: The name of the label
- **kwargs: Extra options to send to the server (e.g. sudo)
- """
- new_data = new_data or {}
- if name:
- new_data["name"] = name
- return super().update(id=None, new_data=new_data, **kwargs)
-
- # Delete without ID.
- @exc.on_http_error(exc.GitlabDeleteError)
- def delete(self, name, **kwargs):
- """Delete a Label on the server.
-
- Args:
- name: The name of the label
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabDeleteError: If the server cannot perform the request
- """
- self.gitlab.http_delete(self.path, query_data={"name": name}, **kwargs)
-
-
-class ProjectLabel(SubscribableMixin, SaveMixin, ObjectDeleteMixin, RESTObject):
- _id_attr = "name"
-
- # Update without ID, but we need an ID to get from list.
- @exc.on_http_error(exc.GitlabUpdateError)
- def save(self, **kwargs):
- """Saves the changes made to the object to the server.
-
- The object is updated to match what the server returns.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct.
- GitlabUpdateError: If the server cannot perform the request.
- """
- updated_data = self._get_updated_data()
-
- # call the manager
- server_data = self.manager.update(None, updated_data, **kwargs)
- self._update_attrs(server_data)
-
-
-class ProjectLabelManager(
- RetrieveMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager
-):
- _path = "/projects/%(project_id)s/labels"
- _obj_cls = ProjectLabel
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("name", "color"), optional=("description", "priority")
- )
- _update_attrs = RequiredOptional(
- required=("name",), optional=("new_name", "color", "description", "priority")
- )
-
- # Update without ID.
- def update(self, name, new_data=None, **kwargs):
- """Update a Label on the server.
-
- Args:
- name: The name of the label
- **kwargs: Extra options to send to the server (e.g. sudo)
- """
- new_data = new_data or {}
- if name:
- new_data["name"] = name
- return super().update(id=None, new_data=new_data, **kwargs)
-
- # Delete without ID.
- @exc.on_http_error(exc.GitlabDeleteError)
- def delete(self, name, **kwargs):
- """Delete a Label on the server.
-
- Args:
- name: The name of the label
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabDeleteError: If the server cannot perform the request
- """
- self.gitlab.http_delete(self.path, query_data={"name": name}, **kwargs)
diff --git a/gitlab/v4/objects/ldap.py b/gitlab/v4/objects/ldap.py
deleted file mode 100644
index e0202a1..0000000
--- a/gitlab/v4/objects/ldap.py
+++ /dev/null
@@ -1,51 +0,0 @@
-from gitlab import exceptions as exc
-from gitlab.base import RESTManager, RESTObject, RESTObjectList
-
-__all__ = [
- "LDAPGroup",
- "LDAPGroupManager",
-]
-
-
-class LDAPGroup(RESTObject):
- _id_attr = None
-
-
-class LDAPGroupManager(RESTManager):
- _path = "/ldap/groups"
- _obj_cls = LDAPGroup
- _list_filters = ("search", "provider")
-
- @exc.on_http_error(exc.GitlabListError)
- def list(self, **kwargs):
- """Retrieve a list of objects.
-
- Args:
- all (bool): If True, return all the items, without pagination
- per_page (int): Number of items to retrieve per request
- page (int): ID of the page to return (starts with page 1)
- as_list (bool): If set to False and no pagination option is
- defined, return a generator instead of a list
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- list: The list of objects, or a generator if `as_list` is False
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the server cannot perform the request
- """
- data = kwargs.copy()
- if self.gitlab.per_page:
- data.setdefault("per_page", self.gitlab.per_page)
-
- if "provider" in data:
- path = "/ldap/%s/groups" % data["provider"]
- else:
- path = self._path
-
- obj = self.gitlab.http_list(path, **data)
- if isinstance(obj, list):
- return [self._obj_cls(self, item) for item in obj]
- else:
- return RESTObjectList(self, self._obj_cls, obj)
diff --git a/gitlab/v4/objects/members.py b/gitlab/v4/objects/members.py
deleted file mode 100644
index 0c92185..0000000
--- a/gitlab/v4/objects/members.py
+++ /dev/null
@@ -1,92 +0,0 @@
-from gitlab import types
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import (
- CRUDMixin,
- DeleteMixin,
- ListMixin,
- ObjectDeleteMixin,
- RetrieveMixin,
- SaveMixin,
-)
-
-__all__ = [
- "GroupBillableMember",
- "GroupBillableMemberManager",
- "GroupBillableMemberMembership",
- "GroupBillableMemberMembershipManager",
- "GroupMember",
- "GroupMemberManager",
- "GroupMemberAllManager",
- "ProjectMember",
- "ProjectMemberManager",
- "ProjectMemberAllManager",
-]
-
-
-class GroupMember(SaveMixin, ObjectDeleteMixin, RESTObject):
- _short_print_attr = "username"
-
-
-class GroupMemberManager(CRUDMixin, RESTManager):
- _path = "/groups/%(group_id)s/members"
- _obj_cls = GroupMember
- _from_parent_attrs = {"group_id": "id"}
- _create_attrs = RequiredOptional(
- required=("access_level", "user_id"), optional=("expires_at",)
- )
- _update_attrs = RequiredOptional(
- required=("access_level",), optional=("expires_at",)
- )
- _types = {"user_ids": types.ListAttribute}
-
-
-class GroupBillableMember(ObjectDeleteMixin, RESTObject):
- _short_print_attr = "username"
-
- memberships: "GroupBillableMemberMembershipManager"
-
-
-class GroupBillableMemberManager(ListMixin, DeleteMixin, RESTManager):
- _path = "/groups/%(group_id)s/billable_members"
- _obj_cls = GroupBillableMember
- _from_parent_attrs = {"group_id": "id"}
- _list_filters = ("search", "sort")
-
-
-class GroupBillableMemberMembership(RESTObject):
- _id_attr = "user_id"
-
-
-class GroupBillableMemberMembershipManager(ListMixin, RESTManager):
- _path = "/groups/%(group_id)s/billable_members/%(user_id)s/memberships"
- _obj_cls = GroupBillableMemberMembership
- _from_parent_attrs = {"group_id": "group_id", "user_id": "id"}
-
-
-class GroupMemberAllManager(RetrieveMixin, RESTManager):
- _path = "/groups/%(group_id)s/members/all"
- _obj_cls = GroupMember
- _from_parent_attrs = {"group_id": "id"}
-
-
-class ProjectMember(SaveMixin, ObjectDeleteMixin, RESTObject):
- _short_print_attr = "username"
-
-
-class ProjectMemberManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/members"
- _obj_cls = ProjectMember
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("access_level", "user_id"), optional=("expires_at",)
- )
- _update_attrs = RequiredOptional(
- required=("access_level",), optional=("expires_at",)
- )
- _types = {"user_ids": types.ListAttribute}
-
-
-class ProjectMemberAllManager(RetrieveMixin, RESTManager):
- _path = "/projects/%(project_id)s/members/all"
- _obj_cls = ProjectMember
- _from_parent_attrs = {"project_id": "id"}
diff --git a/gitlab/v4/objects/merge_request_approvals.py b/gitlab/v4/objects/merge_request_approvals.py
deleted file mode 100644
index 4a41ca4..0000000
--- a/gitlab/v4/objects/merge_request_approvals.py
+++ /dev/null
@@ -1,206 +0,0 @@
-from gitlab import exceptions as exc
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import (
- CreateMixin,
- DeleteMixin,
- GetWithoutIdMixin,
- ListMixin,
- ObjectDeleteMixin,
- SaveMixin,
- UpdateMixin,
-)
-
-__all__ = [
- "ProjectApproval",
- "ProjectApprovalManager",
- "ProjectApprovalRule",
- "ProjectApprovalRuleManager",
- "ProjectMergeRequestApproval",
- "ProjectMergeRequestApprovalManager",
- "ProjectMergeRequestApprovalRule",
- "ProjectMergeRequestApprovalRuleManager",
-]
-
-
-class ProjectApproval(SaveMixin, RESTObject):
- _id_attr = None
-
-
-class ProjectApprovalManager(GetWithoutIdMixin, UpdateMixin, RESTManager):
- _path = "/projects/%(project_id)s/approvals"
- _obj_cls = ProjectApproval
- _from_parent_attrs = {"project_id": "id"}
- _update_attrs = RequiredOptional(
- optional=(
- "approvals_before_merge",
- "reset_approvals_on_push",
- "disable_overriding_approvers_per_merge_request",
- "merge_requests_author_approval",
- "merge_requests_disable_committers_approval",
- ),
- )
- _update_uses_post = True
-
- @exc.on_http_error(exc.GitlabUpdateError)
- def set_approvers(self, approver_ids=None, approver_group_ids=None, **kwargs):
- """Change project-level allowed approvers and approver groups.
-
- Args:
- approver_ids (list): User IDs that can approve MRs
- approver_group_ids (list): Group IDs whose members can approve MRs
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabUpdateError: If the server failed to perform the request
- """
- approver_ids = approver_ids or []
- approver_group_ids = approver_group_ids or []
-
- path = "/projects/%s/approvers" % self._parent.get_id()
- data = {"approver_ids": approver_ids, "approver_group_ids": approver_group_ids}
- self.gitlab.http_put(path, post_data=data, **kwargs)
-
-
-class ProjectApprovalRule(SaveMixin, ObjectDeleteMixin, RESTObject):
- _id_attr = "id"
-
-
-class ProjectApprovalRuleManager(
- ListMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager
-):
- _path = "/projects/%(project_id)s/approval_rules"
- _obj_cls = ProjectApprovalRule
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("name", "approvals_required"),
- optional=("user_ids", "group_ids", "protected_branch_ids"),
- )
-
-
-class ProjectMergeRequestApproval(SaveMixin, RESTObject):
- _id_attr = None
-
-
-class ProjectMergeRequestApprovalManager(GetWithoutIdMixin, UpdateMixin, RESTManager):
- _path = "/projects/%(project_id)s/merge_requests/%(mr_iid)s/approvals"
- _obj_cls = ProjectMergeRequestApproval
- _from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}
- _update_attrs = RequiredOptional(required=("approvals_required",))
- _update_uses_post = True
-
- @exc.on_http_error(exc.GitlabUpdateError)
- def set_approvers(
- self,
- approvals_required,
- approver_ids=None,
- approver_group_ids=None,
- approval_rule_name="name",
- **kwargs
- ):
- """Change MR-level allowed approvers and approver groups.
-
- Args:
- approvals_required (integer): The number of required approvals for this rule
- approver_ids (list of integers): User IDs that can approve MRs
- approver_group_ids (list): Group IDs whose members can approve MRs
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabUpdateError: If the server failed to perform the request
- """
- approver_ids = approver_ids or []
- approver_group_ids = approver_group_ids or []
-
- data = {
- "name": approval_rule_name,
- "approvals_required": approvals_required,
- "rule_type": "regular",
- "user_ids": approver_ids,
- "group_ids": approver_group_ids,
- }
- approval_rules = self._parent.approval_rules
- """ update any existing approval rule matching the name"""
- existing_approval_rules = approval_rules.list()
- for ar in existing_approval_rules:
- if ar.name == approval_rule_name:
- ar.user_ids = data["user_ids"]
- ar.approvals_required = data["approvals_required"]
- ar.group_ids = data["group_ids"]
- ar.save()
- return ar
- """ if there was no rule matching the rule name, create a new one"""
- return approval_rules.create(data=data)
-
-
-class ProjectMergeRequestApprovalRule(SaveMixin, RESTObject):
- _id_attr = "approval_rule_id"
- _short_print_attr = "approval_rule"
-
- @exc.on_http_error(exc.GitlabUpdateError)
- def save(self, **kwargs):
- """Save the changes made to the object to the server.
-
- The object is updated to match what the server returns.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raise:
- GitlabAuthenticationError: If authentication is not correct
- GitlabUpdateError: If the server cannot perform the request
- """
- # There is a mismatch between the name of our id attribute and the put REST API name for the
- # project_id, so we override it here.
- self.approval_rule_id = self.id
- self.merge_request_iid = self._parent_attrs["mr_iid"]
- self.id = self._parent_attrs["project_id"]
- # save will update self.id with the result from the server, so no need to overwrite with
- # what it was before we overwrote it."""
- SaveMixin.save(self, **kwargs)
-
-
-class ProjectMergeRequestApprovalRuleManager(
- ListMixin, UpdateMixin, CreateMixin, RESTManager
-):
- _path = "/projects/%(project_id)s/merge_requests/%(mr_iid)s/approval_rules"
- _obj_cls = ProjectMergeRequestApprovalRule
- _from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}
- _list_filters = ("name", "rule_type")
- _update_attrs = RequiredOptional(
- required=(
- "id",
- "merge_request_iid",
- "approval_rule_id",
- "name",
- "approvals_required",
- ),
- optional=("user_ids", "group_ids"),
- )
- # Important: When approval_project_rule_id is set, the name, users and groups of
- # project-level rule will be copied. The approvals_required specified will be used. """
- _create_attrs = RequiredOptional(
- required=("id", "merge_request_iid", "name", "approvals_required"),
- optional=("approval_project_rule_id", "user_ids", "group_ids"),
- )
-
- def create(self, data, **kwargs):
- """Create a new object.
-
- Args:
- data (dict): Parameters to send to the server to create the
- resource
- **kwargs: Extra options to send to the server (e.g. sudo or
- 'ref_name', 'stage', 'name', 'all')
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server cannot perform the request
-
- Returns:
- RESTObject: A new instance of the manage object class build with
- the data sent by the server
- """
- new_data = data.copy()
- new_data["id"] = self._from_parent_attrs["project_id"]
- new_data["merge_request_iid"] = self._from_parent_attrs["mr_iid"]
- return CreateMixin.create(self, new_data, **kwargs)
diff --git a/gitlab/v4/objects/merge_requests.py b/gitlab/v4/objects/merge_requests.py
deleted file mode 100644
index 4def98c..0000000
--- a/gitlab/v4/objects/merge_requests.py
+++ /dev/null
@@ -1,439 +0,0 @@
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab import types
-from gitlab.base import RequiredOptional, RESTManager, RESTObject, RESTObjectList
-from gitlab.mixins import (
- CRUDMixin,
- ListMixin,
- ObjectDeleteMixin,
- ParticipantsMixin,
- RetrieveMixin,
- SaveMixin,
- SubscribableMixin,
- TimeTrackingMixin,
- TodoMixin,
-)
-
-from .award_emojis import ProjectMergeRequestAwardEmojiManager # noqa: F401
-from .commits import ProjectCommit, ProjectCommitManager
-from .discussions import ProjectMergeRequestDiscussionManager # noqa: F401
-from .events import ( # noqa: F401
- ProjectMergeRequestResourceLabelEventManager,
- ProjectMergeRequestResourceMilestoneEventManager,
- ProjectMergeRequestResourceStateEventManager,
-)
-from .issues import ProjectIssue, ProjectIssueManager
-from .merge_request_approvals import ( # noqa: F401
- ProjectMergeRequestApprovalManager,
- ProjectMergeRequestApprovalRuleManager,
-)
-from .notes import ProjectMergeRequestNoteManager # noqa: F401
-from .pipelines import ProjectMergeRequestPipelineManager # noqa: F401
-
-__all__ = [
- "MergeRequest",
- "MergeRequestManager",
- "GroupMergeRequest",
- "GroupMergeRequestManager",
- "ProjectMergeRequest",
- "ProjectMergeRequestManager",
- "ProjectDeploymentMergeRequest",
- "ProjectDeploymentMergeRequestManager",
- "ProjectMergeRequestDiff",
- "ProjectMergeRequestDiffManager",
-]
-
-
-class MergeRequest(RESTObject):
- pass
-
-
-class MergeRequestManager(ListMixin, RESTManager):
- _path = "/merge_requests"
- _obj_cls = MergeRequest
- _list_filters = (
- "state",
- "order_by",
- "sort",
- "milestone",
- "view",
- "labels",
- "with_labels_details",
- "with_merge_status_recheck",
- "created_after",
- "created_before",
- "updated_after",
- "updated_before",
- "scope",
- "author_id",
- "author_username",
- "assignee_id",
- "approver_ids",
- "approved_by_ids",
- "reviewer_id",
- "reviewer_username",
- "my_reaction_emoji",
- "source_branch",
- "target_branch",
- "search",
- "in",
- "wip",
- "not",
- "environment",
- "deployed_before",
- "deployed_after",
- )
- _types = {
- "approver_ids": types.ListAttribute,
- "approved_by_ids": types.ListAttribute,
- "in": types.ListAttribute,
- "labels": types.ListAttribute,
- }
-
-
-class GroupMergeRequest(RESTObject):
- pass
-
-
-class GroupMergeRequestManager(ListMixin, RESTManager):
- _path = "/groups/%(group_id)s/merge_requests"
- _obj_cls = GroupMergeRequest
- _from_parent_attrs = {"group_id": "id"}
- _list_filters = (
- "state",
- "order_by",
- "sort",
- "milestone",
- "view",
- "labels",
- "created_after",
- "created_before",
- "updated_after",
- "updated_before",
- "scope",
- "author_id",
- "assignee_id",
- "approver_ids",
- "approved_by_ids",
- "my_reaction_emoji",
- "source_branch",
- "target_branch",
- "search",
- "wip",
- )
- _types = {
- "approver_ids": types.ListAttribute,
- "approved_by_ids": types.ListAttribute,
- "labels": types.ListAttribute,
- }
-
-
-class ProjectMergeRequest(
- SubscribableMixin,
- TodoMixin,
- TimeTrackingMixin,
- ParticipantsMixin,
- SaveMixin,
- ObjectDeleteMixin,
- RESTObject,
-):
- _id_attr = "iid"
-
- approval_rules: ProjectMergeRequestApprovalRuleManager
- approvals: ProjectMergeRequestApprovalManager
- awardemojis: ProjectMergeRequestAwardEmojiManager
- diffs: "ProjectMergeRequestDiffManager"
- discussions: ProjectMergeRequestDiscussionManager
- notes: ProjectMergeRequestNoteManager
- pipelines: ProjectMergeRequestPipelineManager
- resourcelabelevents: ProjectMergeRequestResourceLabelEventManager
- resourcemilestoneevents: ProjectMergeRequestResourceMilestoneEventManager
- resourcestateevents: ProjectMergeRequestResourceStateEventManager
-
- @cli.register_custom_action("ProjectMergeRequest")
- @exc.on_http_error(exc.GitlabMROnBuildSuccessError)
- def cancel_merge_when_pipeline_succeeds(self, **kwargs):
- """Cancel merge when the pipeline succeeds.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabMROnBuildSuccessError: If the server could not handle the
- request
- """
-
- path = "%s/%s/cancel_merge_when_pipeline_succeeds" % (
- self.manager.path,
- self.get_id(),
- )
- server_data = self.manager.gitlab.http_put(path, **kwargs)
- self._update_attrs(server_data)
-
- @cli.register_custom_action("ProjectMergeRequest")
- @exc.on_http_error(exc.GitlabListError)
- def closes_issues(self, **kwargs):
- """List issues that will close on merge."
-
- Args:
- all (bool): If True, return all the items, without pagination
- per_page (int): Number of items to retrieve per request
- page (int): ID of the page to return (starts with page 1)
- as_list (bool): If set to False and no pagination option is
- defined, return a generator instead of a list
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the list could not be retrieved
-
- Returns:
- RESTObjectList: List of issues
- """
- path = "%s/%s/closes_issues" % (self.manager.path, self.get_id())
- data_list = self.manager.gitlab.http_list(path, as_list=False, **kwargs)
- manager = ProjectIssueManager(self.manager.gitlab, parent=self.manager._parent)
- return RESTObjectList(manager, ProjectIssue, data_list)
-
- @cli.register_custom_action("ProjectMergeRequest")
- @exc.on_http_error(exc.GitlabListError)
- def commits(self, **kwargs):
- """List the merge request commits.
-
- Args:
- all (bool): If True, return all the items, without pagination
- per_page (int): Number of items to retrieve per request
- page (int): ID of the page to return (starts with page 1)
- as_list (bool): If set to False and no pagination option is
- defined, return a generator instead of a list
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the list could not be retrieved
-
- Returns:
- RESTObjectList: The list of commits
- """
-
- path = "%s/%s/commits" % (self.manager.path, self.get_id())
- data_list = self.manager.gitlab.http_list(path, as_list=False, **kwargs)
- manager = ProjectCommitManager(self.manager.gitlab, parent=self.manager._parent)
- return RESTObjectList(manager, ProjectCommit, data_list)
-
- @cli.register_custom_action("ProjectMergeRequest")
- @exc.on_http_error(exc.GitlabListError)
- def changes(self, **kwargs):
- """List the merge request changes.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the list could not be retrieved
-
- Returns:
- RESTObjectList: List of changes
- """
- path = "%s/%s/changes" % (self.manager.path, self.get_id())
- return self.manager.gitlab.http_get(path, **kwargs)
-
- @cli.register_custom_action("ProjectMergeRequest", tuple(), ("sha",))
- @exc.on_http_error(exc.GitlabMRApprovalError)
- def approve(self, sha=None, **kwargs):
- """Approve the merge request.
-
- Args:
- sha (str): Head SHA of MR
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabMRApprovalError: If the approval failed
- """
- path = "%s/%s/approve" % (self.manager.path, self.get_id())
- data = {}
- if sha:
- data["sha"] = sha
-
- server_data = self.manager.gitlab.http_post(path, post_data=data, **kwargs)
- self._update_attrs(server_data)
-
- @cli.register_custom_action("ProjectMergeRequest")
- @exc.on_http_error(exc.GitlabMRApprovalError)
- def unapprove(self, **kwargs):
- """Unapprove the merge request.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabMRApprovalError: If the unapproval failed
- """
- path = "%s/%s/unapprove" % (self.manager.path, self.get_id())
- data = {}
-
- server_data = self.manager.gitlab.http_post(path, post_data=data, **kwargs)
- self._update_attrs(server_data)
-
- @cli.register_custom_action("ProjectMergeRequest")
- @exc.on_http_error(exc.GitlabMRRebaseError)
- def rebase(self, **kwargs):
- """Attempt to rebase the source branch onto the target branch
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabMRRebaseError: If rebasing failed
- """
- path = "%s/%s/rebase" % (self.manager.path, self.get_id())
- data = {}
- return self.manager.gitlab.http_put(path, post_data=data, **kwargs)
-
- @cli.register_custom_action("ProjectMergeRequest")
- @exc.on_http_error(exc.GitlabGetError)
- def merge_ref(self, **kwargs):
- """Attempt to merge changes between source and target branches into
- `refs/merge-requests/:iid/merge`.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabGetError: If cannot be merged
- """
- path = "%s/%s/merge_ref" % (self.manager.path, self.get_id())
- return self.manager.gitlab.http_get(path, **kwargs)
-
- @cli.register_custom_action(
- "ProjectMergeRequest",
- tuple(),
- (
- "merge_commit_message",
- "should_remove_source_branch",
- "merge_when_pipeline_succeeds",
- ),
- )
- @exc.on_http_error(exc.GitlabMRClosedError)
- def merge(
- self,
- merge_commit_message=None,
- should_remove_source_branch=False,
- merge_when_pipeline_succeeds=False,
- **kwargs
- ):
- """Accept the merge request.
-
- Args:
- merge_commit_message (bool): Commit message
- should_remove_source_branch (bool): If True, removes the source
- branch
- merge_when_pipeline_succeeds (bool): Wait for the build to succeed,
- then merge
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabMRClosedError: If the merge failed
- """
- path = "%s/%s/merge" % (self.manager.path, self.get_id())
- data = {}
- if merge_commit_message:
- data["merge_commit_message"] = merge_commit_message
- if should_remove_source_branch is not None:
- data["should_remove_source_branch"] = should_remove_source_branch
- if merge_when_pipeline_succeeds:
- data["merge_when_pipeline_succeeds"] = True
-
- server_data = self.manager.gitlab.http_put(path, post_data=data, **kwargs)
- self._update_attrs(server_data)
-
-
-class ProjectMergeRequestManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/merge_requests"
- _obj_cls = ProjectMergeRequest
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("source_branch", "target_branch", "title"),
- optional=(
- "assignee_id",
- "description",
- "target_project_id",
- "labels",
- "milestone_id",
- "remove_source_branch",
- "allow_maintainer_to_push",
- "squash",
- "reviewer_ids",
- ),
- )
- _update_attrs = RequiredOptional(
- optional=(
- "target_branch",
- "assignee_id",
- "title",
- "description",
- "state_event",
- "labels",
- "milestone_id",
- "remove_source_branch",
- "discussion_locked",
- "allow_maintainer_to_push",
- "squash",
- "reviewer_ids",
- ),
- )
- _list_filters = (
- "state",
- "order_by",
- "sort",
- "milestone",
- "view",
- "labels",
- "created_after",
- "created_before",
- "updated_after",
- "updated_before",
- "scope",
- "iids",
- "author_id",
- "assignee_id",
- "approver_ids",
- "approved_by_ids",
- "my_reaction_emoji",
- "source_branch",
- "target_branch",
- "search",
- "wip",
- )
- _types = {
- "approver_ids": types.ListAttribute,
- "approved_by_ids": types.ListAttribute,
- "iids": types.ListAttribute,
- "labels": types.ListAttribute,
- }
-
-
-class ProjectDeploymentMergeRequest(MergeRequest):
- pass
-
-
-class ProjectDeploymentMergeRequestManager(MergeRequestManager):
- _path = "/projects/%(project_id)s/deployments/%(deployment_id)s/merge_requests"
- _obj_cls = ProjectDeploymentMergeRequest
- _from_parent_attrs = {"deployment_id": "id", "project_id": "project_id"}
-
-
-class ProjectMergeRequestDiff(RESTObject):
- pass
-
-
-class ProjectMergeRequestDiffManager(RetrieveMixin, RESTManager):
- _path = "/projects/%(project_id)s/merge_requests/%(mr_iid)s/versions"
- _obj_cls = ProjectMergeRequestDiff
- _from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}
diff --git a/gitlab/v4/objects/milestones.py b/gitlab/v4/objects/milestones.py
deleted file mode 100644
index 0a53e1b..0000000
--- a/gitlab/v4/objects/milestones.py
+++ /dev/null
@@ -1,164 +0,0 @@
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab import types
-from gitlab.base import RequiredOptional, RESTManager, RESTObject, RESTObjectList
-from gitlab.mixins import CRUDMixin, ObjectDeleteMixin, SaveMixin
-
-from .issues import GroupIssue, GroupIssueManager, ProjectIssue, ProjectIssueManager
-from .merge_requests import (
- GroupMergeRequest,
- ProjectMergeRequest,
- ProjectMergeRequestManager,
-)
-
-__all__ = [
- "GroupMilestone",
- "GroupMilestoneManager",
- "ProjectMilestone",
- "ProjectMilestoneManager",
-]
-
-
-class GroupMilestone(SaveMixin, ObjectDeleteMixin, RESTObject):
- _short_print_attr = "title"
-
- @cli.register_custom_action("GroupMilestone")
- @exc.on_http_error(exc.GitlabListError)
- def issues(self, **kwargs):
- """List issues related to this milestone.
-
- Args:
- all (bool): If True, return all the items, without pagination
- per_page (int): Number of items to retrieve per request
- page (int): ID of the page to return (starts with page 1)
- as_list (bool): If set to False and no pagination option is
- defined, return a generator instead of a list
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the list could not be retrieved
-
- Returns:
- RESTObjectList: The list of issues
- """
-
- path = "%s/%s/issues" % (self.manager.path, self.get_id())
- data_list = self.manager.gitlab.http_list(path, as_list=False, **kwargs)
- manager = GroupIssueManager(self.manager.gitlab, parent=self.manager._parent)
- # FIXME(gpocentek): the computed manager path is not correct
- return RESTObjectList(manager, GroupIssue, data_list)
-
- @cli.register_custom_action("GroupMilestone")
- @exc.on_http_error(exc.GitlabListError)
- def merge_requests(self, **kwargs):
- """List the merge requests related to this milestone.
-
- Args:
- all (bool): If True, return all the items, without pagination
- per_page (int): Number of items to retrieve per request
- page (int): ID of the page to return (starts with page 1)
- as_list (bool): If set to False and no pagination option is
- defined, return a generator instead of a list
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the list could not be retrieved
-
- Returns:
- RESTObjectList: The list of merge requests
- """
- path = "%s/%s/merge_requests" % (self.manager.path, self.get_id())
- data_list = self.manager.gitlab.http_list(path, as_list=False, **kwargs)
- manager = GroupIssueManager(self.manager.gitlab, parent=self.manager._parent)
- # FIXME(gpocentek): the computed manager path is not correct
- return RESTObjectList(manager, GroupMergeRequest, data_list)
-
-
-class GroupMilestoneManager(CRUDMixin, RESTManager):
- _path = "/groups/%(group_id)s/milestones"
- _obj_cls = GroupMilestone
- _from_parent_attrs = {"group_id": "id"}
- _create_attrs = RequiredOptional(
- required=("title",), optional=("description", "due_date", "start_date")
- )
- _update_attrs = RequiredOptional(
- optional=("title", "description", "due_date", "start_date", "state_event"),
- )
- _list_filters = ("iids", "state", "search")
- _types = {"iids": types.ListAttribute}
-
-
-class ProjectMilestone(SaveMixin, ObjectDeleteMixin, RESTObject):
- _short_print_attr = "title"
-
- @cli.register_custom_action("ProjectMilestone")
- @exc.on_http_error(exc.GitlabListError)
- def issues(self, **kwargs):
- """List issues related to this milestone.
-
- Args:
- all (bool): If True, return all the items, without pagination
- per_page (int): Number of items to retrieve per request
- page (int): ID of the page to return (starts with page 1)
- as_list (bool): If set to False and no pagination option is
- defined, return a generator instead of a list
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the list could not be retrieved
-
- Returns:
- RESTObjectList: The list of issues
- """
-
- path = "%s/%s/issues" % (self.manager.path, self.get_id())
- data_list = self.manager.gitlab.http_list(path, as_list=False, **kwargs)
- manager = ProjectIssueManager(self.manager.gitlab, parent=self.manager._parent)
- # FIXME(gpocentek): the computed manager path is not correct
- return RESTObjectList(manager, ProjectIssue, data_list)
-
- @cli.register_custom_action("ProjectMilestone")
- @exc.on_http_error(exc.GitlabListError)
- def merge_requests(self, **kwargs):
- """List the merge requests related to this milestone.
-
- Args:
- all (bool): If True, return all the items, without pagination
- per_page (int): Number of items to retrieve per request
- page (int): ID of the page to return (starts with page 1)
- as_list (bool): If set to False and no pagination option is
- defined, return a generator instead of a list
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the list could not be retrieved
-
- Returns:
- RESTObjectList: The list of merge requests
- """
- path = "%s/%s/merge_requests" % (self.manager.path, self.get_id())
- data_list = self.manager.gitlab.http_list(path, as_list=False, **kwargs)
- manager = ProjectMergeRequestManager(
- self.manager.gitlab, parent=self.manager._parent
- )
- # FIXME(gpocentek): the computed manager path is not correct
- return RESTObjectList(manager, ProjectMergeRequest, data_list)
-
-
-class ProjectMilestoneManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/milestones"
- _obj_cls = ProjectMilestone
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("title",),
- optional=("description", "due_date", "start_date", "state_event"),
- )
- _update_attrs = RequiredOptional(
- optional=("title", "description", "due_date", "start_date", "state_event"),
- )
- _list_filters = ("iids", "state", "search")
- _types = {"iids": types.ListAttribute}
diff --git a/gitlab/v4/objects/namespaces.py b/gitlab/v4/objects/namespaces.py
deleted file mode 100644
index deee281..0000000
--- a/gitlab/v4/objects/namespaces.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from gitlab.base import RESTManager, RESTObject
-from gitlab.mixins import RetrieveMixin
-
-__all__ = [
- "Namespace",
- "NamespaceManager",
-]
-
-
-class Namespace(RESTObject):
- pass
-
-
-class NamespaceManager(RetrieveMixin, RESTManager):
- _path = "/namespaces"
- _obj_cls = Namespace
- _list_filters = ("search",)
diff --git a/gitlab/v4/objects/notes.py b/gitlab/v4/objects/notes.py
deleted file mode 100644
index cbd237e..0000000
--- a/gitlab/v4/objects/notes.py
+++ /dev/null
@@ -1,169 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import (
- CreateMixin,
- CRUDMixin,
- DeleteMixin,
- GetMixin,
- ObjectDeleteMixin,
- RetrieveMixin,
- SaveMixin,
- UpdateMixin,
-)
-
-from .award_emojis import ( # noqa: F401
- ProjectIssueNoteAwardEmojiManager,
- ProjectMergeRequestNoteAwardEmojiManager,
- ProjectSnippetNoteAwardEmojiManager,
-)
-
-__all__ = [
- "ProjectNote",
- "ProjectNoteManager",
- "ProjectCommitDiscussionNote",
- "ProjectCommitDiscussionNoteManager",
- "ProjectIssueNote",
- "ProjectIssueNoteManager",
- "ProjectIssueDiscussionNote",
- "ProjectIssueDiscussionNoteManager",
- "ProjectMergeRequestNote",
- "ProjectMergeRequestNoteManager",
- "ProjectMergeRequestDiscussionNote",
- "ProjectMergeRequestDiscussionNoteManager",
- "ProjectSnippetNote",
- "ProjectSnippetNoteManager",
- "ProjectSnippetDiscussionNote",
- "ProjectSnippetDiscussionNoteManager",
-]
-
-
-class ProjectNote(RESTObject):
- pass
-
-
-class ProjectNoteManager(RetrieveMixin, RESTManager):
- _path = "/projects/%(project_id)s/notes"
- _obj_cls = ProjectNote
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(required=("body",))
-
-
-class ProjectCommitDiscussionNote(SaveMixin, ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectCommitDiscussionNoteManager(
- GetMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager
-):
- _path = (
- "/projects/%(project_id)s/repository/commits/%(commit_id)s/"
- "discussions/%(discussion_id)s/notes"
- )
- _obj_cls = ProjectCommitDiscussionNote
- _from_parent_attrs = {
- "project_id": "project_id",
- "commit_id": "commit_id",
- "discussion_id": "id",
- }
- _create_attrs = RequiredOptional(
- required=("body",), optional=("created_at", "position")
- )
- _update_attrs = RequiredOptional(required=("body",))
-
-
-class ProjectIssueNote(SaveMixin, ObjectDeleteMixin, RESTObject):
- awardemojis: ProjectIssueNoteAwardEmojiManager
-
-
-class ProjectIssueNoteManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/issues/%(issue_iid)s/notes"
- _obj_cls = ProjectIssueNote
- _from_parent_attrs = {"project_id": "project_id", "issue_iid": "iid"}
- _create_attrs = RequiredOptional(required=("body",), optional=("created_at",))
- _update_attrs = RequiredOptional(required=("body",))
-
-
-class ProjectIssueDiscussionNote(SaveMixin, ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectIssueDiscussionNoteManager(
- GetMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager
-):
- _path = (
- "/projects/%(project_id)s/issues/%(issue_iid)s/"
- "discussions/%(discussion_id)s/notes"
- )
- _obj_cls = ProjectIssueDiscussionNote
- _from_parent_attrs = {
- "project_id": "project_id",
- "issue_iid": "issue_iid",
- "discussion_id": "id",
- }
- _create_attrs = RequiredOptional(required=("body",), optional=("created_at",))
- _update_attrs = RequiredOptional(required=("body",))
-
-
-class ProjectMergeRequestNote(SaveMixin, ObjectDeleteMixin, RESTObject):
- awardemojis: ProjectMergeRequestNoteAwardEmojiManager
-
-
-class ProjectMergeRequestNoteManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/merge_requests/%(mr_iid)s/notes"
- _obj_cls = ProjectMergeRequestNote
- _from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}
- _create_attrs = RequiredOptional(required=("body",))
- _update_attrs = RequiredOptional(required=("body",))
-
-
-class ProjectMergeRequestDiscussionNote(SaveMixin, ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectMergeRequestDiscussionNoteManager(
- GetMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager
-):
- _path = (
- "/projects/%(project_id)s/merge_requests/%(mr_iid)s/"
- "discussions/%(discussion_id)s/notes"
- )
- _obj_cls = ProjectMergeRequestDiscussionNote
- _from_parent_attrs = {
- "project_id": "project_id",
- "mr_iid": "mr_iid",
- "discussion_id": "id",
- }
- _create_attrs = RequiredOptional(required=("body",), optional=("created_at",))
- _update_attrs = RequiredOptional(required=("body",))
-
-
-class ProjectSnippetNote(SaveMixin, ObjectDeleteMixin, RESTObject):
- awardemojis: ProjectMergeRequestNoteAwardEmojiManager
-
-
-class ProjectSnippetNoteManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/snippets/%(snippet_id)s/notes"
- _obj_cls = ProjectSnippetNote
- _from_parent_attrs = {"project_id": "project_id", "snippet_id": "id"}
- _create_attrs = RequiredOptional(required=("body",))
- _update_attrs = RequiredOptional(required=("body",))
-
-
-class ProjectSnippetDiscussionNote(SaveMixin, ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectSnippetDiscussionNoteManager(
- GetMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager
-):
- _path = (
- "/projects/%(project_id)s/snippets/%(snippet_id)s/"
- "discussions/%(discussion_id)s/notes"
- )
- _obj_cls = ProjectSnippetDiscussionNote
- _from_parent_attrs = {
- "project_id": "project_id",
- "snippet_id": "snippet_id",
- "discussion_id": "id",
- }
- _create_attrs = RequiredOptional(required=("body",), optional=("created_at",))
- _update_attrs = RequiredOptional(required=("body",))
diff --git a/gitlab/v4/objects/notification_settings.py b/gitlab/v4/objects/notification_settings.py
deleted file mode 100644
index 3682ed0..0000000
--- a/gitlab/v4/objects/notification_settings.py
+++ /dev/null
@@ -1,57 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import GetWithoutIdMixin, SaveMixin, UpdateMixin
-
-__all__ = [
- "NotificationSettings",
- "NotificationSettingsManager",
- "GroupNotificationSettings",
- "GroupNotificationSettingsManager",
- "ProjectNotificationSettings",
- "ProjectNotificationSettingsManager",
-]
-
-
-class NotificationSettings(SaveMixin, RESTObject):
- _id_attr = None
-
-
-class NotificationSettingsManager(GetWithoutIdMixin, UpdateMixin, RESTManager):
- _path = "/notification_settings"
- _obj_cls = NotificationSettings
-
- _update_attrs = RequiredOptional(
- optional=(
- "level",
- "notification_email",
- "new_note",
- "new_issue",
- "reopen_issue",
- "close_issue",
- "reassign_issue",
- "new_merge_request",
- "reopen_merge_request",
- "close_merge_request",
- "reassign_merge_request",
- "merge_merge_request",
- ),
- )
-
-
-class GroupNotificationSettings(NotificationSettings):
- pass
-
-
-class GroupNotificationSettingsManager(NotificationSettingsManager):
- _path = "/groups/%(group_id)s/notification_settings"
- _obj_cls = GroupNotificationSettings
- _from_parent_attrs = {"group_id": "id"}
-
-
-class ProjectNotificationSettings(NotificationSettings):
- pass
-
-
-class ProjectNotificationSettingsManager(NotificationSettingsManager):
- _path = "/projects/%(project_id)s/notification_settings"
- _obj_cls = ProjectNotificationSettings
- _from_parent_attrs = {"project_id": "id"}
diff --git a/gitlab/v4/objects/packages.py b/gitlab/v4/objects/packages.py
deleted file mode 100644
index e76a5c6..0000000
--- a/gitlab/v4/objects/packages.py
+++ /dev/null
@@ -1,168 +0,0 @@
-from pathlib import Path
-from typing import Any, Callable, Optional, TYPE_CHECKING, Union
-
-import requests
-
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab import utils
-from gitlab.base import RESTManager, RESTObject
-from gitlab.mixins import DeleteMixin, GetMixin, ListMixin, ObjectDeleteMixin
-
-__all__ = [
- "GenericPackage",
- "GenericPackageManager",
- "GroupPackage",
- "GroupPackageManager",
- "ProjectPackage",
- "ProjectPackageManager",
- "ProjectPackageFile",
- "ProjectPackageFileManager",
-]
-
-
-class GenericPackage(RESTObject):
- _id_attr = "package_name"
-
-
-class GenericPackageManager(RESTManager):
- _path = "/projects/%(project_id)s/packages/generic"
- _obj_cls = GenericPackage
- _from_parent_attrs = {"project_id": "id"}
-
- @cli.register_custom_action(
- "GenericPackageManager",
- ("package_name", "package_version", "file_name", "path"),
- )
- @exc.on_http_error(exc.GitlabUploadError)
- def upload(
- self,
- package_name: str,
- package_version: str,
- file_name: str,
- path: Union[str, Path],
- **kwargs,
- ) -> GenericPackage:
- """Upload a file as a generic package.
-
- Args:
- package_name (str): The package name. Must follow generic package
- name regex rules
- package_version (str): The package version. Must follow semantic
- version regex rules
- file_name (str): The name of the file as uploaded in the registry
- path (str): The path to a local file to upload
-
- Raises:
- GitlabConnectionError: If the server cannot be reached
- GitlabUploadError: If the file upload fails
- GitlabUploadError: If ``filepath`` cannot be read
-
- Returns:
- GenericPackage: An object storing the metadata of the uploaded package.
- """
-
- try:
- with open(path, "rb") as f:
- file_data = f.read()
- except OSError:
- raise exc.GitlabUploadError(f"Failed to read package file {path}")
-
- url = f"{self._computed_path}/{package_name}/{package_version}/{file_name}"
- server_data = self.gitlab.http_put(url, post_data=file_data, raw=True, **kwargs)
-
- return self._obj_cls(
- self,
- attrs={
- "package_name": package_name,
- "package_version": package_version,
- "file_name": file_name,
- "path": path,
- "message": server_data["message"],
- },
- )
-
- @cli.register_custom_action(
- "GenericPackageManager",
- ("package_name", "package_version", "file_name"),
- )
- @exc.on_http_error(exc.GitlabGetError)
- def download(
- self,
- package_name: str,
- package_version: str,
- file_name: str,
- streamed: bool = False,
- action: Optional[Callable] = None,
- chunk_size: int = 1024,
- **kwargs: Any,
- ) -> Optional[bytes]:
- """Download a generic package.
-
- Args:
- package_name (str): The package name.
- package_version (str): The package version.
- file_name (str): The name of the file in the registry
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment
- action (callable): Callable responsible of dealing with chunk of
- data
- chunk_size (int): Size of each chunk
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server failed to perform the request
-
- Returns:
- str: The package content if streamed is False, None otherwise
- """
- path = f"{self._computed_path}/{package_name}/{package_version}/{file_name}"
- result = self.gitlab.http_get(path, streamed=streamed, raw=True, **kwargs)
- if TYPE_CHECKING:
- assert isinstance(result, requests.Response)
- return utils.response_content(result, streamed, action, chunk_size)
-
-
-class GroupPackage(RESTObject):
- pass
-
-
-class GroupPackageManager(ListMixin, RESTManager):
- _path = "/groups/%(group_id)s/packages"
- _obj_cls = GroupPackage
- _from_parent_attrs = {"group_id": "id"}
- _list_filters = (
- "exclude_subgroups",
- "order_by",
- "sort",
- "package_type",
- "package_name",
- )
-
-
-class ProjectPackage(ObjectDeleteMixin, RESTObject):
- package_files: "ProjectPackageFileManager"
-
-
-class ProjectPackageManager(ListMixin, GetMixin, DeleteMixin, RESTManager):
- _path = "/projects/%(project_id)s/packages"
- _obj_cls = ProjectPackage
- _from_parent_attrs = {"project_id": "id"}
- _list_filters = (
- "order_by",
- "sort",
- "package_type",
- "package_name",
- )
-
-
-class ProjectPackageFile(RESTObject):
- pass
-
-
-class ProjectPackageFileManager(ListMixin, RESTManager):
- _path = "/projects/%(project_id)s/packages/%(package_id)s/package_files"
- _obj_cls = ProjectPackageFile
- _from_parent_attrs = {"project_id": "project_id", "package_id": "id"}
diff --git a/gitlab/v4/objects/pages.py b/gitlab/v4/objects/pages.py
deleted file mode 100644
index 709d9f0..0000000
--- a/gitlab/v4/objects/pages.py
+++ /dev/null
@@ -1,32 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CRUDMixin, ListMixin, ObjectDeleteMixin, SaveMixin
-
-__all__ = [
- "PagesDomain",
- "PagesDomainManager",
- "ProjectPagesDomain",
- "ProjectPagesDomainManager",
-]
-
-
-class PagesDomain(RESTObject):
- _id_attr = "domain"
-
-
-class PagesDomainManager(ListMixin, RESTManager):
- _path = "/pages/domains"
- _obj_cls = PagesDomain
-
-
-class ProjectPagesDomain(SaveMixin, ObjectDeleteMixin, RESTObject):
- _id_attr = "domain"
-
-
-class ProjectPagesDomainManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/pages/domains"
- _obj_cls = ProjectPagesDomain
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("domain",), optional=("certificate", "key")
- )
- _update_attrs = RequiredOptional(optional=("certificate", "key"))
diff --git a/gitlab/v4/objects/personal_access_tokens.py b/gitlab/v4/objects/personal_access_tokens.py
deleted file mode 100644
index 6cdb305..0000000
--- a/gitlab/v4/objects/personal_access_tokens.py
+++ /dev/null
@@ -1,32 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CreateMixin, DeleteMixin, ListMixin, ObjectDeleteMixin
-
-__all__ = [
- "PersonalAccessToken",
- "PersonalAccessTokenManager",
- "UserPersonalAccessToken",
- "UserPersonalAccessTokenManager",
-]
-
-
-class PersonalAccessToken(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class PersonalAccessTokenManager(DeleteMixin, ListMixin, RESTManager):
- _path = "/personal_access_tokens"
- _obj_cls = PersonalAccessToken
- _list_filters = ("user_id",)
-
-
-class UserPersonalAccessToken(RESTObject):
- pass
-
-
-class UserPersonalAccessTokenManager(CreateMixin, RESTManager):
- _path = "/users/%(user_id)s/personal_access_tokens"
- _obj_cls = UserPersonalAccessToken
- _from_parent_attrs = {"user_id": "id"}
- _create_attrs = RequiredOptional(
- required=("name", "scopes"), optional=("expires_at",)
- )
diff --git a/gitlab/v4/objects/pipelines.py b/gitlab/v4/objects/pipelines.py
deleted file mode 100644
index 2d212a6..0000000
--- a/gitlab/v4/objects/pipelines.py
+++ /dev/null
@@ -1,227 +0,0 @@
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import (
- CreateMixin,
- CRUDMixin,
- DeleteMixin,
- GetWithoutIdMixin,
- ListMixin,
- ObjectDeleteMixin,
- RefreshMixin,
- RetrieveMixin,
- SaveMixin,
- UpdateMixin,
-)
-
-__all__ = [
- "ProjectMergeRequestPipeline",
- "ProjectMergeRequestPipelineManager",
- "ProjectPipeline",
- "ProjectPipelineManager",
- "ProjectPipelineJob",
- "ProjectPipelineJobManager",
- "ProjectPipelineBridge",
- "ProjectPipelineBridgeManager",
- "ProjectPipelineVariable",
- "ProjectPipelineVariableManager",
- "ProjectPipelineScheduleVariable",
- "ProjectPipelineScheduleVariableManager",
- "ProjectPipelineSchedule",
- "ProjectPipelineScheduleManager",
- "ProjectPipelineTestReport",
- "ProjectPipelineTestReportManager",
-]
-
-
-class ProjectMergeRequestPipeline(RESTObject):
- pass
-
-
-class ProjectMergeRequestPipelineManager(CreateMixin, ListMixin, RESTManager):
- _path = "/projects/%(project_id)s/merge_requests/%(mr_iid)s/pipelines"
- _obj_cls = ProjectMergeRequestPipeline
- _from_parent_attrs = {"project_id": "project_id", "mr_iid": "iid"}
-
-
-class ProjectPipeline(RefreshMixin, ObjectDeleteMixin, RESTObject):
- bridges: "ProjectPipelineBridgeManager"
- jobs: "ProjectPipelineJobManager"
- test_report: "ProjectPipelineTestReportManager"
- variables: "ProjectPipelineVariableManager"
-
- @cli.register_custom_action("ProjectPipeline")
- @exc.on_http_error(exc.GitlabPipelineCancelError)
- def cancel(self, **kwargs):
- """Cancel the job.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabPipelineCancelError: If the request failed
- """
- path = "%s/%s/cancel" % (self.manager.path, self.get_id())
- return self.manager.gitlab.http_post(path)
-
- @cli.register_custom_action("ProjectPipeline")
- @exc.on_http_error(exc.GitlabPipelineRetryError)
- def retry(self, **kwargs):
- """Retry the job.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabPipelineRetryError: If the request failed
- """
- path = "%s/%s/retry" % (self.manager.path, self.get_id())
- return self.manager.gitlab.http_post(path)
-
-
-class ProjectPipelineManager(RetrieveMixin, CreateMixin, DeleteMixin, RESTManager):
- _path = "/projects/%(project_id)s/pipelines"
- _obj_cls = ProjectPipeline
- _from_parent_attrs = {"project_id": "id"}
- _list_filters = (
- "scope",
- "status",
- "ref",
- "sha",
- "yaml_errors",
- "name",
- "username",
- "order_by",
- "sort",
- )
- _create_attrs = RequiredOptional(required=("ref",))
-
- def create(self, data, **kwargs):
- """Creates a new object.
-
- Args:
- data (dict): Parameters to send to the server to create the
- resource
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server cannot perform the request
-
- Returns:
- RESTObject: A new instance of the managed object class build with
- the data sent by the server
- """
- path = self.path[:-1] # drop the 's'
- return CreateMixin.create(self, data, path=path, **kwargs)
-
-
-class ProjectPipelineJob(RESTObject):
- pass
-
-
-class ProjectPipelineJobManager(ListMixin, RESTManager):
- _path = "/projects/%(project_id)s/pipelines/%(pipeline_id)s/jobs"
- _obj_cls = ProjectPipelineJob
- _from_parent_attrs = {"project_id": "project_id", "pipeline_id": "id"}
- _list_filters = ("scope", "include_retried")
-
-
-class ProjectPipelineBridge(RESTObject):
- pass
-
-
-class ProjectPipelineBridgeManager(ListMixin, RESTManager):
- _path = "/projects/%(project_id)s/pipelines/%(pipeline_id)s/bridges"
- _obj_cls = ProjectPipelineBridge
- _from_parent_attrs = {"project_id": "project_id", "pipeline_id": "id"}
- _list_filters = ("scope",)
-
-
-class ProjectPipelineVariable(RESTObject):
- _id_attr = "key"
-
-
-class ProjectPipelineVariableManager(ListMixin, RESTManager):
- _path = "/projects/%(project_id)s/pipelines/%(pipeline_id)s/variables"
- _obj_cls = ProjectPipelineVariable
- _from_parent_attrs = {"project_id": "project_id", "pipeline_id": "id"}
-
-
-class ProjectPipelineScheduleVariable(SaveMixin, ObjectDeleteMixin, RESTObject):
- _id_attr = "key"
-
-
-class ProjectPipelineScheduleVariableManager(
- CreateMixin, UpdateMixin, DeleteMixin, RESTManager
-):
- _path = (
- "/projects/%(project_id)s/pipeline_schedules/"
- "%(pipeline_schedule_id)s/variables"
- )
- _obj_cls = ProjectPipelineScheduleVariable
- _from_parent_attrs = {"project_id": "project_id", "pipeline_schedule_id": "id"}
- _create_attrs = RequiredOptional(required=("key", "value"))
- _update_attrs = RequiredOptional(required=("key", "value"))
-
-
-class ProjectPipelineSchedule(SaveMixin, ObjectDeleteMixin, RESTObject):
- variables: ProjectPipelineScheduleVariableManager
-
- @cli.register_custom_action("ProjectPipelineSchedule")
- @exc.on_http_error(exc.GitlabOwnershipError)
- def take_ownership(self, **kwargs):
- """Update the owner of a pipeline schedule.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabOwnershipError: If the request failed
- """
- path = "%s/%s/take_ownership" % (self.manager.path, self.get_id())
- server_data = self.manager.gitlab.http_post(path, **kwargs)
- self._update_attrs(server_data)
-
- @cli.register_custom_action("ProjectPipelineSchedule")
- @exc.on_http_error(exc.GitlabPipelinePlayError)
- def play(self, **kwargs):
- """Trigger a new scheduled pipeline, which runs immediately.
- The next scheduled run of this pipeline is not affected.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabPipelinePlayError: If the request failed
- """
- path = "%s/%s/play" % (self.manager.path, self.get_id())
- server_data = self.manager.gitlab.http_post(path, **kwargs)
- self._update_attrs(server_data)
- return server_data
-
-
-class ProjectPipelineScheduleManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/pipeline_schedules"
- _obj_cls = ProjectPipelineSchedule
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("description", "ref", "cron"), optional=("cron_timezone", "active")
- )
- _update_attrs = RequiredOptional(
- optional=("description", "ref", "cron", "cron_timezone", "active"),
- )
-
-
-class ProjectPipelineTestReport(RESTObject):
- _id_attr = None
-
-
-class ProjectPipelineTestReportManager(GetWithoutIdMixin, RESTManager):
- _path = "/projects/%(project_id)s/pipelines/%(pipeline_id)s/test_report"
- _obj_cls = ProjectPipelineTestReport
- _from_parent_attrs = {"project_id": "project_id", "pipeline_id": "id"}
diff --git a/gitlab/v4/objects/project_access_tokens.py b/gitlab/v4/objects/project_access_tokens.py
deleted file mode 100644
index f59ea85..0000000
--- a/gitlab/v4/objects/project_access_tokens.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from gitlab.base import RESTManager, RESTObject
-from gitlab.mixins import CreateMixin, DeleteMixin, ListMixin, ObjectDeleteMixin
-
-__all__ = [
- "ProjectAccessToken",
- "ProjectAccessTokenManager",
-]
-
-
-class ProjectAccessToken(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectAccessTokenManager(ListMixin, CreateMixin, DeleteMixin, RESTManager):
- _path = "/projects/%(project_id)s/access_tokens"
- _obj_cls = ProjectAccessToken
- _from_parent_attrs = {"project_id": "id"}
diff --git a/gitlab/v4/objects/projects.py b/gitlab/v4/objects/projects.py
deleted file mode 100644
index 551079a..0000000
--- a/gitlab/v4/objects/projects.py
+++ /dev/null
@@ -1,1047 +0,0 @@
-from typing import Any, Callable, cast, Dict, List, Optional, TYPE_CHECKING, Union
-
-import requests
-
-from gitlab import cli, client
-from gitlab import exceptions as exc
-from gitlab import types, utils
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import (
- CreateMixin,
- CRUDMixin,
- ListMixin,
- ObjectDeleteMixin,
- RefreshMixin,
- SaveMixin,
- UpdateMixin,
-)
-
-from .access_requests import ProjectAccessRequestManager # noqa: F401
-from .audit_events import ProjectAuditEventManager # noqa: F401
-from .badges import ProjectBadgeManager # noqa: F401
-from .boards import ProjectBoardManager # noqa: F401
-from .branches import ProjectBranchManager, ProjectProtectedBranchManager # noqa: F401
-from .clusters import ProjectClusterManager # noqa: F401
-from .commits import ProjectCommitManager # noqa: F401
-from .container_registry import ProjectRegistryRepositoryManager # noqa: F401
-from .custom_attributes import ProjectCustomAttributeManager # noqa: F401
-from .deploy_keys import ProjectKeyManager # noqa: F401
-from .deploy_tokens import ProjectDeployTokenManager # noqa: F401
-from .deployments import ProjectDeploymentManager # noqa: F401
-from .environments import ProjectEnvironmentManager # noqa: F401
-from .events import ProjectEventManager # noqa: F401
-from .export_import import ProjectExportManager, ProjectImportManager # noqa: F401
-from .files import ProjectFileManager # noqa: F401
-from .hooks import ProjectHookManager # noqa: F401
-from .issues import ProjectIssueManager # noqa: F401
-from .jobs import ProjectJobManager # noqa: F401
-from .labels import ProjectLabelManager # noqa: F401
-from .members import ProjectMemberAllManager, ProjectMemberManager # noqa: F401
-from .merge_request_approvals import ( # noqa: F401
- ProjectApprovalManager,
- ProjectApprovalRuleManager,
-)
-from .merge_requests import ProjectMergeRequestManager # noqa: F401
-from .milestones import ProjectMilestoneManager # noqa: F401
-from .notes import ProjectNoteManager # noqa: F401
-from .notification_settings import ProjectNotificationSettingsManager # noqa: F401
-from .packages import GenericPackageManager, ProjectPackageManager # noqa: F401
-from .pages import ProjectPagesDomainManager # noqa: F401
-from .pipelines import ( # noqa: F401
- ProjectPipeline,
- ProjectPipelineManager,
- ProjectPipelineScheduleManager,
-)
-from .project_access_tokens import ProjectAccessTokenManager # noqa: F401
-from .push_rules import ProjectPushRulesManager # noqa: F401
-from .releases import ProjectReleaseManager # noqa: F401
-from .repositories import RepositoryMixin
-from .runners import ProjectRunnerManager # noqa: F401
-from .services import ProjectServiceManager # noqa: F401
-from .snippets import ProjectSnippetManager # noqa: F401
-from .statistics import ( # noqa: F401
- ProjectAdditionalStatisticsManager,
- ProjectIssuesStatisticsManager,
-)
-from .tags import ProjectProtectedTagManager, ProjectTagManager # noqa: F401
-from .triggers import ProjectTriggerManager # noqa: F401
-from .users import ProjectUserManager # noqa: F401
-from .variables import ProjectVariableManager # noqa: F401
-from .wikis import ProjectWikiManager # noqa: F401
-
-__all__ = [
- "GroupProject",
- "GroupProjectManager",
- "Project",
- "ProjectManager",
- "ProjectFork",
- "ProjectForkManager",
- "ProjectRemoteMirror",
- "ProjectRemoteMirrorManager",
-]
-
-
-class GroupProject(RESTObject):
- pass
-
-
-class GroupProjectManager(ListMixin, RESTManager):
- _path = "/groups/%(group_id)s/projects"
- _obj_cls = GroupProject
- _from_parent_attrs = {"group_id": "id"}
- _list_filters = (
- "archived",
- "visibility",
- "order_by",
- "sort",
- "search",
- "simple",
- "owned",
- "starred",
- "with_custom_attributes",
- "include_subgroups",
- "with_issues_enabled",
- "with_merge_requests_enabled",
- "with_shared",
- "min_access_level",
- "with_security_reports",
- )
-
-
-class Project(RefreshMixin, SaveMixin, ObjectDeleteMixin, RepositoryMixin, RESTObject):
- _short_print_attr = "path"
-
- access_tokens: ProjectAccessTokenManager
- accessrequests: ProjectAccessRequestManager
- additionalstatistics: ProjectAdditionalStatisticsManager
- approvalrules: ProjectApprovalRuleManager
- approvals: ProjectApprovalManager
- audit_events: ProjectAuditEventManager
- badges: ProjectBadgeManager
- boards: ProjectBoardManager
- branches: ProjectBranchManager
- clusters: ProjectClusterManager
- commits: ProjectCommitManager
- customattributes: ProjectCustomAttributeManager
- deployments: ProjectDeploymentManager
- deploytokens: ProjectDeployTokenManager
- environments: ProjectEnvironmentManager
- events: ProjectEventManager
- exports: ProjectExportManager
- files: ProjectFileManager
- forks: "ProjectForkManager"
- generic_packages: GenericPackageManager
- hooks: ProjectHookManager
- imports: ProjectImportManager
- issues: ProjectIssueManager
- issues_statistics: ProjectIssuesStatisticsManager
- jobs: ProjectJobManager
- keys: ProjectKeyManager
- labels: ProjectLabelManager
- members: ProjectMemberManager
- members_all: ProjectMemberAllManager
- mergerequests: ProjectMergeRequestManager
- milestones: ProjectMilestoneManager
- notes: ProjectNoteManager
- notificationsettings: ProjectNotificationSettingsManager
- packages: ProjectPackageManager
- pagesdomains: ProjectPagesDomainManager
- pipelines: ProjectPipelineManager
- pipelineschedules: ProjectPipelineScheduleManager
- protectedbranches: ProjectProtectedBranchManager
- protectedtags: ProjectProtectedTagManager
- pushrules: ProjectPushRulesManager
- releases: ProjectReleaseManager
- remote_mirrors: "ProjectRemoteMirrorManager"
- repositories: ProjectRegistryRepositoryManager
- runners: ProjectRunnerManager
- services: ProjectServiceManager
- snippets: ProjectSnippetManager
- tags: ProjectTagManager
- triggers: ProjectTriggerManager
- users: ProjectUserManager
- variables: ProjectVariableManager
- wikis: ProjectWikiManager
-
- @cli.register_custom_action("Project", ("forked_from_id",))
- @exc.on_http_error(exc.GitlabCreateError)
- def create_fork_relation(self, forked_from_id: int, **kwargs: Any) -> None:
- """Create a forked from/to relation between existing projects.
-
- Args:
- forked_from_id (int): The ID of the project that was forked from
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the relation could not be created
- """
- path = "/projects/%s/fork/%s" % (self.get_id(), forked_from_id)
- self.manager.gitlab.http_post(path, **kwargs)
-
- @cli.register_custom_action("Project")
- @exc.on_http_error(exc.GitlabDeleteError)
- def delete_fork_relation(self, **kwargs: Any) -> None:
- """Delete a forked relation between existing projects.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabDeleteError: If the server failed to perform the request
- """
- path = "/projects/%s/fork" % self.get_id()
- self.manager.gitlab.http_delete(path, **kwargs)
-
- @cli.register_custom_action("Project")
- @exc.on_http_error(exc.GitlabGetError)
- def languages(self, **kwargs: Any) -> Union[Dict[str, Any], requests.Response]:
- """Get languages used in the project with percentage value.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server failed to perform the request
- """
- path = "/projects/%s/languages" % self.get_id()
- return self.manager.gitlab.http_get(path, **kwargs)
-
- @cli.register_custom_action("Project")
- @exc.on_http_error(exc.GitlabCreateError)
- def star(self, **kwargs: Any) -> None:
- """Star a project.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server failed to perform the request
- """
- path = "/projects/%s/star" % self.get_id()
- server_data = self.manager.gitlab.http_post(path, **kwargs)
- if TYPE_CHECKING:
- assert isinstance(server_data, dict)
- self._update_attrs(server_data)
-
- @cli.register_custom_action("Project")
- @exc.on_http_error(exc.GitlabDeleteError)
- def unstar(self, **kwargs: Any) -> None:
- """Unstar a project.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabDeleteError: If the server failed to perform the request
- """
- path = "/projects/%s/unstar" % self.get_id()
- server_data = self.manager.gitlab.http_post(path, **kwargs)
- if TYPE_CHECKING:
- assert isinstance(server_data, dict)
- self._update_attrs(server_data)
-
- @cli.register_custom_action("Project")
- @exc.on_http_error(exc.GitlabCreateError)
- def archive(self, **kwargs: Any) -> None:
- """Archive a project.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server failed to perform the request
- """
- path = "/projects/%s/archive" % self.get_id()
- server_data = self.manager.gitlab.http_post(path, **kwargs)
- if TYPE_CHECKING:
- assert isinstance(server_data, dict)
- self._update_attrs(server_data)
-
- @cli.register_custom_action("Project")
- @exc.on_http_error(exc.GitlabDeleteError)
- def unarchive(self, **kwargs: Any) -> None:
- """Unarchive a project.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabDeleteError: If the server failed to perform the request
- """
- path = "/projects/%s/unarchive" % self.get_id()
- server_data = self.manager.gitlab.http_post(path, **kwargs)
- if TYPE_CHECKING:
- assert isinstance(server_data, dict)
- self._update_attrs(server_data)
-
- @cli.register_custom_action(
- "Project", ("group_id", "group_access"), ("expires_at",)
- )
- @exc.on_http_error(exc.GitlabCreateError)
- def share(
- self,
- group_id: int,
- group_access: int,
- expires_at: Optional[str] = None,
- **kwargs: Any
- ) -> None:
- """Share the project with a group.
-
- Args:
- group_id (int): ID of the group.
- group_access (int): Access level for the group.
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server failed to perform the request
- """
- path = "/projects/%s/share" % self.get_id()
- data = {
- "group_id": group_id,
- "group_access": group_access,
- "expires_at": expires_at,
- }
- self.manager.gitlab.http_post(path, post_data=data, **kwargs)
-
- @cli.register_custom_action("Project", ("group_id",))
- @exc.on_http_error(exc.GitlabDeleteError)
- def unshare(self, group_id: int, **kwargs: Any) -> None:
- """Delete a shared project link within a group.
-
- Args:
- group_id (int): ID of the group.
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabDeleteError: If the server failed to perform the request
- """
- path = "/projects/%s/share/%s" % (self.get_id(), group_id)
- self.manager.gitlab.http_delete(path, **kwargs)
-
- # variables not supported in CLI
- @cli.register_custom_action("Project", ("ref", "token"))
- @exc.on_http_error(exc.GitlabCreateError)
- def trigger_pipeline(
- self,
- ref: str,
- token: str,
- variables: Optional[Dict[str, Any]] = None,
- **kwargs: Any
- ) -> ProjectPipeline:
- """Trigger a CI build.
-
- See https://gitlab.com/help/ci/triggers/README.md#trigger-a-build
-
- Args:
- ref (str): Commit to build; can be a branch name or a tag
- token (str): The trigger token
- variables (dict): Variables passed to the build script
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server failed to perform the request
- """
- variables = variables or {}
- path = "/projects/%s/trigger/pipeline" % self.get_id()
- post_data = {"ref": ref, "token": token, "variables": variables}
- attrs = self.manager.gitlab.http_post(path, post_data=post_data, **kwargs)
- if TYPE_CHECKING:
- assert isinstance(attrs, dict)
- return ProjectPipeline(self.pipelines, attrs)
-
- @cli.register_custom_action("Project")
- @exc.on_http_error(exc.GitlabHousekeepingError)
- def housekeeping(self, **kwargs: Any) -> None:
- """Start the housekeeping task.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabHousekeepingError: If the server failed to perform the
- request
- """
- path = "/projects/%s/housekeeping" % self.get_id()
- self.manager.gitlab.http_post(path, **kwargs)
-
- # see #56 - add file attachment features
- @cli.register_custom_action("Project", ("filename", "filepath"))
- @exc.on_http_error(exc.GitlabUploadError)
- def upload(
- self,
- filename: str,
- filedata: Optional[bytes] = None,
- filepath: Optional[str] = None,
- **kwargs: Any
- ) -> Dict[str, Any]:
- """Upload the specified file into the project.
-
- .. note::
-
- Either ``filedata`` or ``filepath`` *MUST* be specified.
-
- Args:
- filename (str): The name of the file being uploaded
- filedata (bytes): The raw data of the file being uploaded
- filepath (str): The path to a local file to upload (optional)
-
- Raises:
- GitlabConnectionError: If the server cannot be reached
- GitlabUploadError: If the file upload fails
- GitlabUploadError: If ``filedata`` and ``filepath`` are not
- specified
- GitlabUploadError: If both ``filedata`` and ``filepath`` are
- specified
-
- Returns:
- dict: A ``dict`` with the keys:
- * ``alt`` - The alternate text for the upload
- * ``url`` - The direct url to the uploaded file
- * ``markdown`` - Markdown for the uploaded file
- """
- if filepath is None and filedata is None:
- raise exc.GitlabUploadError("No file contents or path specified")
-
- if filedata is not None and filepath is not None:
- raise exc.GitlabUploadError("File contents and file path specified")
-
- if filepath is not None:
- with open(filepath, "rb") as f:
- filedata = f.read()
-
- url = "/projects/%(id)s/uploads" % {"id": self.id}
- file_info = {"file": (filename, filedata)}
- data = self.manager.gitlab.http_post(url, files=file_info)
-
- if TYPE_CHECKING:
- assert isinstance(data, dict)
- return {"alt": data["alt"], "url": data["url"], "markdown": data["markdown"]}
-
- @cli.register_custom_action("Project", optional=("wiki",))
- @exc.on_http_error(exc.GitlabGetError)
- def snapshot(
- self,
- wiki: bool = False,
- streamed: bool = False,
- action: Optional[Callable] = None,
- chunk_size: int = 1024,
- **kwargs: Any
- ) -> Optional[bytes]:
- """Return a snapshot of the repository.
-
- Args:
- wiki (bool): If True return the wiki repository
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment.
- action (callable): Callable responsible of dealing with chunk of
- data
- chunk_size (int): Size of each chunk
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the content could not be retrieved
-
- Returns:
- str: The uncompressed tar archive of the repository
- """
- path = "/projects/%s/snapshot" % self.get_id()
- result = self.manager.gitlab.http_get(
- path, streamed=streamed, raw=True, **kwargs
- )
- if TYPE_CHECKING:
- assert isinstance(result, requests.Response)
- return utils.response_content(result, streamed, action, chunk_size)
-
- @cli.register_custom_action("Project", ("scope", "search"))
- @exc.on_http_error(exc.GitlabSearchError)
- def search(
- self, scope: str, search: str, **kwargs: Any
- ) -> Union[client.GitlabList, List[Dict[str, Any]]]:
- """Search the project resources matching the provided string.'
-
- Args:
- scope (str): Scope of the search
- search (str): Search string
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabSearchError: If the server failed to perform the request
-
- Returns:
- GitlabList: A list of dicts describing the resources found.
- """
- data = {"scope": scope, "search": search}
- path = "/projects/%s/search" % self.get_id()
- return self.manager.gitlab.http_list(path, query_data=data, **kwargs)
-
- @cli.register_custom_action("Project")
- @exc.on_http_error(exc.GitlabCreateError)
- def mirror_pull(self, **kwargs: Any) -> None:
- """Start the pull mirroring process for the project.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server failed to perform the request
- """
- path = "/projects/%s/mirror/pull" % self.get_id()
- self.manager.gitlab.http_post(path, **kwargs)
-
- @cli.register_custom_action("Project", ("to_namespace",))
- @exc.on_http_error(exc.GitlabTransferProjectError)
- def transfer_project(self, to_namespace: str, **kwargs: Any) -> None:
- """Transfer a project to the given namespace ID
-
- Args:
- to_namespace (str): ID or path of the namespace to transfer the
- project to
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabTransferProjectError: If the project could not be transferred
- """
- path = "/projects/%s/transfer" % (self.id,)
- self.manager.gitlab.http_put(
- path, post_data={"namespace": to_namespace}, **kwargs
- )
-
- @cli.register_custom_action("Project", ("ref_name", "job"), ("job_token",))
- @exc.on_http_error(exc.GitlabGetError)
- def artifacts(
- self,
- ref_name: str,
- job: str,
- streamed: bool = False,
- action: Optional[Callable] = None,
- chunk_size: int = 1024,
- **kwargs: Any
- ) -> Optional[bytes]:
- """Get the job artifacts archive from a specific tag or branch.
-
- Args:
- ref_name (str): Branch or tag name in repository. HEAD or SHA references
- are not supported.
- artifact_path (str): Path to a file inside the artifacts archive.
- job (str): The name of the job.
- job_token (str): Job token for multi-project pipeline triggers.
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment
- action (callable): Callable responsible of dealing with chunk of
- data
- chunk_size (int): Size of each chunk
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the artifacts could not be retrieved
-
- Returns:
- str: The artifacts if `streamed` is False, None otherwise.
- """
- path = "/projects/%s/jobs/artifacts/%s/download" % (self.get_id(), ref_name)
- result = self.manager.gitlab.http_get(
- path, job=job, streamed=streamed, raw=True, **kwargs
- )
- if TYPE_CHECKING:
- assert isinstance(result, requests.Response)
- return utils.response_content(result, streamed, action, chunk_size)
-
- @cli.register_custom_action("Project", ("ref_name", "artifact_path", "job"))
- @exc.on_http_error(exc.GitlabGetError)
- def artifact(
- self,
- ref_name: str,
- artifact_path: str,
- job: str,
- streamed: bool = False,
- action: Optional[Callable] = None,
- chunk_size: int = 1024,
- **kwargs: Any
- ) -> Optional[bytes]:
- """Download a single artifact file from a specific tag or branch from within the job’s artifacts archive.
-
- Args:
- ref_name (str): Branch or tag name in repository. HEAD or SHA references are not supported.
- artifact_path (str): Path to a file inside the artifacts archive.
- job (str): The name of the job.
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment
- action (callable): Callable responsible of dealing with chunk of
- data
- chunk_size (int): Size of each chunk
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the artifacts could not be retrieved
-
- Returns:
- str: The artifacts if `streamed` is False, None otherwise.
- """
-
- path = "/projects/%s/jobs/artifacts/%s/raw/%s?job=%s" % (
- self.get_id(),
- ref_name,
- artifact_path,
- job,
- )
- result = self.manager.gitlab.http_get(
- path, streamed=streamed, raw=True, **kwargs
- )
- if TYPE_CHECKING:
- assert isinstance(result, requests.Response)
- return utils.response_content(result, streamed, action, chunk_size)
-
-
-class ProjectManager(CRUDMixin, RESTManager):
- _path = "/projects"
- _obj_cls = Project
- # Please keep these _create_attrs in same order as they are at:
- # https://docs.gitlab.com/ee/api/projects.html#create-project
- _create_attrs = RequiredOptional(
- optional=(
- "name",
- "path",
- "allow_merge_on_skipped_pipeline",
- "analytics_access_level",
- "approvals_before_merge",
- "auto_cancel_pending_pipelines",
- "auto_devops_deploy_strategy",
- "auto_devops_enabled",
- "autoclose_referenced_issues",
- "avatar",
- "build_coverage_regex",
- "build_git_strategy",
- "build_timeout",
- "builds_access_level",
- "ci_config_path",
- "container_expiration_policy_attributes",
- "container_registry_enabled",
- "default_branch",
- "description",
- "emails_disabled",
- "external_authorization_classification_label",
- "forking_access_level",
- "group_with_project_templates_id",
- "import_url",
- "initialize_with_readme",
- "issues_access_level",
- "issues_enabled",
- "jobs_enabled",
- "lfs_enabled",
- "merge_method",
- "merge_requests_access_level",
- "merge_requests_enabled",
- "mirror_trigger_builds",
- "mirror",
- "namespace_id",
- "operations_access_level",
- "only_allow_merge_if_all_discussions_are_resolved",
- "only_allow_merge_if_pipeline_succeeds",
- "packages_enabled",
- "pages_access_level",
- "requirements_access_level",
- "printing_merge_request_link_enabled",
- "public_builds",
- "remove_source_branch_after_merge",
- "repository_access_level",
- "repository_storage",
- "request_access_enabled",
- "resolve_outdated_diff_discussions",
- "shared_runners_enabled",
- "show_default_award_emojis",
- "snippets_access_level",
- "snippets_enabled",
- "tag_list",
- "template_name",
- "template_project_id",
- "use_custom_template",
- "visibility",
- "wiki_access_level",
- "wiki_enabled",
- ),
- )
- # Please keep these _update_attrs in same order as they are at:
- # https://docs.gitlab.com/ee/api/projects.html#edit-project
- _update_attrs = RequiredOptional(
- optional=(
- "allow_merge_on_skipped_pipeline",
- "analytics_access_level",
- "approvals_before_merge",
- "auto_cancel_pending_pipelines",
- "auto_devops_deploy_strategy",
- "auto_devops_enabled",
- "autoclose_referenced_issues",
- "avatar",
- "build_coverage_regex",
- "build_git_strategy",
- "build_timeout",
- "builds_access_level",
- "ci_config_path",
- "ci_default_git_depth",
- "ci_forward_deployment_enabled",
- "container_expiration_policy_attributes",
- "container_registry_enabled",
- "default_branch",
- "description",
- "emails_disabled",
- "external_authorization_classification_label",
- "forking_access_level",
- "import_url",
- "issues_access_level",
- "issues_enabled",
- "jobs_enabled",
- "lfs_enabled",
- "merge_method",
- "merge_requests_access_level",
- "merge_requests_enabled",
- "mirror_overwrites_diverged_branches",
- "mirror_trigger_builds",
- "mirror_user_id",
- "mirror",
- "name",
- "operations_access_level",
- "only_allow_merge_if_all_discussions_are_resolved",
- "only_allow_merge_if_pipeline_succeeds",
- "only_mirror_protected_branches",
- "packages_enabled",
- "pages_access_level",
- "requirements_access_level",
- "restrict_user_defined_variables",
- "path",
- "public_builds",
- "remove_source_branch_after_merge",
- "repository_access_level",
- "repository_storage",
- "request_access_enabled",
- "resolve_outdated_diff_discussions",
- "service_desk_enabled",
- "shared_runners_enabled",
- "show_default_award_emojis",
- "snippets_access_level",
- "snippets_enabled",
- "suggestion_commit_message",
- "tag_list",
- "visibility",
- "wiki_access_level",
- "wiki_enabled",
- "issues_template",
- "merge_requests_template",
- ),
- )
- _list_filters = (
- "archived",
- "id_after",
- "id_before",
- "last_activity_after",
- "last_activity_before",
- "membership",
- "min_access_level",
- "order_by",
- "owned",
- "repository_checksum_failed",
- "repository_storage",
- "search_namespaces",
- "search",
- "simple",
- "sort",
- "starred",
- "statistics",
- "topic",
- "visibility",
- "wiki_checksum_failed",
- "with_custom_attributes",
- "with_issues_enabled",
- "with_merge_requests_enabled",
- "with_programming_language",
- )
- _types = {"avatar": types.ImageAttribute, "topic": types.ListAttribute}
-
- def get(self, id: Union[str, int], lazy: bool = False, **kwargs: Any) -> Project:
- return cast(Project, super().get(id=id, lazy=lazy, **kwargs))
-
- def import_project(
- self,
- file: str,
- path: str,
- name: Optional[str] = None,
- namespace: Optional[str] = None,
- overwrite: bool = False,
- override_params: Optional[Dict[str, Any]] = None,
- **kwargs: Any
- ) -> Union[Dict[str, Any], requests.Response]:
- """Import a project from an archive file.
-
- Args:
- file: Data or file object containing the project
- path (str): Name and path for the new project
- namespace (str): The ID or path of the namespace that the project
- will be imported to
- overwrite (bool): If True overwrite an existing project with the
- same path
- override_params (dict): Set the specific settings for the project
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the server failed to perform the request
-
- Returns:
- dict: A representation of the import status.
- """
- files = {"file": ("file.tar.gz", file, "application/octet-stream")}
- data = {"path": path, "overwrite": str(overwrite)}
- if override_params:
- for k, v in override_params.items():
- data["override_params[%s]" % k] = v
- if name is not None:
- data["name"] = name
- if namespace:
- data["namespace"] = namespace
- return self.gitlab.http_post(
- "/projects/import", post_data=data, files=files, **kwargs
- )
-
- def import_bitbucket_server(
- self,
- bitbucket_server_url: str,
- bitbucket_server_username: str,
- personal_access_token: str,
- bitbucket_server_project: str,
- bitbucket_server_repo: str,
- new_name: Optional[str] = None,
- target_namespace: Optional[str] = None,
- **kwargs: Any
- ) -> Union[Dict[str, Any], requests.Response]:
- """Import a project from BitBucket Server to Gitlab (schedule the import)
-
- This method will return when an import operation has been safely queued,
- or an error has occurred. After triggering an import, check the
- ``import_status`` of the newly created project to detect when the import
- operation has completed.
-
- .. note::
- This request may take longer than most other API requests.
- So this method will specify a 60 second default timeout if none is specified.
- A timeout can be specified via kwargs to override this functionality.
-
- Args:
- bitbucket_server_url (str): Bitbucket Server URL
- bitbucket_server_username (str): Bitbucket Server Username
- personal_access_token (str): Bitbucket Server personal access
- token/password
- bitbucket_server_project (str): Bitbucket Project Key
- bitbucket_server_repo (str): Bitbucket Repository Name
- new_name (str): New repository name (Optional)
- target_namespace (str): Namespace to import repository into.
- Supports subgroups like /namespace/subgroup (Optional)
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the server failed to perform the request
-
- Returns:
- dict: A representation of the import status.
-
- Example:
-
- .. code-block:: python
-
- gl = gitlab.Gitlab_from_config()
- print("Triggering import")
- result = gl.projects.import_bitbucket_server(
- bitbucket_server_url="https://some.server.url",
- bitbucket_server_username="some_bitbucket_user",
- personal_access_token="my_password_or_access_token",
- bitbucket_server_project="my_project",
- bitbucket_server_repo="my_repo",
- new_name="gl_project_name",
- target_namespace="gl_project_path"
- )
- project = gl.projects.get(ret['id'])
- print("Waiting for import to complete")
- while project.import_status == u'started':
- time.sleep(1.0)
- project = gl.projects.get(project.id)
- print("BitBucket import complete")
-
- """
- data = {
- "bitbucket_server_url": bitbucket_server_url,
- "bitbucket_server_username": bitbucket_server_username,
- "personal_access_token": personal_access_token,
- "bitbucket_server_project": bitbucket_server_project,
- "bitbucket_server_repo": bitbucket_server_repo,
- }
- if new_name:
- data["new_name"] = new_name
- if target_namespace:
- data["target_namespace"] = target_namespace
- if (
- "timeout" not in kwargs
- or self.gitlab.timeout is None
- or self.gitlab.timeout < 60.0
- ):
- # Ensure that this HTTP request has a longer-than-usual default timeout
- # The base gitlab object tends to have a default that is <10 seconds,
- # and this is too short for this API command, typically.
- # On the order of 24 seconds has been measured on a typical gitlab instance.
- kwargs["timeout"] = 60.0
- result = self.gitlab.http_post(
- "/import/bitbucket_server", post_data=data, **kwargs
- )
- return result
-
- def import_github(
- self,
- personal_access_token: str,
- repo_id: int,
- target_namespace: str,
- new_name: Optional[str] = None,
- **kwargs: Any
- ) -> Union[Dict[str, Any], requests.Response]:
- """Import a project from Github to Gitlab (schedule the import)
-
- This method will return when an import operation has been safely queued,
- or an error has occurred. After triggering an import, check the
- ``import_status`` of the newly created project to detect when the import
- operation has completed.
-
- .. note::
- This request may take longer than most other API requests.
- So this method will specify a 60 second default timeout if none is specified.
- A timeout can be specified via kwargs to override this functionality.
-
- Args:
- personal_access_token (str): GitHub personal access token
- repo_id (int): Github repository ID
- target_namespace (str): Namespace to import repo into
- new_name (str): New repo name (Optional)
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the server failed to perform the request
-
- Returns:
- dict: A representation of the import status.
-
- Example:
-
- .. code-block:: python
-
- gl = gitlab.Gitlab_from_config()
- print("Triggering import")
- result = gl.projects.import_github(ACCESS_TOKEN,
- 123456,
- "my-group/my-subgroup")
- project = gl.projects.get(ret['id'])
- print("Waiting for import to complete")
- while project.import_status == u'started':
- time.sleep(1.0)
- project = gl.projects.get(project.id)
- print("Github import complete")
-
- """
- data = {
- "personal_access_token": personal_access_token,
- "repo_id": repo_id,
- "target_namespace": target_namespace,
- }
- if new_name:
- data["new_name"] = new_name
- if (
- "timeout" not in kwargs
- or self.gitlab.timeout is None
- or self.gitlab.timeout < 60.0
- ):
- # Ensure that this HTTP request has a longer-than-usual default timeout
- # The base gitlab object tends to have a default that is <10 seconds,
- # and this is too short for this API command, typically.
- # On the order of 24 seconds has been measured on a typical gitlab instance.
- kwargs["timeout"] = 60.0
- result = self.gitlab.http_post("/import/github", post_data=data, **kwargs)
- return result
-
-
-class ProjectFork(RESTObject):
- pass
-
-
-class ProjectForkManager(CreateMixin, ListMixin, RESTManager):
- _path = "/projects/%(project_id)s/forks"
- _obj_cls = ProjectFork
- _from_parent_attrs = {"project_id": "id"}
- _list_filters = (
- "archived",
- "visibility",
- "order_by",
- "sort",
- "search",
- "simple",
- "owned",
- "membership",
- "starred",
- "statistics",
- "with_custom_attributes",
- "with_issues_enabled",
- "with_merge_requests_enabled",
- )
- _create_attrs = RequiredOptional(optional=("namespace",))
-
- def create(
- self, data: Optional[Dict[str, Any]] = None, **kwargs: Any
- ) -> ProjectFork:
- """Creates a new object.
-
- Args:
- data (dict): Parameters to send to the server to create the
- resource
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabCreateError: If the server cannot perform the request
-
- Returns:
- RESTObject: A new instance of the managed object class build with
- the data sent by the server
- """
- if TYPE_CHECKING:
- assert self.path is not None
- path = self.path[:-1] # drop the 's'
- return cast(ProjectFork, CreateMixin.create(self, data, path=path, **kwargs))
-
-
-class ProjectRemoteMirror(SaveMixin, RESTObject):
- pass
-
-
-class ProjectRemoteMirrorManager(ListMixin, CreateMixin, UpdateMixin, RESTManager):
- _path = "/projects/%(project_id)s/remote_mirrors"
- _obj_cls = ProjectRemoteMirror
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("url",), optional=("enabled", "only_protected_branches")
- )
- _update_attrs = RequiredOptional(optional=("enabled", "only_protected_branches"))
diff --git a/gitlab/v4/objects/push_rules.py b/gitlab/v4/objects/push_rules.py
deleted file mode 100644
index ee20f96..0000000
--- a/gitlab/v4/objects/push_rules.py
+++ /dev/null
@@ -1,50 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import (
- CreateMixin,
- DeleteMixin,
- GetWithoutIdMixin,
- ObjectDeleteMixin,
- SaveMixin,
- UpdateMixin,
-)
-
-__all__ = [
- "ProjectPushRules",
- "ProjectPushRulesManager",
-]
-
-
-class ProjectPushRules(SaveMixin, ObjectDeleteMixin, RESTObject):
- _id_attr = None
-
-
-class ProjectPushRulesManager(
- GetWithoutIdMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTManager
-):
- _path = "/projects/%(project_id)s/push_rule"
- _obj_cls = ProjectPushRules
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- optional=(
- "deny_delete_tag",
- "member_check",
- "prevent_secrets",
- "commit_message_regex",
- "branch_name_regex",
- "author_email_regex",
- "file_name_regex",
- "max_file_size",
- ),
- )
- _update_attrs = RequiredOptional(
- optional=(
- "deny_delete_tag",
- "member_check",
- "prevent_secrets",
- "commit_message_regex",
- "branch_name_regex",
- "author_email_regex",
- "file_name_regex",
- "max_file_size",
- ),
- )
diff --git a/gitlab/v4/objects/releases.py b/gitlab/v4/objects/releases.py
deleted file mode 100644
index 2af3248..0000000
--- a/gitlab/v4/objects/releases.py
+++ /dev/null
@@ -1,41 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CRUDMixin, ObjectDeleteMixin, SaveMixin
-
-__all__ = [
- "ProjectRelease",
- "ProjectReleaseManager",
- "ProjectReleaseLink",
- "ProjectReleaseLinkManager",
-]
-
-
-class ProjectRelease(SaveMixin, RESTObject):
- _id_attr = "tag_name"
-
- links: "ProjectReleaseLinkManager"
-
-
-class ProjectReleaseManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/releases"
- _obj_cls = ProjectRelease
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("tag_name", "description"), optional=("name", "ref", "assets")
- )
- _update_attrs = RequiredOptional(
- optional=("name", "description", "milestones", "released_at")
- )
-
-
-class ProjectReleaseLink(ObjectDeleteMixin, SaveMixin, RESTObject):
- pass
-
-
-class ProjectReleaseLinkManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/releases/%(tag_name)s/assets/links"
- _obj_cls = ProjectReleaseLink
- _from_parent_attrs = {"project_id": "project_id", "tag_name": "tag_name"}
- _create_attrs = RequiredOptional(
- required=("name", "url"), optional=("filepath", "link_type")
- )
- _update_attrs = RequiredOptional(optional=("name", "url", "filepath", "link_type"))
diff --git a/gitlab/v4/objects/repositories.py b/gitlab/v4/objects/repositories.py
deleted file mode 100644
index de5f0d2..0000000
--- a/gitlab/v4/objects/repositories.py
+++ /dev/null
@@ -1,207 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ee/api/repositories.html
-
-Currently this module only contains repository-related methods for projects.
-"""
-
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab import utils
-
-
-class RepositoryMixin:
- @cli.register_custom_action("Project", ("submodule", "branch", "commit_sha"))
- @exc.on_http_error(exc.GitlabUpdateError)
- def update_submodule(self, submodule, branch, commit_sha, **kwargs):
- """Update a project submodule
-
- Args:
- submodule (str): Full path to the submodule
- branch (str): Name of the branch to commit into
- commit_sha (str): Full commit SHA to update the submodule to
- commit_message (str): Commit message. If no message is provided, a default one will be set (optional)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabPutError: If the submodule could not be updated
- """
-
- submodule = submodule.replace("/", "%2F") # .replace('.', '%2E')
- path = "/projects/%s/repository/submodules/%s" % (self.get_id(), submodule)
- data = {"branch": branch, "commit_sha": commit_sha}
- if "commit_message" in kwargs:
- data["commit_message"] = kwargs["commit_message"]
- return self.manager.gitlab.http_put(path, post_data=data)
-
- @cli.register_custom_action("Project", tuple(), ("path", "ref", "recursive"))
- @exc.on_http_error(exc.GitlabGetError)
- def repository_tree(self, path="", ref="", recursive=False, **kwargs):
- """Return a list of files in the repository.
-
- Args:
- path (str): Path of the top folder (/ by default)
- ref (str): Reference to a commit or branch
- recursive (bool): Whether to get the tree recursively
- all (bool): If True, return all the items, without pagination
- per_page (int): Number of items to retrieve per request
- page (int): ID of the page to return (starts with page 1)
- as_list (bool): If set to False and no pagination option is
- defined, return a generator instead of a list
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server failed to perform the request
-
- Returns:
- list: The representation of the tree
- """
- gl_path = "/projects/%s/repository/tree" % self.get_id()
- query_data = {"recursive": recursive}
- if path:
- query_data["path"] = path
- if ref:
- query_data["ref"] = ref
- return self.manager.gitlab.http_list(gl_path, query_data=query_data, **kwargs)
-
- @cli.register_custom_action("Project", ("sha",))
- @exc.on_http_error(exc.GitlabGetError)
- def repository_blob(self, sha, **kwargs):
- """Return a file by blob SHA.
-
- Args:
- sha(str): ID of the blob
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server failed to perform the request
-
- Returns:
- dict: The blob content and metadata
- """
-
- path = "/projects/%s/repository/blobs/%s" % (self.get_id(), sha)
- return self.manager.gitlab.http_get(path, **kwargs)
-
- @cli.register_custom_action("Project", ("sha",))
- @exc.on_http_error(exc.GitlabGetError)
- def repository_raw_blob(
- self, sha, streamed=False, action=None, chunk_size=1024, **kwargs
- ):
- """Return the raw file contents for a blob.
-
- Args:
- sha(str): ID of the blob
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment
- action (callable): Callable responsible of dealing with chunk of
- data
- chunk_size (int): Size of each chunk
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server failed to perform the request
-
- Returns:
- str: The blob content if streamed is False, None otherwise
- """
- path = "/projects/%s/repository/blobs/%s/raw" % (self.get_id(), sha)
- result = self.manager.gitlab.http_get(
- path, streamed=streamed, raw=True, **kwargs
- )
- return utils.response_content(result, streamed, action, chunk_size)
-
- @cli.register_custom_action("Project", ("from_", "to"))
- @exc.on_http_error(exc.GitlabGetError)
- def repository_compare(self, from_, to, **kwargs):
- """Return a diff between two branches/commits.
-
- Args:
- from_(str): Source branch/SHA
- to(str): Destination branch/SHA
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server failed to perform the request
-
- Returns:
- str: The diff
- """
- path = "/projects/%s/repository/compare" % self.get_id()
- query_data = {"from": from_, "to": to}
- return self.manager.gitlab.http_get(path, query_data=query_data, **kwargs)
-
- @cli.register_custom_action("Project")
- @exc.on_http_error(exc.GitlabGetError)
- def repository_contributors(self, **kwargs):
- """Return a list of contributors for the project.
-
- Args:
- all (bool): If True, return all the items, without pagination
- per_page (int): Number of items to retrieve per request
- page (int): ID of the page to return (starts with page 1)
- as_list (bool): If set to False and no pagination option is
- defined, return a generator instead of a list
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server failed to perform the request
-
- Returns:
- list: The contributors
- """
- path = "/projects/%s/repository/contributors" % self.get_id()
- return self.manager.gitlab.http_list(path, **kwargs)
-
- @cli.register_custom_action("Project", tuple(), ("sha",))
- @exc.on_http_error(exc.GitlabListError)
- def repository_archive(
- self, sha=None, streamed=False, action=None, chunk_size=1024, **kwargs
- ):
- """Return a tarball of the repository.
-
- Args:
- sha (str): ID of the commit (default branch by default)
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment
- action (callable): Callable responsible of dealing with chunk of
- data
- chunk_size (int): Size of each chunk
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the server failed to perform the request
-
- Returns:
- bytes: The binary data of the archive
- """
- path = "/projects/%s/repository/archive" % self.get_id()
- query_data = {}
- if sha:
- query_data["sha"] = sha
- result = self.manager.gitlab.http_get(
- path, query_data=query_data, raw=True, streamed=streamed, **kwargs
- )
- return utils.response_content(result, streamed, action, chunk_size)
-
- @cli.register_custom_action("Project")
- @exc.on_http_error(exc.GitlabDeleteError)
- def delete_merged_branches(self, **kwargs):
- """Delete merged branches.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabDeleteError: If the server failed to perform the request
- """
- path = "/projects/%s/repository/merged_branches" % self.get_id()
- self.manager.gitlab.http_delete(path, **kwargs)
diff --git a/gitlab/v4/objects/runners.py b/gitlab/v4/objects/runners.py
deleted file mode 100644
index a32dc84..0000000
--- a/gitlab/v4/objects/runners.py
+++ /dev/null
@@ -1,140 +0,0 @@
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab import types
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import (
- CRUDMixin,
- ListMixin,
- NoUpdateMixin,
- ObjectDeleteMixin,
- SaveMixin,
-)
-
-__all__ = [
- "RunnerJob",
- "RunnerJobManager",
- "Runner",
- "RunnerManager",
- "GroupRunner",
- "GroupRunnerManager",
- "ProjectRunner",
- "ProjectRunnerManager",
-]
-
-
-class RunnerJob(RESTObject):
- pass
-
-
-class RunnerJobManager(ListMixin, RESTManager):
- _path = "/runners/%(runner_id)s/jobs"
- _obj_cls = RunnerJob
- _from_parent_attrs = {"runner_id": "id"}
- _list_filters = ("status",)
-
-
-class Runner(SaveMixin, ObjectDeleteMixin, RESTObject):
- jobs: RunnerJobManager
-
-
-class RunnerManager(CRUDMixin, RESTManager):
- _path = "/runners"
- _obj_cls = Runner
- _create_attrs = RequiredOptional(
- required=("token",),
- optional=(
- "description",
- "info",
- "active",
- "locked",
- "run_untagged",
- "tag_list",
- "access_level",
- "maximum_timeout",
- ),
- )
- _update_attrs = RequiredOptional(
- optional=(
- "description",
- "active",
- "tag_list",
- "run_untagged",
- "locked",
- "access_level",
- "maximum_timeout",
- ),
- )
- _list_filters = ("scope", "tag_list")
- _types = {"tag_list": types.ListAttribute}
-
- @cli.register_custom_action("RunnerManager", tuple(), ("scope",))
- @exc.on_http_error(exc.GitlabListError)
- def all(self, scope=None, **kwargs):
- """List all the runners.
-
- Args:
- scope (str): The scope of runners to show, one of: specific,
- shared, active, paused, online
- all (bool): If True, return all the items, without pagination
- per_page (int): Number of items to retrieve per request
- page (int): ID of the page to return (starts with page 1)
- as_list (bool): If set to False and no pagination option is
- defined, return a generator instead of a list
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the server failed to perform the request
-
- Returns:
- list(Runner): a list of runners matching the scope.
- """
- path = "/runners/all"
- query_data = {}
- if scope is not None:
- query_data["scope"] = scope
- obj = self.gitlab.http_list(path, query_data, **kwargs)
- return [self._obj_cls(self, item) for item in obj]
-
- @cli.register_custom_action("RunnerManager", ("token",))
- @exc.on_http_error(exc.GitlabVerifyError)
- def verify(self, token, **kwargs):
- """Validates authentication credentials for a registered Runner.
-
- Args:
- token (str): The runner's authentication token
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabVerifyError: If the server failed to verify the token
- """
- path = "/runners/verify"
- post_data = {"token": token}
- self.gitlab.http_post(path, post_data=post_data, **kwargs)
-
-
-class GroupRunner(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class GroupRunnerManager(NoUpdateMixin, RESTManager):
- _path = "/groups/%(group_id)s/runners"
- _obj_cls = GroupRunner
- _from_parent_attrs = {"group_id": "id"}
- _create_attrs = RequiredOptional(required=("runner_id",))
- _list_filters = ("scope", "tag_list")
- _types = {"tag_list": types.ListAttribute}
-
-
-class ProjectRunner(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectRunnerManager(NoUpdateMixin, RESTManager):
- _path = "/projects/%(project_id)s/runners"
- _obj_cls = ProjectRunner
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(required=("runner_id",))
- _list_filters = ("scope", "tag_list")
- _types = {"tag_list": types.ListAttribute}
diff --git a/gitlab/v4/objects/services.py b/gitlab/v4/objects/services.py
deleted file mode 100644
index 6aedc39..0000000
--- a/gitlab/v4/objects/services.py
+++ /dev/null
@@ -1,303 +0,0 @@
-from gitlab import cli
-from gitlab.base import RESTManager, RESTObject
-from gitlab.mixins import (
- DeleteMixin,
- GetMixin,
- ListMixin,
- ObjectDeleteMixin,
- SaveMixin,
- UpdateMixin,
-)
-
-__all__ = [
- "ProjectService",
- "ProjectServiceManager",
-]
-
-
-class ProjectService(SaveMixin, ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectServiceManager(GetMixin, UpdateMixin, DeleteMixin, ListMixin, RESTManager):
- _path = "/projects/%(project_id)s/services"
- _from_parent_attrs = {"project_id": "id"}
- _obj_cls = ProjectService
-
- _service_attrs = {
- "asana": (("api_key",), ("restrict_to_branch", "push_events")),
- "assembla": (("token",), ("subdomain", "push_events")),
- "bamboo": (
- ("bamboo_url", "build_key", "username", "password"),
- ("push_events",),
- ),
- "bugzilla": (
- ("new_issue_url", "issues_url", "project_url"),
- ("description", "title", "push_events"),
- ),
- "buildkite": (
- ("token", "project_url"),
- ("enable_ssl_verification", "push_events"),
- ),
- "campfire": (("token",), ("subdomain", "room", "push_events")),
- "circuit": (
- ("webhook",),
- (
- "notify_only_broken_pipelines",
- "branches_to_be_notified",
- "push_events",
- "issues_events",
- "confidential_issues_events",
- "merge_requests_events",
- "tag_push_events",
- "note_events",
- "confidential_note_events",
- "pipeline_events",
- "wiki_page_events",
- ),
- ),
- "custom-issue-tracker": (
- ("new_issue_url", "issues_url", "project_url"),
- ("description", "title", "push_events"),
- ),
- "drone-ci": (
- ("token", "drone_url"),
- (
- "enable_ssl_verification",
- "push_events",
- "merge_requests_events",
- "tag_push_events",
- ),
- ),
- "emails-on-push": (
- ("recipients",),
- (
- "disable_diffs",
- "send_from_committer_email",
- "push_events",
- "tag_push_events",
- "branches_to_be_notified",
- ),
- ),
- "pipelines-email": (
- ("recipients",),
- (
- "add_pusher",
- "notify_only_broken_builds",
- "branches_to_be_notified",
- "notify_only_default_branch",
- "pipeline_events",
- ),
- ),
- "external-wiki": (("external_wiki_url",), tuple()),
- "flowdock": (("token",), ("push_events",)),
- "github": (("token", "repository_url"), ("static_context",)),
- "hangouts-chat": (
- ("webhook",),
- (
- "notify_only_broken_pipelines",
- "notify_only_default_branch",
- "branches_to_be_notified",
- "push_events",
- "issues_events",
- "confidential_issues_events",
- "merge_requests_events",
- "tag_push_events",
- "note_events",
- "confidential_note_events",
- "pipeline_events",
- "wiki_page_events",
- ),
- ),
- "hipchat": (
- ("token",),
- (
- "color",
- "notify",
- "room",
- "api_version",
- "server",
- "push_events",
- "issues_events",
- "confidential_issues_events",
- "merge_requests_events",
- "tag_push_events",
- "note_events",
- "confidential_note_events",
- "pipeline_events",
- ),
- ),
- "irker": (
- ("recipients",),
- (
- "default_irc_uri",
- "server_port",
- "server_host",
- "colorize_messages",
- "push_events",
- ),
- ),
- "jira": (
- (
- "url",
- "username",
- "password",
- ),
- (
- "api_url",
- "active",
- "jira_issue_transition_id",
- "commit_events",
- "merge_requests_events",
- "comment_on_event_enabled",
- ),
- ),
- "slack-slash-commands": (("token",), tuple()),
- "mattermost-slash-commands": (("token",), ("username",)),
- "packagist": (
- ("username", "token"),
- ("server", "push_events", "merge_requests_events", "tag_push_events"),
- ),
- "mattermost": (
- ("webhook",),
- (
- "username",
- "channel",
- "notify_only_broken_pipelines",
- "notify_only_default_branch",
- "branches_to_be_notified",
- "push_events",
- "issues_events",
- "confidential_issues_events",
- "merge_requests_events",
- "tag_push_events",
- "note_events",
- "confidential_note_events",
- "pipeline_events",
- "wiki_page_events",
- "push_channel",
- "issue_channel",
- "confidential_issue_channel" "merge_request_channel",
- "note_channel",
- "confidential_note_channel",
- "tag_push_channel",
- "pipeline_channel",
- "wiki_page_channel",
- ),
- ),
- "pivotaltracker": (("token",), ("restrict_to_branch", "push_events")),
- "prometheus": (("api_url",), tuple()),
- "pushover": (
- ("api_key", "user_key", "priority"),
- ("device", "sound", "push_events"),
- ),
- "redmine": (
- ("new_issue_url", "project_url", "issues_url"),
- ("description", "push_events"),
- ),
- "slack": (
- ("webhook",),
- (
- "username",
- "channel",
- "notify_only_broken_pipelines",
- "notify_only_default_branch",
- "branches_to_be_notified",
- "commit_events",
- "confidential_issue_channel",
- "confidential_issues_events",
- "confidential_note_channel",
- "confidential_note_events",
- "deployment_channel",
- "deployment_events",
- "issue_channel",
- "issues_events",
- "job_events",
- "merge_request_channel",
- "merge_requests_events",
- "note_channel",
- "note_events",
- "pipeline_channel",
- "pipeline_events",
- "push_channel",
- "push_events",
- "tag_push_channel",
- "tag_push_events",
- "wiki_page_channel",
- "wiki_page_events",
- ),
- ),
- "microsoft-teams": (
- ("webhook",),
- (
- "notify_only_broken_pipelines",
- "notify_only_default_branch",
- "branches_to_be_notified",
- "push_events",
- "issues_events",
- "confidential_issues_events",
- "merge_requests_events",
- "tag_push_events",
- "note_events",
- "confidential_note_events",
- "pipeline_events",
- "wiki_page_events",
- ),
- ),
- "teamcity": (
- ("teamcity_url", "build_type", "username", "password"),
- ("push_events",),
- ),
- "jenkins": (("jenkins_url", "project_name"), ("username", "password")),
- "mock-ci": (("mock_service_url",), tuple()),
- "youtrack": (("issues_url", "project_url"), ("description", "push_events")),
- }
-
- def get(self, id, **kwargs):
- """Retrieve a single object.
-
- Args:
- id (int or str): ID of the object to retrieve
- lazy (bool): If True, don't request the server, but create a
- shallow object giving access to the managers. This is
- useful if you want to avoid useless calls to the API.
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- object: The generated RESTObject.
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the server cannot perform the request
- """
- obj = super(ProjectServiceManager, self).get(id, **kwargs)
- obj.id = id
- return obj
-
- def update(self, id=None, new_data=None, **kwargs):
- """Update an object on the server.
-
- Args:
- id: ID of the object to update (can be None if not required)
- new_data: the update data for the object
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- dict: The new object data (*not* a RESTObject)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabUpdateError: If the server cannot perform the request
- """
- new_data = new_data or {}
- super(ProjectServiceManager, self).update(id, new_data, **kwargs)
- self.id = id
-
- @cli.register_custom_action("ProjectServiceManager")
- def available(self, **kwargs):
- """List the services known by python-gitlab.
-
- Returns:
- list (str): The list of service code names.
- """
- return list(self._service_attrs.keys())
diff --git a/gitlab/v4/objects/settings.py b/gitlab/v4/objects/settings.py
deleted file mode 100644
index 1c8be25..0000000
--- a/gitlab/v4/objects/settings.py
+++ /dev/null
@@ -1,109 +0,0 @@
-from gitlab import exceptions as exc
-from gitlab import types
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import GetWithoutIdMixin, SaveMixin, UpdateMixin
-
-__all__ = [
- "ApplicationSettings",
- "ApplicationSettingsManager",
-]
-
-
-class ApplicationSettings(SaveMixin, RESTObject):
- _id_attr = None
-
-
-class ApplicationSettingsManager(GetWithoutIdMixin, UpdateMixin, RESTManager):
- _path = "/application/settings"
- _obj_cls = ApplicationSettings
- _update_attrs = RequiredOptional(
- optional=(
- "id",
- "default_projects_limit",
- "signup_enabled",
- "password_authentication_enabled_for_web",
- "gravatar_enabled",
- "sign_in_text",
- "created_at",
- "updated_at",
- "home_page_url",
- "default_branch_protection",
- "restricted_visibility_levels",
- "max_attachment_size",
- "session_expire_delay",
- "default_project_visibility",
- "default_snippet_visibility",
- "default_group_visibility",
- "outbound_local_requests_whitelist",
- "disabled_oauth_sign_in_sources",
- "domain_whitelist",
- "domain_blacklist_enabled",
- "domain_blacklist",
- "domain_allowlist",
- "domain_denylist_enabled",
- "domain_denylist",
- "external_authorization_service_enabled",
- "external_authorization_service_url",
- "external_authorization_service_default_label",
- "external_authorization_service_timeout",
- "import_sources",
- "user_oauth_applications",
- "after_sign_out_path",
- "container_registry_token_expire_delay",
- "repository_storages",
- "plantuml_enabled",
- "plantuml_url",
- "terminal_max_session_time",
- "polling_interval_multiplier",
- "rsa_key_restriction",
- "dsa_key_restriction",
- "ecdsa_key_restriction",
- "ed25519_key_restriction",
- "first_day_of_week",
- "enforce_terms",
- "terms",
- "performance_bar_allowed_group_id",
- "instance_statistics_visibility_private",
- "user_show_add_ssh_key_message",
- "file_template_project_id",
- "local_markdown_version",
- "asset_proxy_enabled",
- "asset_proxy_url",
- "asset_proxy_whitelist",
- "asset_proxy_allowlist",
- "geo_node_allowed_ips",
- "allow_local_requests_from_hooks_and_services",
- "allow_local_requests_from_web_hooks_and_services",
- "allow_local_requests_from_system_hooks",
- ),
- )
- _types = {
- "asset_proxy_allowlist": types.ListAttribute,
- "disabled_oauth_sign_in_sources": types.ListAttribute,
- "domain_allowlist": types.ListAttribute,
- "domain_denylist": types.ListAttribute,
- "import_sources": types.ListAttribute,
- "restricted_visibility_levels": types.ListAttribute,
- }
-
- @exc.on_http_error(exc.GitlabUpdateError)
- def update(self, id=None, new_data=None, **kwargs):
- """Update an object on the server.
-
- Args:
- id: ID of the object to update (can be None if not required)
- new_data: the update data for the object
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- dict: The new object data (*not* a RESTObject)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabUpdateError: If the server cannot perform the request
- """
- new_data = new_data or {}
- data = new_data.copy()
- if "domain_whitelist" in data and data["domain_whitelist"] is None:
- data.pop("domain_whitelist")
- super(ApplicationSettingsManager, self).update(id, data, **kwargs)
diff --git a/gitlab/v4/objects/sidekiq.py b/gitlab/v4/objects/sidekiq.py
deleted file mode 100644
index dc1094a..0000000
--- a/gitlab/v4/objects/sidekiq.py
+++ /dev/null
@@ -1,83 +0,0 @@
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab.base import RESTManager
-
-__all__ = [
- "SidekiqManager",
-]
-
-
-class SidekiqManager(RESTManager):
- """Manager for the Sidekiq methods.
-
- This manager doesn't actually manage objects but provides helper function
- for the sidekiq metrics API.
- """
-
- @cli.register_custom_action("SidekiqManager")
- @exc.on_http_error(exc.GitlabGetError)
- def queue_metrics(self, **kwargs):
- """Return the registered queues information.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the information couldn't be retrieved
-
- Returns:
- dict: Information about the Sidekiq queues
- """
- return self.gitlab.http_get("/sidekiq/queue_metrics", **kwargs)
-
- @cli.register_custom_action("SidekiqManager")
- @exc.on_http_error(exc.GitlabGetError)
- def process_metrics(self, **kwargs):
- """Return the registered sidekiq workers.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the information couldn't be retrieved
-
- Returns:
- dict: Information about the register Sidekiq worker
- """
- return self.gitlab.http_get("/sidekiq/process_metrics", **kwargs)
-
- @cli.register_custom_action("SidekiqManager")
- @exc.on_http_error(exc.GitlabGetError)
- def job_stats(self, **kwargs):
- """Return statistics about the jobs performed.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the information couldn't be retrieved
-
- Returns:
- dict: Statistics about the Sidekiq jobs performed
- """
- return self.gitlab.http_get("/sidekiq/job_stats", **kwargs)
-
- @cli.register_custom_action("SidekiqManager")
- @exc.on_http_error(exc.GitlabGetError)
- def compound_metrics(self, **kwargs):
- """Return all available metrics and statistics.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the information couldn't be retrieved
-
- Returns:
- dict: All available Sidekiq metrics and statistics
- """
- return self.gitlab.http_get("/sidekiq/compound_metrics", **kwargs)
diff --git a/gitlab/v4/objects/snippets.py b/gitlab/v4/objects/snippets.py
deleted file mode 100644
index 164b30c..0000000
--- a/gitlab/v4/objects/snippets.py
+++ /dev/null
@@ -1,123 +0,0 @@
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab import utils
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CRUDMixin, ObjectDeleteMixin, SaveMixin, UserAgentDetailMixin
-
-from .award_emojis import ProjectSnippetAwardEmojiManager # noqa: F401
-from .discussions import ProjectSnippetDiscussionManager # noqa: F401
-from .notes import ProjectSnippetNoteManager # noqa: F401
-
-__all__ = [
- "Snippet",
- "SnippetManager",
- "ProjectSnippet",
- "ProjectSnippetManager",
-]
-
-
-class Snippet(UserAgentDetailMixin, SaveMixin, ObjectDeleteMixin, RESTObject):
- _short_print_attr = "title"
-
- @cli.register_custom_action("Snippet")
- @exc.on_http_error(exc.GitlabGetError)
- def content(self, streamed=False, action=None, chunk_size=1024, **kwargs):
- """Return the content of a snippet.
-
- Args:
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment.
- action (callable): Callable responsible of dealing with chunk of
- data
- chunk_size (int): Size of each chunk
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the content could not be retrieved
-
- Returns:
- str: The snippet content
- """
- path = "/snippets/%s/raw" % self.get_id()
- result = self.manager.gitlab.http_get(
- path, streamed=streamed, raw=True, **kwargs
- )
- return utils.response_content(result, streamed, action, chunk_size)
-
-
-class SnippetManager(CRUDMixin, RESTManager):
- _path = "/snippets"
- _obj_cls = Snippet
- _create_attrs = RequiredOptional(
- required=("title", "file_name", "content"), optional=("lifetime", "visibility")
- )
- _update_attrs = RequiredOptional(
- optional=("title", "file_name", "content", "visibility")
- )
-
- @cli.register_custom_action("SnippetManager")
- def public(self, **kwargs):
- """List all the public snippets.
-
- Args:
- all (bool): If True the returned object will be a list
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabListError: If the list could not be retrieved
-
- Returns:
- RESTObjectList: A generator for the snippets list
- """
- return self.list(path="/snippets/public", **kwargs)
-
-
-class ProjectSnippet(UserAgentDetailMixin, SaveMixin, ObjectDeleteMixin, RESTObject):
- _url = "/projects/%(project_id)s/snippets"
- _short_print_attr = "title"
-
- awardemojis: ProjectSnippetAwardEmojiManager
- discussions: ProjectSnippetDiscussionManager
- notes: ProjectSnippetNoteManager
-
- @cli.register_custom_action("ProjectSnippet")
- @exc.on_http_error(exc.GitlabGetError)
- def content(self, streamed=False, action=None, chunk_size=1024, **kwargs):
- """Return the content of a snippet.
-
- Args:
- streamed (bool): If True the data will be processed by chunks of
- `chunk_size` and each chunk is passed to `action` for
- treatment.
- action (callable): Callable responsible of dealing with chunk of
- data
- chunk_size (int): Size of each chunk
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabGetError: If the content could not be retrieved
-
- Returns:
- str: The snippet content
- """
- path = "%s/%s/raw" % (self.manager.path, self.get_id())
- result = self.manager.gitlab.http_get(
- path, streamed=streamed, raw=True, **kwargs
- )
- return utils.response_content(result, streamed, action, chunk_size)
-
-
-class ProjectSnippetManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/snippets"
- _obj_cls = ProjectSnippet
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("title", "file_name", "content", "visibility"),
- optional=("description",),
- )
- _update_attrs = RequiredOptional(
- optional=("title", "file_name", "content", "visibility", "description"),
- )
diff --git a/gitlab/v4/objects/statistics.py b/gitlab/v4/objects/statistics.py
deleted file mode 100644
index 5d7c19e..0000000
--- a/gitlab/v4/objects/statistics.py
+++ /dev/null
@@ -1,52 +0,0 @@
-from gitlab.base import RESTManager, RESTObject
-from gitlab.mixins import GetWithoutIdMixin, RefreshMixin
-
-__all__ = [
- "GroupIssuesStatistics",
- "GroupIssuesStatisticsManager",
- "ProjectAdditionalStatistics",
- "ProjectAdditionalStatisticsManager",
- "IssuesStatistics",
- "IssuesStatisticsManager",
- "ProjectIssuesStatistics",
- "ProjectIssuesStatisticsManager",
-]
-
-
-class ProjectAdditionalStatistics(RefreshMixin, RESTObject):
- _id_attr = None
-
-
-class ProjectAdditionalStatisticsManager(GetWithoutIdMixin, RESTManager):
- _path = "/projects/%(project_id)s/statistics"
- _obj_cls = ProjectAdditionalStatistics
- _from_parent_attrs = {"project_id": "id"}
-
-
-class IssuesStatistics(RefreshMixin, RESTObject):
- _id_attr = None
-
-
-class IssuesStatisticsManager(GetWithoutIdMixin, RESTManager):
- _path = "/issues_statistics"
- _obj_cls = IssuesStatistics
-
-
-class GroupIssuesStatistics(RefreshMixin, RESTObject):
- _id_attr = None
-
-
-class GroupIssuesStatisticsManager(GetWithoutIdMixin, RESTManager):
- _path = "/groups/%(group_id)s/issues_statistics"
- _obj_cls = GroupIssuesStatistics
- _from_parent_attrs = {"group_id": "id"}
-
-
-class ProjectIssuesStatistics(RefreshMixin, RESTObject):
- _id_attr = None
-
-
-class ProjectIssuesStatisticsManager(GetWithoutIdMixin, RESTManager):
- _path = "/projects/%(project_id)s/issues_statistics"
- _obj_cls = ProjectIssuesStatistics
- _from_parent_attrs = {"project_id": "id"}
diff --git a/gitlab/v4/objects/tags.py b/gitlab/v4/objects/tags.py
deleted file mode 100644
index 44fc23c..0000000
--- a/gitlab/v4/objects/tags.py
+++ /dev/null
@@ -1,37 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import NoUpdateMixin, ObjectDeleteMixin
-
-__all__ = [
- "ProjectTag",
- "ProjectTagManager",
- "ProjectProtectedTag",
- "ProjectProtectedTagManager",
-]
-
-
-class ProjectTag(ObjectDeleteMixin, RESTObject):
- _id_attr = "name"
- _short_print_attr = "name"
-
-
-class ProjectTagManager(NoUpdateMixin, RESTManager):
- _path = "/projects/%(project_id)s/repository/tags"
- _obj_cls = ProjectTag
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("tag_name", "ref"), optional=("message",)
- )
-
-
-class ProjectProtectedTag(ObjectDeleteMixin, RESTObject):
- _id_attr = "name"
- _short_print_attr = "name"
-
-
-class ProjectProtectedTagManager(NoUpdateMixin, RESTManager):
- _path = "/projects/%(project_id)s/protected_tags"
- _obj_cls = ProjectProtectedTag
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("name",), optional=("create_access_level",)
- )
diff --git a/gitlab/v4/objects/templates.py b/gitlab/v4/objects/templates.py
deleted file mode 100644
index 04de463..0000000
--- a/gitlab/v4/objects/templates.py
+++ /dev/null
@@ -1,51 +0,0 @@
-from gitlab.base import RESTManager, RESTObject
-from gitlab.mixins import RetrieveMixin
-
-__all__ = [
- "Dockerfile",
- "DockerfileManager",
- "Gitignore",
- "GitignoreManager",
- "Gitlabciyml",
- "GitlabciymlManager",
- "License",
- "LicenseManager",
-]
-
-
-class Dockerfile(RESTObject):
- _id_attr = "name"
-
-
-class DockerfileManager(RetrieveMixin, RESTManager):
- _path = "/templates/dockerfiles"
- _obj_cls = Dockerfile
-
-
-class Gitignore(RESTObject):
- _id_attr = "name"
-
-
-class GitignoreManager(RetrieveMixin, RESTManager):
- _path = "/templates/gitignores"
- _obj_cls = Gitignore
-
-
-class Gitlabciyml(RESTObject):
- _id_attr = "name"
-
-
-class GitlabciymlManager(RetrieveMixin, RESTManager):
- _path = "/templates/gitlab_ci_ymls"
- _obj_cls = Gitlabciyml
-
-
-class License(RESTObject):
- _id_attr = "key"
-
-
-class LicenseManager(RetrieveMixin, RESTManager):
- _path = "/templates/licenses"
- _obj_cls = License
- _list_filters = ("popular",)
- _optional_get_attrs = ("project", "fullname")
diff --git a/gitlab/v4/objects/todos.py b/gitlab/v4/objects/todos.py
deleted file mode 100644
index de82437..0000000
--- a/gitlab/v4/objects/todos.py
+++ /dev/null
@@ -1,50 +0,0 @@
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab.base import RESTManager, RESTObject
-from gitlab.mixins import DeleteMixin, ListMixin, ObjectDeleteMixin
-
-__all__ = [
- "Todo",
- "TodoManager",
-]
-
-
-class Todo(ObjectDeleteMixin, RESTObject):
- @cli.register_custom_action("Todo")
- @exc.on_http_error(exc.GitlabTodoError)
- def mark_as_done(self, **kwargs):
- """Mark the todo as done.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabTodoError: If the server failed to perform the request
- """
- path = "%s/%s/mark_as_done" % (self.manager.path, self.id)
- server_data = self.manager.gitlab.http_post(path, **kwargs)
- self._update_attrs(server_data)
-
-
-class TodoManager(ListMixin, DeleteMixin, RESTManager):
- _path = "/todos"
- _obj_cls = Todo
- _list_filters = ("action", "author_id", "project_id", "state", "type")
-
- @cli.register_custom_action("TodoManager")
- @exc.on_http_error(exc.GitlabTodoError)
- def mark_all_as_done(self, **kwargs):
- """Mark all the todos as done.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabTodoError: If the server failed to perform the request
-
- Returns:
- int: The number of todos marked done
- """
- self.gitlab.http_post("/todos/mark_as_done", **kwargs)
diff --git a/gitlab/v4/objects/triggers.py b/gitlab/v4/objects/triggers.py
deleted file mode 100644
index f203d93..0000000
--- a/gitlab/v4/objects/triggers.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CRUDMixin, ObjectDeleteMixin, SaveMixin
-
-__all__ = [
- "ProjectTrigger",
- "ProjectTriggerManager",
-]
-
-
-class ProjectTrigger(SaveMixin, ObjectDeleteMixin, RESTObject):
- pass
-
-
-class ProjectTriggerManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/triggers"
- _obj_cls = ProjectTrigger
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(required=("description",))
- _update_attrs = RequiredOptional(required=("description",))
diff --git a/gitlab/v4/objects/users.py b/gitlab/v4/objects/users.py
deleted file mode 100644
index e4bbcf1..0000000
--- a/gitlab/v4/objects/users.py
+++ /dev/null
@@ -1,514 +0,0 @@
-from typing import Any, cast, Dict, List, Union
-
-import requests
-
-from gitlab import cli
-from gitlab import exceptions as exc
-from gitlab import types
-from gitlab.base import RequiredOptional, RESTManager, RESTObject, RESTObjectList
-from gitlab.mixins import (
- CreateMixin,
- CRUDMixin,
- DeleteMixin,
- GetWithoutIdMixin,
- ListMixin,
- NoUpdateMixin,
- ObjectDeleteMixin,
- RetrieveMixin,
- SaveMixin,
- UpdateMixin,
-)
-
-from .custom_attributes import UserCustomAttributeManager # noqa: F401
-from .events import UserEventManager # noqa: F401
-from .personal_access_tokens import UserPersonalAccessTokenManager # noqa: F401
-
-__all__ = [
- "CurrentUserEmail",
- "CurrentUserEmailManager",
- "CurrentUserGPGKey",
- "CurrentUserGPGKeyManager",
- "CurrentUserKey",
- "CurrentUserKeyManager",
- "CurrentUserStatus",
- "CurrentUserStatusManager",
- "CurrentUser",
- "CurrentUserManager",
- "User",
- "UserManager",
- "ProjectUser",
- "ProjectUserManager",
- "UserEmail",
- "UserEmailManager",
- "UserActivities",
- "UserStatus",
- "UserStatusManager",
- "UserActivitiesManager",
- "UserGPGKey",
- "UserGPGKeyManager",
- "UserKey",
- "UserKeyManager",
- "UserIdentityProviderManager",
- "UserImpersonationToken",
- "UserImpersonationTokenManager",
- "UserMembership",
- "UserMembershipManager",
- "UserProject",
- "UserProjectManager",
-]
-
-
-class CurrentUserEmail(ObjectDeleteMixin, RESTObject):
- _short_print_attr = "email"
-
-
-class CurrentUserEmailManager(RetrieveMixin, CreateMixin, DeleteMixin, RESTManager):
- _path = "/user/emails"
- _obj_cls = CurrentUserEmail
- _create_attrs = RequiredOptional(required=("email",))
-
-
-class CurrentUserGPGKey(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class CurrentUserGPGKeyManager(RetrieveMixin, CreateMixin, DeleteMixin, RESTManager):
- _path = "/user/gpg_keys"
- _obj_cls = CurrentUserGPGKey
- _create_attrs = RequiredOptional(required=("key",))
-
-
-class CurrentUserKey(ObjectDeleteMixin, RESTObject):
- _short_print_attr = "title"
-
-
-class CurrentUserKeyManager(RetrieveMixin, CreateMixin, DeleteMixin, RESTManager):
- _path = "/user/keys"
- _obj_cls = CurrentUserKey
- _create_attrs = RequiredOptional(required=("title", "key"))
-
-
-class CurrentUserStatus(SaveMixin, RESTObject):
- _id_attr = None
- _short_print_attr = "message"
-
-
-class CurrentUserStatusManager(GetWithoutIdMixin, UpdateMixin, RESTManager):
- _path = "/user/status"
- _obj_cls = CurrentUserStatus
- _update_attrs = RequiredOptional(optional=("emoji", "message"))
-
-
-class CurrentUser(RESTObject):
- _id_attr = None
- _short_print_attr = "username"
-
- emails: CurrentUserEmailManager
- gpgkeys: CurrentUserGPGKeyManager
- keys: CurrentUserKeyManager
- status: CurrentUserStatusManager
-
-
-class CurrentUserManager(GetWithoutIdMixin, RESTManager):
- _path = "/user"
- _obj_cls = CurrentUser
-
-
-class User(SaveMixin, ObjectDeleteMixin, RESTObject):
- _short_print_attr = "username"
-
- customattributes: UserCustomAttributeManager
- emails: "UserEmailManager"
- events: UserEventManager
- followers_users: "UserFollowersManager"
- following_users: "UserFollowingManager"
- gpgkeys: "UserGPGKeyManager"
- identityproviders: "UserIdentityProviderManager"
- impersonationtokens: "UserImpersonationTokenManager"
- keys: "UserKeyManager"
- memberships: "UserMembershipManager"
- personal_access_tokens: UserPersonalAccessTokenManager
- projects: "UserProjectManager"
- status: "UserStatusManager"
-
- @cli.register_custom_action("User")
- @exc.on_http_error(exc.GitlabBlockError)
- def block(self, **kwargs: Any) -> Union[Dict[str, Any], requests.Response]:
- """Block the user.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabBlockError: If the user could not be blocked
-
- Returns:
- bool: Whether the user status has been changed
- """
- path = "/users/%s/block" % self.id
- server_data = self.manager.gitlab.http_post(path, **kwargs)
- if server_data is True:
- self._attrs["state"] = "blocked"
- return server_data
-
- @cli.register_custom_action("User")
- @exc.on_http_error(exc.GitlabFollowError)
- def follow(self, **kwargs: Any) -> Union[Dict[str, Any], requests.Response]:
- """Follow the user.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabFollowError: If the user could not be followed
-
- Returns:
- dict: The new object data (*not* a RESTObject)
- """
- path = "/users/%s/follow" % self.id
- return self.manager.gitlab.http_post(path, **kwargs)
-
- @cli.register_custom_action("User")
- @exc.on_http_error(exc.GitlabUnfollowError)
- def unfollow(self, **kwargs: Any) -> Union[Dict[str, Any], requests.Response]:
- """Unfollow the user.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabUnfollowError: If the user could not be followed
-
- Returns:
- dict: The new object data (*not* a RESTObject)
- """
- path = "/users/%s/unfollow" % self.id
- return self.manager.gitlab.http_post(path, **kwargs)
-
- @cli.register_custom_action("User")
- @exc.on_http_error(exc.GitlabUnblockError)
- def unblock(self, **kwargs: Any) -> Union[Dict[str, Any], requests.Response]:
- """Unblock the user.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabUnblockError: If the user could not be unblocked
-
- Returns:
- bool: Whether the user status has been changed
- """
- path = "/users/%s/unblock" % self.id
- server_data = self.manager.gitlab.http_post(path, **kwargs)
- if server_data is True:
- self._attrs["state"] = "active"
- return server_data
-
- @cli.register_custom_action("User")
- @exc.on_http_error(exc.GitlabDeactivateError)
- def deactivate(self, **kwargs: Any) -> Union[Dict[str, Any], requests.Response]:
- """Deactivate the user.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabDeactivateError: If the user could not be deactivated
-
- Returns:
- bool: Whether the user status has been changed
- """
- path = "/users/%s/deactivate" % self.id
- server_data = self.manager.gitlab.http_post(path, **kwargs)
- if server_data:
- self._attrs["state"] = "deactivated"
- return server_data
-
- @cli.register_custom_action("User")
- @exc.on_http_error(exc.GitlabActivateError)
- def activate(self, **kwargs: Any) -> Union[Dict[str, Any], requests.Response]:
- """Activate the user.
-
- Args:
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabActivateError: If the user could not be activated
-
- Returns:
- bool: Whether the user status has been changed
- """
- path = "/users/%s/activate" % self.id
- server_data = self.manager.gitlab.http_post(path, **kwargs)
- if server_data:
- self._attrs["state"] = "active"
- return server_data
-
-
-class UserManager(CRUDMixin, RESTManager):
- _path = "/users"
- _obj_cls = User
-
- _list_filters = (
- "active",
- "blocked",
- "username",
- "extern_uid",
- "provider",
- "external",
- "search",
- "custom_attributes",
- "status",
- "two_factor",
- )
- _create_attrs = RequiredOptional(
- optional=(
- "email",
- "username",
- "name",
- "password",
- "reset_password",
- "skype",
- "linkedin",
- "twitter",
- "projects_limit",
- "extern_uid",
- "provider",
- "bio",
- "admin",
- "can_create_group",
- "website_url",
- "skip_confirmation",
- "external",
- "organization",
- "location",
- "avatar",
- "public_email",
- "private_profile",
- "color_scheme_id",
- "theme_id",
- ),
- )
- _update_attrs = RequiredOptional(
- required=("email", "username", "name"),
- optional=(
- "password",
- "skype",
- "linkedin",
- "twitter",
- "projects_limit",
- "extern_uid",
- "provider",
- "bio",
- "admin",
- "can_create_group",
- "website_url",
- "skip_reconfirmation",
- "external",
- "organization",
- "location",
- "avatar",
- "public_email",
- "private_profile",
- "color_scheme_id",
- "theme_id",
- ),
- )
- _types = {"confirm": types.LowercaseStringAttribute, "avatar": types.ImageAttribute}
-
- def get(self, id: Union[str, int], lazy: bool = False, **kwargs: Any) -> User:
- return cast(User, super().get(id=id, lazy=lazy, **kwargs))
-
-
-class ProjectUser(RESTObject):
- pass
-
-
-class ProjectUserManager(ListMixin, RESTManager):
- _path = "/projects/%(project_id)s/users"
- _obj_cls = ProjectUser
- _from_parent_attrs = {"project_id": "id"}
- _list_filters = ("search", "skip_users")
- _types = {"skip_users": types.ListAttribute}
-
-
-class UserEmail(ObjectDeleteMixin, RESTObject):
- _short_print_attr = "email"
-
-
-class UserEmailManager(RetrieveMixin, CreateMixin, DeleteMixin, RESTManager):
- _path = "/users/%(user_id)s/emails"
- _obj_cls = UserEmail
- _from_parent_attrs = {"user_id": "id"}
- _create_attrs = RequiredOptional(required=("email",))
-
-
-class UserActivities(RESTObject):
- _id_attr = "username"
-
-
-class UserStatus(RESTObject):
- _id_attr = None
- _short_print_attr = "message"
-
-
-class UserStatusManager(GetWithoutIdMixin, RESTManager):
- _path = "/users/%(user_id)s/status"
- _obj_cls = UserStatus
- _from_parent_attrs = {"user_id": "id"}
-
-
-class UserActivitiesManager(ListMixin, RESTManager):
- _path = "/user/activities"
- _obj_cls = UserActivities
-
-
-class UserGPGKey(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class UserGPGKeyManager(RetrieveMixin, CreateMixin, DeleteMixin, RESTManager):
- _path = "/users/%(user_id)s/gpg_keys"
- _obj_cls = UserGPGKey
- _from_parent_attrs = {"user_id": "id"}
- _create_attrs = RequiredOptional(required=("key",))
-
-
-class UserKey(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class UserKeyManager(ListMixin, CreateMixin, DeleteMixin, RESTManager):
- _path = "/users/%(user_id)s/keys"
- _obj_cls = UserKey
- _from_parent_attrs = {"user_id": "id"}
- _create_attrs = RequiredOptional(required=("title", "key"))
-
-
-class UserIdentityProviderManager(DeleteMixin, RESTManager):
- """Manager for user identities.
-
- This manager does not actually manage objects but enables
- functionality for deletion of user identities by provider.
- """
-
- _path = "/users/%(user_id)s/identities"
- _from_parent_attrs = {"user_id": "id"}
-
-
-class UserImpersonationToken(ObjectDeleteMixin, RESTObject):
- pass
-
-
-class UserImpersonationTokenManager(NoUpdateMixin, RESTManager):
- _path = "/users/%(user_id)s/impersonation_tokens"
- _obj_cls = UserImpersonationToken
- _from_parent_attrs = {"user_id": "id"}
- _create_attrs = RequiredOptional(
- required=("name", "scopes"), optional=("expires_at",)
- )
- _list_filters = ("state",)
-
-
-class UserMembership(RESTObject):
- _id_attr = "source_id"
-
-
-class UserMembershipManager(RetrieveMixin, RESTManager):
- _path = "/users/%(user_id)s/memberships"
- _obj_cls = UserMembership
- _from_parent_attrs = {"user_id": "id"}
- _list_filters = ("type",)
-
-
-# Having this outside projects avoids circular imports due to ProjectUser
-class UserProject(RESTObject):
- pass
-
-
-class UserProjectManager(ListMixin, CreateMixin, RESTManager):
- _path = "/projects/user/%(user_id)s"
- _obj_cls = UserProject
- _from_parent_attrs = {"user_id": "id"}
- _create_attrs = RequiredOptional(
- required=("name",),
- optional=(
- "default_branch",
- "issues_enabled",
- "wall_enabled",
- "merge_requests_enabled",
- "wiki_enabled",
- "snippets_enabled",
- "public",
- "visibility",
- "description",
- "builds_enabled",
- "public_builds",
- "import_url",
- "only_allow_merge_if_build_succeeds",
- ),
- )
- _list_filters = (
- "archived",
- "visibility",
- "order_by",
- "sort",
- "search",
- "simple",
- "owned",
- "membership",
- "starred",
- "statistics",
- "with_issues_enabled",
- "with_merge_requests_enabled",
- "with_custom_attributes",
- "with_programming_language",
- "wiki_checksum_failed",
- "repository_checksum_failed",
- "min_access_level",
- "id_after",
- "id_before",
- )
-
- def list(self, **kwargs: Any) -> Union[RESTObjectList, List[RESTObject]]:
- """Retrieve a list of objects.
-
- Args:
- all (bool): If True, return all the items, without pagination
- per_page (int): Number of items to retrieve per request
- page (int): ID of the page to return (starts with page 1)
- as_list (bool): If set to False and no pagination option is
- defined, return a generator instead of a list
- **kwargs: Extra options to send to the server (e.g. sudo)
-
- Returns:
- list: The list of objects, or a generator if `as_list` is False
-
- Raises:
- GitlabAuthenticationError: If authentication is not correct
- GitlabListError: If the server cannot perform the request
- """
- if self._parent:
- path = "/users/%s/projects" % self._parent.id
- else:
- path = "/users/%s/projects" % kwargs["user_id"]
- return ListMixin.list(self, path=path, **kwargs)
-
-
-class UserFollowersManager(ListMixin, RESTManager):
- _path = "/users/%(user_id)s/followers"
- _obj_cls = User
- _from_parent_attrs = {"user_id": "id"}
-
-
-class UserFollowingManager(ListMixin, RESTManager):
- _path = "/users/%(user_id)s/following"
- _obj_cls = User
- _from_parent_attrs = {"user_id": "id"}
diff --git a/gitlab/v4/objects/variables.py b/gitlab/v4/objects/variables.py
deleted file mode 100644
index 2e5e483..0000000
--- a/gitlab/v4/objects/variables.py
+++ /dev/null
@@ -1,66 +0,0 @@
-"""
-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 RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CRUDMixin, ObjectDeleteMixin, SaveMixin
-
-__all__ = [
- "Variable",
- "VariableManager",
- "GroupVariable",
- "GroupVariableManager",
- "ProjectVariable",
- "ProjectVariableManager",
-]
-
-
-class Variable(SaveMixin, ObjectDeleteMixin, RESTObject):
- _id_attr = "key"
-
-
-class VariableManager(CRUDMixin, RESTManager):
- _path = "/admin/ci/variables"
- _obj_cls = Variable
- _create_attrs = RequiredOptional(
- required=("key", "value"), optional=("protected", "variable_type", "masked")
- )
- _update_attrs = RequiredOptional(
- required=("key", "value"), optional=("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 = RequiredOptional(
- required=("key", "value"), optional=("protected", "variable_type", "masked")
- )
- _update_attrs = RequiredOptional(
- required=("key", "value"), optional=("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 = RequiredOptional(
- required=("key", "value"),
- optional=("protected", "variable_type", "masked", "environment_scope"),
- )
- _update_attrs = RequiredOptional(
- required=("key", "value"),
- optional=("protected", "variable_type", "masked", "environment_scope"),
- )
diff --git a/gitlab/v4/objects/wikis.py b/gitlab/v4/objects/wikis.py
deleted file mode 100644
index a86b442..0000000
--- a/gitlab/v4/objects/wikis.py
+++ /dev/null
@@ -1,41 +0,0 @@
-from gitlab.base import RequiredOptional, RESTManager, RESTObject
-from gitlab.mixins import CRUDMixin, ObjectDeleteMixin, SaveMixin
-
-__all__ = [
- "ProjectWiki",
- "ProjectWikiManager",
- "GroupWiki",
- "GroupWikiManager",
-]
-
-
-class ProjectWiki(SaveMixin, ObjectDeleteMixin, RESTObject):
- _id_attr = "slug"
- _short_print_attr = "slug"
-
-
-class ProjectWikiManager(CRUDMixin, RESTManager):
- _path = "/projects/%(project_id)s/wikis"
- _obj_cls = ProjectWiki
- _from_parent_attrs = {"project_id": "id"}
- _create_attrs = RequiredOptional(
- required=("title", "content"), optional=("format",)
- )
- _update_attrs = RequiredOptional(optional=("title", "content", "format"))
- _list_filters = ("with_content",)
-
-
-class GroupWiki(SaveMixin, ObjectDeleteMixin, RESTObject):
- _id_attr = "slug"
- _short_print_attr = "slug"
-
-
-class GroupWikiManager(CRUDMixin, RESTManager):
- _path = "/groups/%(group_id)s/wikis"
- _obj_cls = GroupWiki
- _from_parent_attrs = {"group_id": "id"}
- _create_attrs = RequiredOptional(
- required=("title", "content"), optional=("format",)
- )
- _update_attrs = RequiredOptional(optional=("title", "content", "format"))
- _list_filters = ("with_content",)
diff --git a/pyproject.toml b/pyproject.toml
deleted file mode 100644
index a924199..0000000
--- a/pyproject.toml
+++ /dev/null
@@ -1,34 +0,0 @@
-[tool.isort]
-profile = "black"
-multi_line_output = 3
-order_by_type = false
-
-[tool.mypy]
-disallow_incomplete_defs = true
-disallow_untyped_defs = true
-files = "."
-
-[[tool.mypy.overrides]] # Overrides for currently untyped modules
-module = [
- "docs.*",
- "docs.ext.*",
- "gitlab.v4.objects.*",
- "setup",
- "tests.functional.*",
- "tests.functional.api.*",
- "tests.unit.*",
- "tests.smoke.*"
-]
-ignore_errors = true
-
-[[tool.mypy.overrides]] # Overrides to negate above patterns
-module = [
- "gitlab.v4.objects.projects",
- "gitlab.v4.objects.users"
-]
-ignore_errors = false
-
-[tool.semantic_release]
-version_variable = "gitlab/__version__.py:__version__"
-commit_subject = "chore: release v{version}"
-commit_message = ""
diff --git a/requirements-docker.txt b/requirements-docker.txt
deleted file mode 100644
index 4ff5657..0000000
--- a/requirements-docker.txt
+++ /dev/null
@@ -1,5 +0,0 @@
--r requirements.txt
--r requirements-test.txt
-docker-compose==1.29.2 # prevent inconsistent .env behavior from system install
-pytest-console-scripts
-pytest-docker
diff --git a/requirements-docs.txt b/requirements-docs.txt
deleted file mode 100644
index 05e55ba..0000000
--- a/requirements-docs.txt
+++ /dev/null
@@ -1,6 +0,0 @@
--r requirements.txt
-jinja2
-myst-parser
-sphinx==4.2.0
-sphinx_rtd_theme
-sphinxcontrib-autoprogram
diff --git a/requirements-lint.txt b/requirements-lint.txt
deleted file mode 100644
index 3b47c00..0000000
--- a/requirements-lint.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-black==20.8b1
-flake8==3.9.2
-isort==5.9.3
-mypy==0.910
-types-PyYAML==5.4.10
-types-requests==2.25.9
diff --git a/requirements-test.txt b/requirements-test.txt
deleted file mode 100644
index 8d61ad1..0000000
--- a/requirements-test.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-coverage
-httmock
-mock
-pytest
-pytest-cov
-responses
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index f7dd2f6..0000000
--- a/requirements.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-requests==2.26.0
-requests-toolbelt==0.9.1
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index 0e198a6..0000000
--- a/setup.cfg
+++ /dev/null
@@ -1,3 +0,0 @@
-[build_sphinx]
-warning-is-error = 1
-keep-going = 1
diff --git a/setup.py b/setup.py
deleted file mode 100644
index c809142..0000000
--- a/setup.py
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-from setuptools import find_packages, setup
-
-
-def get_version():
- with open("gitlab/__version__.py") as f:
- for line in f:
- if line.startswith("__version__"):
- return eval(line.split("=")[-1])
-
-
-with open("README.rst", "r") as readme_file:
- readme = readme_file.read()
-
-setup(
- name="python-gitlab",
- version=get_version(),
- description="Interact with GitLab API",
- long_description=readme,
- long_description_content_type="text/x-rst",
- author="Gauvain Pocentek",
- author_email="gauvain@pocentek.net",
- license="LGPLv3",
- url="https://github.com/python-gitlab/python-gitlab",
- packages=find_packages(exclude=["tests*"]),
- install_requires=["requests>=2.25.0", "requests-toolbelt>=0.9.1"],
- package_data={
- "gitlab": ["py.typed"],
- },
- python_requires=">=3.6.0",
- entry_points={"console_scripts": ["gitlab = gitlab.cli:main"]},
- classifiers=[
- "Development Status :: 5 - Production/Stable",
- "Environment :: Console",
- "Intended Audience :: System Administrators",
- "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
- "Natural Language :: English",
- "Operating System :: POSIX",
- "Operating System :: Microsoft :: Windows",
- "Programming Language :: Python",
- "Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.6",
- "Programming Language :: Python :: 3.7",
- "Programming Language :: Python :: 3.8",
- "Programming Language :: Python :: 3.9",
- "Programming Language :: Python :: 3.10",
- ],
- extras_require={
- "autocompletion": ["argcomplete>=1.10.0,<2"],
- "yaml": ["PyYaml>=5.2"],
- },
-)
diff --git a/tests/__init__.py b/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tests/__init__.py
+++ /dev/null
diff --git a/tests/functional/__init__.py b/tests/functional/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tests/functional/__init__.py
+++ /dev/null
diff --git a/tests/functional/api/__init__.py b/tests/functional/api/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tests/functional/api/__init__.py
+++ /dev/null
diff --git a/tests/functional/api/test_clusters.py b/tests/functional/api/test_clusters.py
deleted file mode 100644
index 8930aad..0000000
--- a/tests/functional/api/test_clusters.py
+++ /dev/null
@@ -1,46 +0,0 @@
-def test_project_clusters(project):
- project.clusters.create(
- {
- "name": "cluster1",
- "platform_kubernetes_attributes": {
- "api_url": "http://url",
- "token": "tokenval",
- },
- }
- )
- clusters = project.clusters.list()
- assert len(clusters) == 1
-
- cluster = clusters[0]
- cluster.platform_kubernetes_attributes = {"api_url": "http://newurl"}
- cluster.save()
-
- cluster = project.clusters.list()[0]
- assert cluster.platform_kubernetes["api_url"] == "http://newurl"
-
- cluster.delete()
- assert len(project.clusters.list()) == 0
-
-
-def test_group_clusters(group):
- group.clusters.create(
- {
- "name": "cluster1",
- "platform_kubernetes_attributes": {
- "api_url": "http://url",
- "token": "tokenval",
- },
- }
- )
- clusters = group.clusters.list()
- assert len(clusters) == 1
-
- cluster = clusters[0]
- cluster.platform_kubernetes_attributes = {"api_url": "http://newurl"}
- cluster.save()
-
- cluster = group.clusters.list()[0]
- assert cluster.platform_kubernetes["api_url"] == "http://newurl"
-
- cluster.delete()
- assert len(group.clusters.list()) == 0
diff --git a/tests/functional/api/test_current_user.py b/tests/functional/api/test_current_user.py
deleted file mode 100644
index 5802457..0000000
--- a/tests/functional/api/test_current_user.py
+++ /dev/null
@@ -1,42 +0,0 @@
-def test_current_user_email(gl):
- gl.auth()
- mail = gl.user.emails.create({"email": "current@user.com"})
- assert len(gl.user.emails.list()) == 1
-
- mail.delete()
- assert len(gl.user.emails.list()) == 0
-
-
-def test_current_user_gpg_keys(gl, GPG_KEY):
- gl.auth()
- gkey = gl.user.gpgkeys.create({"key": GPG_KEY})
- assert len(gl.user.gpgkeys.list()) == 1
-
- # Seems broken on the gitlab side
- gkey = gl.user.gpgkeys.get(gkey.id)
- gkey.delete()
- assert len(gl.user.gpgkeys.list()) == 0
-
-
-def test_current_user_ssh_keys(gl, SSH_KEY):
- gl.auth()
- key = gl.user.keys.create({"title": "testkey", "key": SSH_KEY})
- assert len(gl.user.keys.list()) == 1
-
- key.delete()
- assert len(gl.user.keys.list()) == 0
-
-
-def test_current_user_status(gl):
- gl.auth()
- message = "Test"
- emoji = "thumbsup"
- status = gl.user.status.get()
-
- status.message = message
- status.emoji = emoji
- status.save()
-
- new_status = gl.user.status.get()
- assert new_status.message == message
- assert new_status.emoji == emoji
diff --git a/tests/functional/api/test_deploy_keys.py b/tests/functional/api/test_deploy_keys.py
deleted file mode 100644
index 18828a2..0000000
--- a/tests/functional/api/test_deploy_keys.py
+++ /dev/null
@@ -1,12 +0,0 @@
-def test_project_deploy_keys(gl, project, DEPLOY_KEY):
- deploy_key = project.keys.create({"title": "foo@bar", "key": DEPLOY_KEY})
- project_keys = list(project.keys.list())
- assert len(project_keys) == 1
-
- project2 = gl.projects.create({"name": "deploy-key-project"})
- project2.keys.enable(deploy_key.id)
- assert len(project2.keys.list()) == 1
-
- project2.keys.delete(deploy_key.id)
- assert len(project2.keys.list()) == 0
- project2.delete()
diff --git a/tests/functional/api/test_deploy_tokens.py b/tests/functional/api/test_deploy_tokens.py
deleted file mode 100644
index efcf8b1..0000000
--- a/tests/functional/api/test_deploy_tokens.py
+++ /dev/null
@@ -1,36 +0,0 @@
-def test_project_deploy_tokens(gl, project):
- deploy_token = project.deploytokens.create(
- {
- "name": "foo",
- "username": "bar",
- "expires_at": "2022-01-01",
- "scopes": ["read_registry"],
- }
- )
- assert len(project.deploytokens.list()) == 1
- assert gl.deploytokens.list() == project.deploytokens.list()
-
- assert project.deploytokens.list()[0].name == "foo"
- assert project.deploytokens.list()[0].expires_at == "2022-01-01T00:00:00.000Z"
- assert project.deploytokens.list()[0].scopes == ["read_registry"]
- assert project.deploytokens.list()[0].username == "bar"
-
- deploy_token.delete()
- assert len(project.deploytokens.list()) == 0
- assert len(gl.deploytokens.list()) == 0
-
-
-def test_group_deploy_tokens(gl, group):
- deploy_token = group.deploytokens.create(
- {
- "name": "foo",
- "scopes": ["read_registry"],
- }
- )
-
- assert len(group.deploytokens.list()) == 1
- assert gl.deploytokens.list() == group.deploytokens.list()
-
- deploy_token.delete()
- assert len(group.deploytokens.list()) == 0
- assert len(gl.deploytokens.list()) == 0
diff --git a/tests/functional/api/test_gitlab.py b/tests/functional/api/test_gitlab.py
deleted file mode 100644
index 7a70a56..0000000
--- a/tests/functional/api/test_gitlab.py
+++ /dev/null
@@ -1,183 +0,0 @@
-import pytest
-
-import gitlab
-
-
-def test_auth_from_config(gl, temp_dir):
- """Test token authentication from config file"""
- test_gitlab = gitlab.Gitlab.from_config(
- config_files=[temp_dir / "python-gitlab.cfg"]
- )
- test_gitlab.auth()
- assert isinstance(test_gitlab.user, gitlab.v4.objects.CurrentUser)
-
-
-def test_broadcast_messages(gl):
- msg = gl.broadcastmessages.create({"message": "this is the message"})
- msg.color = "#444444"
- msg.save()
- msg_id = msg.id
-
- msg = gl.broadcastmessages.list(all=True)[0]
- assert msg.color == "#444444"
-
- msg = gl.broadcastmessages.get(msg_id)
- assert msg.color == "#444444"
-
- msg.delete()
- assert len(gl.broadcastmessages.list()) == 0
-
-
-def test_markdown(gl):
- html = gl.markdown("foo")
- assert "foo" in html
-
-
-def test_lint(gl):
- success, errors = gl.lint("Invalid")
- assert success is False
- assert errors
-
-
-def test_sidekiq_queue_metrics(gl):
- out = gl.sidekiq.queue_metrics()
- assert isinstance(out, dict)
- assert "pages" in out["queues"]
-
-
-def test_sidekiq_process_metrics(gl):
- out = gl.sidekiq.process_metrics()
- assert isinstance(out, dict)
- assert "hostname" in out["processes"][0]
-
-
-def test_sidekiq_job_stats(gl):
- out = gl.sidekiq.job_stats()
- assert isinstance(out, dict)
- assert "processed" in out["jobs"]
-
-
-def test_sidekiq_compound_metrics(gl):
- out = gl.sidekiq.compound_metrics()
- assert isinstance(out, dict)
- assert "jobs" in out
- assert "processes" in out
- assert "queues" in out
-
-
-def test_gitlab_settings(gl):
- settings = gl.settings.get()
- settings.default_projects_limit = 42
- settings.save()
- settings = gl.settings.get()
- assert settings.default_projects_limit == 42
-
-
-def test_template_dockerfile(gl):
- assert gl.dockerfiles.list()
-
- dockerfile = gl.dockerfiles.get("Node")
- assert dockerfile.content is not None
-
-
-def test_template_gitignore(gl):
- assert gl.gitignores.list()
- gitignore = gl.gitignores.get("Node")
- assert gitignore.content is not None
-
-
-def test_template_gitlabciyml(gl):
- assert gl.gitlabciymls.list()
- gitlabciyml = gl.gitlabciymls.get("Nodejs")
- assert gitlabciyml.content is not None
-
-
-def test_template_license(gl):
- assert gl.licenses.list()
- license = gl.licenses.get(
- "bsd-2-clause", project="mytestproject", fullname="mytestfullname"
- )
- assert "mytestfullname" in license.content
-
-
-def test_hooks(gl):
- hook = gl.hooks.create({"url": "http://whatever.com"})
- assert len(gl.hooks.list()) == 1
-
- hook.delete()
- assert len(gl.hooks.list()) == 0
-
-
-def test_namespaces(gl):
- namespace = gl.namespaces.list(all=True)
- assert namespace
-
- namespace = gl.namespaces.list(search="root", all=True)[0]
- assert namespace.kind == "user"
-
-
-def test_notification_settings(gl):
- settings = gl.notificationsettings.get()
- settings.level = gitlab.NOTIFICATION_LEVEL_WATCH
- settings.save()
-
- settings = gl.notificationsettings.get()
- assert settings.level == gitlab.NOTIFICATION_LEVEL_WATCH
-
-
-def test_user_activities(gl):
- activities = gl.user_activities.list(query_parameters={"from": "2019-01-01"})
- assert isinstance(activities, list)
-
-
-def test_events(gl):
- events = gl.events.list()
- assert isinstance(events, list)
-
-
-@pytest.mark.skip
-def test_features(gl):
- feat = gl.features.set("foo", 30)
- assert feat.name == "foo"
- assert len(gl.features.list()) == 1
-
- feat.delete()
- assert len(gl.features.list()) == 0
-
-
-def test_pagination(gl, project):
- project2 = gl.projects.create({"name": "project-page-2"})
-
- list1 = gl.projects.list(per_page=1, page=1)
- list2 = gl.projects.list(per_page=1, page=2)
- assert len(list1) == 1
- assert len(list2) == 1
- assert list1[0].id != list2[0].id
-
- project2.delete()
-
-
-def test_rate_limits(gl):
- settings = gl.settings.get()
- settings.throttle_authenticated_api_enabled = True
- settings.throttle_authenticated_api_requests_per_period = 1
- settings.throttle_authenticated_api_period_in_seconds = 3
- settings.save()
-
- projects = list()
- for i in range(0, 20):
- projects.append(gl.projects.create({"name": str(i) + "ok"}))
-
- with pytest.raises(gitlab.GitlabCreateError) as e:
- for i in range(20, 40):
- projects.append(
- gl.projects.create(
- {"name": str(i) + "shouldfail"}, obey_rate_limit=False
- )
- )
-
- assert "Retry later" in str(e.value)
-
- settings.throttle_authenticated_api_enabled = False
- settings.save()
- [project.delete() for project in projects]
diff --git a/tests/functional/api/test_groups.py b/tests/functional/api/test_groups.py
deleted file mode 100644
index 665c933..0000000
--- a/tests/functional/api/test_groups.py
+++ /dev/null
@@ -1,223 +0,0 @@
-import pytest
-
-import gitlab
-
-
-def test_groups(gl):
- # TODO: This one still needs lots of work
- user = gl.users.create(
- {
- "email": "user@test.com",
- "username": "user",
- "name": "user",
- "password": "user_pass",
- }
- )
- user2 = gl.users.create(
- {
- "email": "user2@test.com",
- "username": "user2",
- "name": "user2",
- "password": "user2_pass",
- }
- )
- group1 = gl.groups.create({"name": "group1", "path": "group1"})
- group2 = gl.groups.create({"name": "group2", "path": "group2"})
-
- p_id = gl.groups.list(search="group2")[0].id
- group3 = gl.groups.create({"name": "group3", "path": "group3", "parent_id": p_id})
- group4 = gl.groups.create({"name": "group4", "path": "group4"})
-
- assert len(gl.groups.list()) == 4
- assert len(gl.groups.list(search="oup1")) == 1
- assert group3.parent_id == p_id
- assert group2.subgroups.list()[0].id == group3.id
- assert group2.descendant_groups.list()[0].id == group3.id
-
- filtered_groups = gl.groups.list(skip_groups=[group3.id, group4.id])
- assert group3 not in filtered_groups
- assert group3 not in filtered_groups
-
- group1.members.create(
- {"access_level": gitlab.const.OWNER_ACCESS, "user_id": user.id}
- )
- group1.members.create(
- {"access_level": gitlab.const.GUEST_ACCESS, "user_id": user2.id}
- )
- group2.members.create(
- {"access_level": gitlab.const.OWNER_ACCESS, "user_id": user2.id}
- )
-
- group4.share(group1.id, gitlab.const.DEVELOPER_ACCESS)
- group4.share(group2.id, gitlab.const.MAINTAINER_ACCESS)
- # Reload group4 to have updated shared_with_groups
- group4 = gl.groups.get(group4.id)
- assert len(group4.shared_with_groups) == 2
- group4.unshare(group1.id)
- # Reload group4 to have updated shared_with_groups
- group4 = gl.groups.get(group4.id)
- assert len(group4.shared_with_groups) == 1
-
- # User memberships (admin only)
- memberships1 = user.memberships.list()
- assert len(memberships1) == 1
-
- memberships2 = user2.memberships.list()
- assert len(memberships2) == 2
-
- membership = memberships1[0]
- assert membership.source_type == "Namespace"
- assert membership.access_level == gitlab.const.OWNER_ACCESS
-
- project_memberships = user.memberships.list(type="Project")
- assert len(project_memberships) == 0
-
- group_memberships = user.memberships.list(type="Namespace")
- assert len(group_memberships) == 1
-
- with pytest.raises(gitlab.GitlabListError) as e:
- membership = user.memberships.list(type="Invalid")
- assert "type does not have a valid value" in str(e.value)
-
- with pytest.raises(gitlab.GitlabListError) as e:
- user.memberships.list(sudo=user.name)
- assert "403 Forbidden" in str(e.value)
-
- # Administrator belongs to the groups
- assert len(group1.members.list()) == 3
- assert len(group2.members.list()) == 2
-
- group1.members.delete(user.id)
- assert len(group1.members.list()) == 2
- assert len(group1.members_all.list())
- member = group1.members.get(user2.id)
- member.access_level = gitlab.const.OWNER_ACCESS
- member.save()
- member = group1.members.get(user2.id)
- assert member.access_level == gitlab.const.OWNER_ACCESS
-
- group2.members.delete(gl.user.id)
-
-
-@pytest.mark.skip(reason="Commented out in legacy test")
-def test_group_labels(group):
- group.labels.create({"name": "foo", "description": "bar", "color": "#112233"})
- label = group.labels.get("foo")
- assert label.description == "bar"
-
- label.description = "baz"
- label.save()
- label = group.labels.get("foo")
- assert label.description == "baz"
- assert len(group.labels.list()) == 1
-
- label.delete()
- assert len(group.labels.list()) == 0
-
-
-def test_group_notification_settings(group):
- settings = group.notificationsettings.get()
- settings.level = "disabled"
- settings.save()
-
- settings = group.notificationsettings.get()
- assert settings.level == "disabled"
-
-
-def test_group_badges(group):
- badge_image = "http://example.com"
- badge_link = "http://example/img.svg"
- badge = group.badges.create({"link_url": badge_link, "image_url": badge_image})
- assert len(group.badges.list()) == 1
-
- badge.image_url = "http://another.example.com"
- badge.save()
-
- badge = group.badges.get(badge.id)
- assert badge.image_url == "http://another.example.com"
-
- badge.delete()
- assert len(group.badges.list()) == 0
-
-
-def test_group_milestones(group):
- milestone = group.milestones.create({"title": "groupmilestone1"})
- assert len(group.milestones.list()) == 1
-
- milestone.due_date = "2020-01-01T00:00:00Z"
- milestone.save()
- milestone.state_event = "close"
- milestone.save()
-
- milestone = group.milestones.get(milestone.id)
- assert milestone.state == "closed"
- assert len(milestone.issues()) == 0
- assert len(milestone.merge_requests()) == 0
-
-
-def test_group_custom_attributes(gl, group):
- attrs = group.customattributes.list()
- assert len(attrs) == 0
-
- attr = group.customattributes.set("key", "value1")
- assert len(gl.groups.list(custom_attributes={"key": "value1"})) == 1
- assert attr.key == "key"
- assert attr.value == "value1"
- assert len(group.customattributes.list()) == 1
-
- attr = group.customattributes.set("key", "value2")
- attr = group.customattributes.get("key")
- assert attr.value == "value2"
- assert len(group.customattributes.list()) == 1
-
- attr.delete()
- assert len(group.customattributes.list()) == 0
-
-
-def test_group_subgroups_projects(gl, user):
- # TODO: fixture factories
- group1 = gl.groups.list(search="group1")[0]
- group2 = gl.groups.list(search="group2")[0]
-
- group3 = gl.groups.create(
- {"name": "subgroup1", "path": "subgroup1", "parent_id": group1.id}
- )
- group4 = gl.groups.create(
- {"name": "subgroup2", "path": "subgroup2", "parent_id": group2.id}
- )
-
- gr1_project = gl.projects.create({"name": "gr1_project", "namespace_id": group1.id})
- gr2_project = gl.projects.create({"name": "gr2_project", "namespace_id": group3.id})
-
- assert group3.parent_id == group1.id
- assert group4.parent_id == group2.id
- assert gr1_project.namespace["id"] == group1.id
- assert gr2_project.namespace["parent_id"] == group1.id
-
-
-@pytest.mark.skip
-def test_group_wiki(group):
- content = "Group Wiki page content"
- wiki = group.wikis.create({"title": "groupwikipage", "content": content})
- assert len(group.wikis.list()) == 1
-
- wiki = group.wikis.get(wiki.slug)
- assert wiki.content == content
-
- wiki.content = "new content"
- wiki.save()
- wiki.delete()
- assert len(group.wikis.list()) == 0
-
-
-@pytest.mark.skip(reason="EE feature")
-def test_group_hooks(group):
- hook = group.hooks.create({"url": "http://hook.url"})
- assert len(group.hooks.list()) == 1
-
- hook.note_events = True
- hook.save()
-
- hook = group.hooks.get(hook.id)
- assert hook.note_events is True
- hook.delete()
diff --git a/tests/functional/api/test_import_export.py b/tests/functional/api/test_import_export.py
deleted file mode 100644
index d4bdd19..0000000
--- a/tests/functional/api/test_import_export.py
+++ /dev/null
@@ -1,66 +0,0 @@
-import time
-
-import gitlab
-
-
-def test_group_import_export(gl, group, temp_dir):
- export = group.exports.create()
- assert export.message == "202 Accepted"
-
- # We cannot check for export_status with group export API
- time.sleep(10)
-
- import_archive = temp_dir / "gitlab-group-export.tgz"
- import_path = "imported_group"
- import_name = "Imported Group"
-
- with open(import_archive, "wb") as f:
- export.download(streamed=True, action=f.write)
-
- with open(import_archive, "rb") as f:
- output = gl.groups.import_group(f, import_path, import_name)
- assert output["message"] == "202 Accepted"
-
- # We cannot check for returned ID with group import API
- time.sleep(10)
- group_import = gl.groups.get(import_path)
-
- assert group_import.path == import_path
- assert group_import.name == import_name
-
-
-def test_project_import_export(gl, project, temp_dir):
- export = project.exports.create()
- assert export.message == "202 Accepted"
-
- export = project.exports.get()
- assert isinstance(export, gitlab.v4.objects.ProjectExport)
-
- count = 0
- while export.export_status != "finished":
- time.sleep(1)
- export.refresh()
- count += 1
- if count == 15:
- raise Exception("Project export taking too much time")
-
- with open(temp_dir / "gitlab-export.tgz", "wb") as f:
- export.download(streamed=True, action=f.write)
-
- output = gl.projects.import_project(
- open(temp_dir / "gitlab-export.tgz", "rb"),
- "imported_project",
- name="Imported Project",
- )
- project_import = gl.projects.get(output["id"], lazy=True).imports.get()
-
- assert project_import.path == "imported_project"
- assert project_import.name == "Imported Project"
-
- count = 0
- while project_import.import_status != "finished":
- time.sleep(1)
- project_import.refresh()
- count += 1
- if count == 15:
- raise Exception("Project import taking too much time")
diff --git a/tests/functional/api/test_issues.py b/tests/functional/api/test_issues.py
deleted file mode 100644
index 64db46e..0000000
--- a/tests/functional/api/test_issues.py
+++ /dev/null
@@ -1,93 +0,0 @@
-import gitlab
-
-
-def test_create_issue(project):
- issue = project.issues.create({"title": "my issue 1"})
- issue2 = project.issues.create({"title": "my issue 2"})
- issue_iids = [issue.iid for issue in project.issues.list()]
- assert len(issue_iids) == 2
-
- # Test 'iids' as a list
- assert len(project.issues.list(iids=issue_iids)) == 2
-
- issue2.state_event = "close"
- issue2.save()
- assert len(project.issues.list(state="closed")) == 1
- assert len(project.issues.list(state="opened")) == 1
-
- assert isinstance(issue.user_agent_detail(), dict)
- assert issue.user_agent_detail()["user_agent"]
- assert issue.participants()
- assert type(issue.closed_by()) == list
- assert type(issue.related_merge_requests()) == list
-
-
-def test_issue_notes(issue):
- size = len(issue.notes.list())
-
- note = issue.notes.create({"body": "This is an issue note"})
- assert len(issue.notes.list()) == size + 1
-
- emoji = note.awardemojis.create({"name": "tractor"})
- assert len(note.awardemojis.list()) == 1
-
- emoji.delete()
- assert len(note.awardemojis.list()) == 0
-
- note.delete()
- assert len(issue.notes.list()) == size
-
-
-def test_issue_labels(project, issue):
- project.labels.create({"name": "label2", "color": "#aabbcc"})
- issue.labels = ["label2"]
- issue.save()
-
- assert issue in project.issues.list(labels=["label2"])
- assert issue in project.issues.list(labels="label2")
- assert issue in project.issues.list(labels="Any")
- assert issue not in project.issues.list(labels="None")
-
-
-def test_issue_events(issue):
- events = issue.resourcelabelevents.list()
- assert isinstance(events, list)
-
- event = issue.resourcelabelevents.get(events[0].id)
- assert isinstance(event, gitlab.v4.objects.ProjectIssueResourceLabelEvent)
-
-
-def test_issue_milestones(project, milestone):
- data = {"title": "my issue 1", "milestone_id": milestone.id}
- issue = project.issues.create(data)
- assert milestone.issues().next().title == "my issue 1"
-
- milestone_events = issue.resourcemilestoneevents.list()
- assert isinstance(milestone_events, list)
-
- milestone_event = issue.resourcemilestoneevents.get(milestone_events[0].id)
- assert isinstance(
- milestone_event, gitlab.v4.objects.ProjectIssueResourceMilestoneEvent
- )
-
- milestone_issues = project.issues.list(milestone=milestone.title)
- assert len(milestone_issues) == 1
-
-
-def test_issue_discussions(issue):
- size = len(issue.discussions.list())
-
- discussion = issue.discussions.create({"body": "Discussion body"})
- assert len(issue.discussions.list()) == size + 1
-
- d_note = discussion.notes.create({"body": "first note"})
- d_note_from_get = discussion.notes.get(d_note.id)
- d_note_from_get.body = "updated body"
- d_note_from_get.save()
-
- discussion = issue.discussions.get(discussion.id)
- assert discussion.attributes["notes"][-1]["body"] == "updated body"
-
- d_note_from_get.delete()
- discussion = issue.discussions.get(discussion.id)
- assert len(discussion.attributes["notes"]) == 1
diff --git a/tests/functional/api/test_keys.py b/tests/functional/api/test_keys.py
deleted file mode 100644
index 82a75e5..0000000
--- a/tests/functional/api/test_keys.py
+++ /dev/null
@@ -1,42 +0,0 @@
-"""
-GitLab API:
-https://docs.gitlab.com/ce/api/keys.html
-"""
-import base64
-import hashlib
-
-
-def key_fingerprint(key):
- key_part = key.split()[1]
- decoded = base64.b64decode(key_part.encode("ascii"))
- digest = hashlib.sha256(decoded).digest()
- return "SHA256:" + base64.b64encode(digest).rstrip(b"=").decode("utf-8")
-
-
-def test_keys_ssh(gl, user, SSH_KEY):
- key = user.keys.create({"title": "foo@bar", "key": SSH_KEY})
-
- # Get key by ID (admin only).
- key_by_id = gl.keys.get(key.id)
- assert key_by_id.title == key.title
- assert key_by_id.key == key.key
-
- fingerprint = key_fingerprint(SSH_KEY)
- # Get key by fingerprint (admin only).
- key_by_fingerprint = gl.keys.get(fingerprint=fingerprint)
- assert key_by_fingerprint.title == key.title
- assert key_by_fingerprint.key == key.key
-
- key.delete()
-
-
-def test_keys_deploy(gl, project, DEPLOY_KEY):
- key = project.keys.create({"title": "foo@bar", "key": DEPLOY_KEY})
-
- fingerprint = key_fingerprint(DEPLOY_KEY)
- key_by_fingerprint = gl.keys.get(fingerprint=fingerprint)
- assert key_by_fingerprint.title == key.title
- assert key_by_fingerprint.key == key.key
- assert len(key_by_fingerprint.deploy_keys_projects) == 1
-
- key.delete()
diff --git a/tests/functional/api/test_merge_requests.py b/tests/functional/api/test_merge_requests.py
deleted file mode 100644
index b20b66a..0000000
--- a/tests/functional/api/test_merge_requests.py
+++ /dev/null
@@ -1,205 +0,0 @@
-import time
-
-import pytest
-
-import gitlab
-import gitlab.v4.objects
-
-
-def test_merge_requests(project):
- project.files.create(
- {
- "file_path": "README.rst",
- "branch": "master",
- "content": "Initial content",
- "commit_message": "Initial commit",
- }
- )
-
- source_branch = "branch1"
- project.branches.create({"branch": source_branch, "ref": "master"})
-
- project.files.create(
- {
- "file_path": "README2.rst",
- "branch": source_branch,
- "content": "Initial content",
- "commit_message": "New commit in new branch",
- }
- )
- project.mergerequests.create(
- {"source_branch": "branch1", "target_branch": "master", "title": "MR readme2"}
- )
-
-
-def test_merge_request_discussion(project):
- mr = project.mergerequests.list()[0]
- size = len(mr.discussions.list())
-
- discussion = mr.discussions.create({"body": "Discussion body"})
- assert len(mr.discussions.list()) == size + 1
-
- note = discussion.notes.create({"body": "first note"})
- note_from_get = discussion.notes.get(note.id)
- note_from_get.body = "updated body"
- note_from_get.save()
-
- discussion = mr.discussions.get(discussion.id)
- assert discussion.attributes["notes"][-1]["body"] == "updated body"
-
- note_from_get.delete()
- discussion = mr.discussions.get(discussion.id)
- assert len(discussion.attributes["notes"]) == 1
-
-
-def test_merge_request_labels(project):
- mr = project.mergerequests.list()[0]
- mr.labels = ["label2"]
- mr.save()
-
- events = mr.resourcelabelevents.list()
- assert events
-
- event = mr.resourcelabelevents.get(events[0].id)
- assert event
-
-
-def test_merge_request_milestone_events(project, milestone):
- mr = project.mergerequests.list()[0]
- mr.milestone_id = milestone.id
- mr.save()
-
- milestones = mr.resourcemilestoneevents.list()
- assert milestones
-
- milestone = mr.resourcemilestoneevents.get(milestones[0].id)
- assert milestone
-
-
-def test_merge_request_basic(project):
- mr = project.mergerequests.list()[0]
- # basic testing: only make sure that the methods exist
- mr.commits()
- mr.changes()
- assert mr.participants()
-
-
-def test_merge_request_rebase(project):
- mr = project.mergerequests.list()[0]
- assert mr.rebase()
-
-
-@pytest.mark.skip(reason="flaky test")
-def test_merge_request_merge(project):
- mr = project.mergerequests.list()[0]
- mr.merge()
- project.branches.delete(mr.source_branch)
-
- with pytest.raises(gitlab.GitlabMRClosedError):
- # Two merge attempts should raise GitlabMRClosedError
- mr.merge()
-
-
-def test_merge_request_should_remove_source_branch(
- project, merge_request, wait_for_sidekiq
-) -> None:
- """Test to ensure
- https://github.com/python-gitlab/python-gitlab/issues/1120 is fixed.
- Bug reported that they could not use 'should_remove_source_branch' in
- mr.merge() call"""
-
- source_branch = "remove_source_branch"
- mr = merge_request(source_branch=source_branch)
-
- mr.merge(should_remove_source_branch=True)
-
- result = wait_for_sidekiq(timeout=60)
- assert result is True, "sidekiq process should have terminated but did not"
-
- # Wait until it is merged
- mr_iid = mr.iid
- for _ in range(60):
- mr = project.mergerequests.get(mr_iid)
- if mr.merged_at is not None:
- break
- time.sleep(0.5)
- assert mr.merged_at is not None
- time.sleep(0.5)
- result = wait_for_sidekiq(timeout=60)
- assert result is True, "sidekiq process should have terminated but did not"
-
- # Ensure we can NOT get the MR branch
- with pytest.raises(gitlab.exceptions.GitlabGetError):
- result = project.branches.get(source_branch)
- # Help to debug in case the expected exception doesn't happen.
- import pprint
-
- print("mr:", pprint.pformat(mr))
- print("mr.merged_at:", pprint.pformat(mr.merged_at))
- print("result:", pprint.pformat(result))
-
-
-def test_merge_request_large_commit_message(
- project, merge_request, wait_for_sidekiq
-) -> None:
- """Test to ensure https://github.com/python-gitlab/python-gitlab/issues/1452
- is fixed.
- Bug reported that very long 'merge_commit_message' in mr.merge() would
- cause an error: 414 Request too large
- """
-
- source_branch = "large_commit_message"
- mr = merge_request(source_branch=source_branch)
-
- merge_commit_message = "large_message\r\n" * 1_000
- assert len(merge_commit_message) > 10_000
-
- mr.merge(merge_commit_message=merge_commit_message)
-
- result = wait_for_sidekiq(timeout=60)
- assert result is True, "sidekiq process should have terminated but did not"
-
- # Wait until it is merged
- mr_iid = mr.iid
- for _ in range(60):
- mr = project.mergerequests.get(mr_iid)
- if mr.merged_at is not None:
- break
- time.sleep(0.5)
- assert mr.merged_at is not None
- time.sleep(0.5)
-
- # Ensure we can get the MR branch
- project.branches.get(source_branch)
-
-
-def test_merge_request_merge_ref(merge_request) -> None:
- source_branch = "merge_ref_test"
- mr = merge_request(source_branch=source_branch)
-
- response = mr.merge_ref()
- assert response and "commit_id" in response
-
-
-def test_merge_request_merge_ref_should_fail(
- project, merge_request, wait_for_sidekiq
-) -> None:
- source_branch = "merge_ref_test2"
- mr = merge_request(source_branch=source_branch)
-
- # Create conflict
- project.files.create(
- {
- "file_path": f"README.{source_branch}",
- "branch": project.default_branch,
- "content": "Different initial content",
- "commit_message": "Another commit in main branch",
- }
- )
- result = wait_for_sidekiq(timeout=60)
- assert result is True, "sidekiq process should have terminated but did not"
-
- # Check for non-existing merge_ref for MR with conflicts
- with pytest.raises(gitlab.exceptions.GitlabGetError):
- response = mr.merge_ref()
- assert "commit_id" not in response
diff --git a/tests/functional/api/test_packages.py b/tests/functional/api/test_packages.py
deleted file mode 100644
index 64b57b8..0000000
--- a/tests/functional/api/test_packages.py
+++ /dev/null
@@ -1,62 +0,0 @@
-"""
-GitLab API:
-https://docs.gitlab.com/ce/api/packages.html
-https://docs.gitlab.com/ee/user/packages/generic_packages
-"""
-from gitlab.v4.objects import GenericPackage
-
-package_name = "hello-world"
-package_version = "v1.0.0"
-file_name = "hello.tar.gz"
-file_content = "package content"
-
-
-def test_list_project_packages(project):
- packages = project.packages.list()
- assert isinstance(packages, list)
-
-
-def test_list_group_packages(group):
- packages = group.packages.list()
- assert isinstance(packages, list)
-
-
-def test_upload_generic_package(tmp_path, project):
- path = tmp_path / file_name
- path.write_text(file_content)
- package = project.generic_packages.upload(
- package_name=package_name,
- package_version=package_version,
- file_name=file_name,
- path=path,
- )
-
- assert isinstance(package, GenericPackage)
- assert package.message == "201 Created"
-
-
-def test_download_generic_package(project):
- package = project.generic_packages.download(
- package_name=package_name,
- package_version=package_version,
- file_name=file_name,
- )
-
- assert isinstance(package, bytes)
- assert package.decode("utf-8") == file_content
-
-
-def test_download_generic_package_to_file(tmp_path, project):
- path = tmp_path / file_name
-
- with open(path, "wb") as f:
- project.generic_packages.download(
- package_name=package_name,
- package_version=package_version,
- file_name=file_name,
- streamed=True,
- action=f.write,
- )
-
- with open(path, "r") as f:
- assert f.read() == file_content
diff --git a/tests/functional/api/test_projects.py b/tests/functional/api/test_projects.py
deleted file mode 100644
index 88b274c..0000000
--- a/tests/functional/api/test_projects.py
+++ /dev/null
@@ -1,268 +0,0 @@
-import pytest
-
-import gitlab
-
-
-def test_create_project(gl, user):
- # Moved from group tests chunk in legacy tests, TODO cleanup
- admin_project = gl.projects.create({"name": "admin_project"})
- assert isinstance(admin_project, gitlab.v4.objects.Project)
- assert len(gl.projects.list(search="admin")) == 1
-
- sudo_project = gl.projects.create({"name": "sudo_project"}, sudo=user.id)
-
- created = gl.projects.list()
- created_gen = gl.projects.list(as_list=False)
- owned = gl.projects.list(owned=True)
-
- assert admin_project in created and sudo_project in created
- assert admin_project in owned and sudo_project not in owned
- assert len(created) == len(list(created_gen))
-
- admin_project.delete()
- sudo_project.delete()
-
-
-def test_project_badges(project):
- badge_image = "http://example.com"
- badge_link = "http://example/img.svg"
-
- badge = project.badges.create({"link_url": badge_link, "image_url": badge_image})
- assert len(project.badges.list()) == 1
-
- badge.image_url = "http://another.example.com"
- badge.save()
-
- badge = project.badges.get(badge.id)
- assert badge.image_url == "http://another.example.com"
-
- badge.delete()
- assert len(project.badges.list()) == 0
-
-
-@pytest.mark.skip(reason="Commented out in legacy test")
-def test_project_boards(project):
- boards = project.boards.list()
- assert len(boards)
-
- board = boards[0]
- lists = board.lists.list()
- begin_size = len(lists)
- last_list = lists[-1]
- last_list.position = 0
- last_list.save()
- last_list.delete()
- lists = board.lists.list()
- assert len(lists) == begin_size - 1
-
-
-def test_project_custom_attributes(gl, project):
- attrs = project.customattributes.list()
- assert len(attrs) == 0
-
- attr = project.customattributes.set("key", "value1")
- assert attr.key == "key"
- assert attr.value == "value1"
- assert len(project.customattributes.list()) == 1
- assert len(gl.projects.list(custom_attributes={"key": "value1"})) == 1
-
- attr = project.customattributes.set("key", "value2")
- attr = project.customattributes.get("key")
- assert attr.value == "value2"
- assert len(project.customattributes.list()) == 1
-
- attr.delete()
- assert len(project.customattributes.list()) == 0
-
-
-def test_project_environments(project):
- project.environments.create(
- {"name": "env1", "external_url": "http://fake.env/whatever"}
- )
- environments = project.environments.list()
- assert len(environments) == 1
-
- environment = environments[0]
- environment.external_url = "http://new.env/whatever"
- environment.save()
-
- environment = project.environments.list()[0]
- assert environment.external_url == "http://new.env/whatever"
-
- environment.stop()
- environment.delete()
- assert len(project.environments.list()) == 0
-
-
-def test_project_events(project):
- events = project.events.list()
- assert isinstance(events, list)
-
-
-def test_project_file_uploads(project):
- filename = "test.txt"
- file_contents = "testing contents"
-
- uploaded_file = project.upload(filename, file_contents)
- assert uploaded_file["alt"] == filename
- assert uploaded_file["url"].startswith("/uploads/")
- assert uploaded_file["url"].endswith("/" + filename)
- assert uploaded_file["markdown"] == "[{}]({})".format(
- uploaded_file["alt"], uploaded_file["url"]
- )
-
-
-def test_project_forks(gl, project, user):
- fork = project.forks.create({"namespace": user.username})
- fork_project = gl.projects.get(fork.id)
- assert fork_project.forked_from_project["id"] == project.id
-
- forks = project.forks.list()
- assert fork.id in map(lambda fork_project: fork_project.id, forks)
-
-
-def test_project_hooks(project):
- hook = project.hooks.create({"url": "http://hook.url"})
- assert len(project.hooks.list()) == 1
-
- hook.note_events = True
- hook.save()
-
- hook = project.hooks.get(hook.id)
- assert hook.note_events is True
- hook.delete()
-
-
-def test_project_housekeeping(project):
- project.housekeeping()
-
-
-def test_project_labels(project):
- label = project.labels.create({"name": "label", "color": "#778899"})
- labels = project.labels.list()
- assert len(labels) == 1
-
- label = project.labels.get("label")
- assert label == labels[0]
-
- label.new_name = "labelupdated"
- label.save()
- assert label.name == "labelupdated"
-
- label.subscribe()
- assert label.subscribed is True
-
- label.unsubscribe()
- assert label.subscribed is False
-
- label.delete()
- assert len(project.labels.list()) == 0
-
-
-def test_project_milestones(project):
- milestone = project.milestones.create({"title": "milestone1"})
- assert len(project.milestones.list()) == 1
-
- milestone.due_date = "2020-01-01T00:00:00Z"
- milestone.save()
-
- milestone.state_event = "close"
- milestone.save()
-
- milestone = project.milestones.get(milestone.id)
- assert milestone.state == "closed"
- assert len(milestone.issues()) == 0
- assert len(milestone.merge_requests()) == 0
-
-
-def test_project_pages_domains(gl, project):
- domain = project.pagesdomains.create({"domain": "foo.domain.com"})
- assert len(project.pagesdomains.list()) == 1
- assert len(gl.pagesdomains.list()) == 1
-
- domain = project.pagesdomains.get("foo.domain.com")
- assert domain.domain == "foo.domain.com"
-
- domain.delete()
- assert len(project.pagesdomains.list()) == 0
-
-
-def test_project_protected_branches(project):
- p_b = project.protectedbranches.create({"name": "*-stable"})
- assert p_b.name == "*-stable"
- assert len(project.protectedbranches.list()) == 1
-
- p_b = project.protectedbranches.get("*-stable")
- p_b.delete()
- assert len(project.protectedbranches.list()) == 0
-
-
-def test_project_remote_mirrors(project):
- mirror_url = "http://gitlab.test/root/mirror.git"
-
- mirror = project.remote_mirrors.create({"url": mirror_url})
- assert mirror.url == mirror_url
-
- mirror.enabled = True
- mirror.save()
-
- mirror = project.remote_mirrors.list()[0]
- assert isinstance(mirror, gitlab.v4.objects.ProjectRemoteMirror)
- assert mirror.url == mirror_url
- assert mirror.enabled is True
-
-
-def test_project_services(project):
- # Use 'update' to create a service as we don't have a 'create' method and
- # to add one is somewhat complicated so it hasn't been done yet.
- project.services.update("asana", api_key="foo")
-
- service = project.services.get("asana")
- assert service.active is True
- service.api_key = "whatever"
- service.save()
-
- service = project.services.get("asana")
- assert service.active is True
-
- service.delete()
-
- service = project.services.get("asana")
- assert service.active is False
-
-
-def test_project_stars(project):
- project.star()
- assert project.star_count == 1
-
- project.unstar()
- assert project.star_count == 0
-
-
-def test_project_tags(project, project_file):
- tag = project.tags.create({"tag_name": "v1.0", "ref": "master"})
- assert len(project.tags.list()) == 1
-
- tag.delete()
- assert len(project.tags.list()) == 0
-
-
-def test_project_triggers(project):
- trigger = project.triggers.create({"description": "trigger1"})
- assert len(project.triggers.list()) == 1
- trigger.delete()
-
-
-def test_project_wiki(project):
- content = "Wiki page content"
- wiki = project.wikis.create({"title": "wikipage", "content": content})
- assert len(project.wikis.list()) == 1
-
- wiki = project.wikis.get(wiki.slug)
- assert wiki.content == content
-
- # update and delete seem broken
- wiki.content = "new content"
- wiki.save()
- wiki.delete()
- assert len(project.wikis.list()) == 0
diff --git a/tests/functional/api/test_releases.py b/tests/functional/api/test_releases.py
deleted file mode 100644
index f409c23..0000000
--- a/tests/functional/api/test_releases.py
+++ /dev/null
@@ -1,63 +0,0 @@
-release_name = "Demo Release"
-release_tag_name = "v1.2.3"
-release_description = "release notes go here"
-
-link_data = {"url": "https://example.com", "name": "link_name"}
-
-
-def test_create_project_release(project, project_file):
- project.refresh() # Gets us the current default branch
- release = project.releases.create(
- {
- "name": release_name,
- "tag_name": release_tag_name,
- "description": release_description,
- "ref": project.default_branch,
- }
- )
-
- assert len(project.releases.list()) == 1
- assert project.releases.get(release_tag_name)
- assert release.name == release_name
- assert release.tag_name == release_tag_name
- assert release.description == release_description
-
-
-def test_create_project_release_no_name(project, project_file):
- unnamed_release_tag_name = "v2.3.4"
-
- project.refresh() # Gets us the current default branch
- release = project.releases.create(
- {
- "tag_name": unnamed_release_tag_name,
- "description": release_description,
- "ref": project.default_branch,
- }
- )
-
- assert len(project.releases.list()) >= 1
- assert project.releases.get(unnamed_release_tag_name)
- assert release.tag_name == unnamed_release_tag_name
- assert release.description == release_description
-
-
-def test_update_save_project_release(project, release):
- updated_description = f"{release.description} updated"
- release.description = updated_description
- release.save()
-
- release = project.releases.get(release.tag_name)
- assert release.description == updated_description
-
-
-def test_delete_project_release(project, release):
- project.releases.delete(release.tag_name)
- assert release not in project.releases.list()
-
-
-def test_create_project_release_links(project, release):
- release.links.create(link_data)
-
- release = project.releases.get(release.tag_name)
- assert release.assets["links"][0]["url"] == link_data["url"]
- assert release.assets["links"][0]["name"] == link_data["name"]
diff --git a/tests/functional/api/test_repository.py b/tests/functional/api/test_repository.py
deleted file mode 100644
index 7ba84ea..0000000
--- a/tests/functional/api/test_repository.py
+++ /dev/null
@@ -1,126 +0,0 @@
-import base64
-import time
-
-import pytest
-
-import gitlab
-
-
-def test_repository_files(project):
- project.files.create(
- {
- "file_path": "README",
- "branch": "master",
- "content": "Initial content",
- "commit_message": "Initial commit",
- }
- )
- readme = project.files.get(file_path="README", ref="master")
- readme.content = base64.b64encode(b"Improved README").decode()
-
- time.sleep(2)
- readme.save(branch="master", commit_message="new commit")
- readme.delete(commit_message="Removing README", branch="master")
-
- project.files.create(
- {
- "file_path": "README.rst",
- "branch": "master",
- "content": "Initial content",
- "commit_message": "New commit",
- }
- )
- readme = project.files.get(file_path="README.rst", ref="master")
- # The first decode() is the ProjectFile method, the second one is the bytes
- # object method
- assert readme.decode().decode() == "Initial content"
-
- blame = project.files.blame(file_path="README.rst", ref="master")
- assert blame
-
-
-def test_repository_tree(project):
- tree = project.repository_tree()
- assert tree
- assert tree[0]["name"] == "README.rst"
-
- blob_id = tree[0]["id"]
- blob = project.repository_raw_blob(blob_id)
- assert blob.decode() == "Initial content"
-
- archive = project.repository_archive()
- assert isinstance(archive, bytes)
-
- archive2 = project.repository_archive("master")
- assert archive == archive2
-
- snapshot = project.snapshot()
- assert isinstance(snapshot, bytes)
-
-
-def test_create_commit(project):
- data = {
- "branch": "master",
- "commit_message": "blah blah blah",
- "actions": [{"action": "create", "file_path": "blah", "content": "blah"}],
- }
- commit = project.commits.create(data)
-
- assert "@@" in project.commits.list()[0].diff()[0]["diff"]
- assert isinstance(commit.refs(), list)
- assert isinstance(commit.merge_requests(), list)
-
-
-def test_create_commit_status(project):
- commit = project.commits.list()[0]
- size = len(commit.statuses.list())
- commit.statuses.create({"state": "success", "sha": commit.id})
- assert len(commit.statuses.list()) == size + 1
-
-
-def test_commit_signature(project):
- commit = project.commits.list()[0]
-
- with pytest.raises(gitlab.GitlabGetError) as e:
- commit.signature()
-
- assert "404 Signature Not Found" in str(e.value)
-
-
-def test_commit_comment(project):
- commit = project.commits.list()[0]
-
- commit.comments.create({"note": "This is a commit comment"})
- assert len(commit.comments.list()) == 1
-
-
-def test_commit_discussion(project):
- commit = project.commits.list()[0]
- count = len(commit.discussions.list())
-
- discussion = commit.discussions.create({"body": "Discussion body"})
- assert len(commit.discussions.list()) == (count + 1)
-
- note = discussion.notes.create({"body": "first note"})
- note_from_get = discussion.notes.get(note.id)
- note_from_get.body = "updated body"
- note_from_get.save()
- discussion = commit.discussions.get(discussion.id)
- # assert discussion.attributes["notes"][-1]["body"] == "updated body"
- note_from_get.delete()
- discussion = commit.discussions.get(discussion.id)
- # assert len(discussion.attributes["notes"]) == 1
-
-
-def test_revert_commit(project):
- commit = project.commits.list()[0]
- revert_commit = commit.revert(branch="master")
-
- expected_message = 'Revert "{}"\n\nThis reverts commit {}'.format(
- commit.message, commit.id
- )
- assert revert_commit["message"] == expected_message
-
- with pytest.raises(gitlab.GitlabRevertError):
- # Two revert attempts should raise GitlabRevertError
- commit.revert(branch="master")
diff --git a/tests/functional/api/test_snippets.py b/tests/functional/api/test_snippets.py
deleted file mode 100644
index 9e0f833..0000000
--- a/tests/functional/api/test_snippets.py
+++ /dev/null
@@ -1,74 +0,0 @@
-import gitlab
-
-
-def test_snippets(gl):
- snippets = gl.snippets.list(all=True)
- assert len(snippets) == 0
-
- snippet = gl.snippets.create(
- {"title": "snippet1", "file_name": "snippet1.py", "content": "import gitlab"}
- )
- snippet = gl.snippets.get(snippet.id)
- snippet.title = "updated_title"
- snippet.save()
-
- snippet = gl.snippets.get(snippet.id)
- assert snippet.title == "updated_title"
-
- content = snippet.content()
- assert content.decode() == "import gitlab"
- assert snippet.user_agent_detail()["user_agent"]
-
- snippet.delete()
- snippets = gl.snippets.list(all=True)
- assert len(snippets) == 0
-
-
-def test_project_snippets(project):
- project.snippets_enabled = True
- project.save()
-
- snippet = project.snippets.create(
- {
- "title": "snip1",
- "file_name": "foo.py",
- "content": "initial content",
- "visibility": gitlab.VISIBILITY_PRIVATE,
- }
- )
-
- assert snippet.user_agent_detail()["user_agent"]
-
-
-def test_project_snippet_discussion(project):
- snippet = project.snippets.list()[0]
- size = len(snippet.discussions.list())
-
- discussion = snippet.discussions.create({"body": "Discussion body"})
- assert len(snippet.discussions.list()) == size + 1
-
- note = discussion.notes.create({"body": "first note"})
- note_from_get = discussion.notes.get(note.id)
- note_from_get.body = "updated body"
- note_from_get.save()
-
- discussion = snippet.discussions.get(discussion.id)
- assert discussion.attributes["notes"][-1]["body"] == "updated body"
-
- note_from_get.delete()
- discussion = snippet.discussions.get(discussion.id)
- assert len(discussion.attributes["notes"]) == 1
-
-
-def test_project_snippet_file(project):
- snippet = project.snippets.list()[0]
- snippet.file_name = "bar.py"
- snippet.save()
-
- snippet = project.snippets.get(snippet.id)
- assert snippet.content().decode() == "initial content"
- assert snippet.file_name == "bar.py"
-
- size = len(project.snippets.list())
- snippet.delete()
- assert len(project.snippets.list()) == (size - 1)
diff --git a/tests/functional/api/test_users.py b/tests/functional/api/test_users.py
deleted file mode 100644
index 1ef237c..0000000
--- a/tests/functional/api/test_users.py
+++ /dev/null
@@ -1,170 +0,0 @@
-"""
-GitLab API:
-https://docs.gitlab.com/ee/api/users.html
-https://docs.gitlab.com/ee/api/users.html#delete-authentication-identity-from-user
-"""
-import pytest
-import requests
-
-
-@pytest.fixture(scope="session")
-def avatar_path(test_dir):
- return test_dir / "fixtures" / "avatar.png"
-
-
-def test_create_user(gl, avatar_path):
- user = gl.users.create(
- {
- "email": "foo@bar.com",
- "username": "foo",
- "name": "foo",
- "password": "foo_password",
- "avatar": open(avatar_path, "rb"),
- }
- )
-
- created_user = gl.users.list(username="foo")[0]
- assert created_user.username == user.username
- assert created_user.email == user.email
-
- avatar_url = user.avatar_url.replace("gitlab.test", "localhost:8080")
- uploaded_avatar = requests.get(avatar_url).content
- assert uploaded_avatar == open(avatar_path, "rb").read()
-
-
-def test_block_user(gl, user):
- user.block()
- users = gl.users.list(blocked=True)
- assert user in users
-
- user.unblock()
- users = gl.users.list(blocked=False)
- assert user in users
-
-
-def test_delete_user(gl, wait_for_sidekiq):
- new_user = gl.users.create(
- {
- "email": "delete-user@test.com",
- "username": "delete-user",
- "name": "delete-user",
- "password": "delete-user-pass",
- }
- )
-
- new_user.delete()
- result = wait_for_sidekiq(timeout=60)
- assert result is True, "sidekiq process should have terminated but did not"
-
- assert new_user.id not in [user.id for user in gl.users.list()]
-
-
-def test_user_projects_list(gl, user):
- projects = user.projects.list()
- assert isinstance(projects, list)
- assert not projects
-
-
-def test_user_events_list(gl, user):
- events = user.events.list()
- assert isinstance(events, list)
- assert not events
-
-
-def test_user_bio(gl, user):
- user.bio = "This is the user bio"
- user.save()
-
-
-def test_list_multiple_users(gl, user):
- second_email = f"{user.email}.2"
- second_username = f"{user.username}_2"
- second_user = gl.users.create(
- {
- "email": second_email,
- "username": second_username,
- "name": "Foo Bar",
- "password": "foobar_password",
- }
- )
- assert gl.users.list(search=second_user.username)[0].id == second_user.id
-
- expected = [user, second_user]
- actual = list(gl.users.list(search=user.username))
-
- assert len(expected) == len(actual)
- assert len(gl.users.list(search="asdf")) == 0
-
-
-def test_user_gpg_keys(gl, user, GPG_KEY):
- gkey = user.gpgkeys.create({"key": GPG_KEY})
- assert len(user.gpgkeys.list()) == 1
-
- # Seems broken on the gitlab side
- # gkey = user.gpgkeys.get(gkey.id)
-
- gkey.delete()
- assert len(user.gpgkeys.list()) == 0
-
-
-def test_user_ssh_keys(gl, user, SSH_KEY):
- key = user.keys.create({"title": "testkey", "key": SSH_KEY})
- assert len(user.keys.list()) == 1
-
- key.delete()
- assert len(user.keys.list()) == 0
-
-
-def test_user_email(gl, user):
- email = user.emails.create({"email": "foo2@bar.com"})
- assert len(user.emails.list()) == 1
-
- email.delete()
- assert len(user.emails.list()) == 0
-
-
-def test_user_custom_attributes(gl, user):
- attrs = user.customattributes.list()
- assert len(attrs) == 0
-
- attr = user.customattributes.set("key", "value1")
- assert len(gl.users.list(custom_attributes={"key": "value1"})) == 1
- assert attr.key == "key"
- assert attr.value == "value1"
- assert len(user.customattributes.list()) == 1
-
- attr = user.customattributes.set("key", "value2")
- attr = user.customattributes.get("key")
- assert attr.value == "value2"
- assert len(user.customattributes.list()) == 1
-
- attr.delete()
- assert len(user.customattributes.list()) == 0
-
-
-def test_user_impersonation_tokens(gl, user):
- token = user.impersonationtokens.create(
- {"name": "token1", "scopes": ["api", "read_user"]}
- )
-
- tokens = user.impersonationtokens.list(state="active")
- assert len(tokens) == 1
-
- token.delete()
- tokens = user.impersonationtokens.list(state="active")
- assert len(tokens) == 0
- tokens = user.impersonationtokens.list(state="inactive")
- assert len(tokens) == 1
-
-
-def test_user_identities(gl, user):
- provider = "test_provider"
-
- user.provider = provider
- user.extern_uid = "1"
- user.save()
- assert provider in [item["provider"] for item in user.identities]
-
- user.identityproviders.delete(provider)
- user = gl.users.get(user.id)
- assert provider not in [item["provider"] for item in user.identities]
diff --git a/tests/functional/api/test_variables.py b/tests/functional/api/test_variables.py
deleted file mode 100644
index d20ebba..0000000
--- a/tests/functional/api/test_variables.py
+++ /dev/null
@@ -1,48 +0,0 @@
-"""
-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/tests/functional/cli/__init__.py b/tests/functional/cli/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tests/functional/cli/__init__.py
+++ /dev/null
diff --git a/tests/functional/cli/conftest.py b/tests/functional/cli/conftest.py
deleted file mode 100644
index ba94dcb..0000000
--- a/tests/functional/cli/conftest.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import pytest
-
-
-@pytest.fixture
-def gitlab_cli(script_runner, gitlab_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", gitlab_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/tests/functional/cli/test_cli_artifacts.py b/tests/functional/cli/test_cli_artifacts.py
deleted file mode 100644
index aab0546..0000000
--- a/tests/functional/cli/test_cli_artifacts.py
+++ /dev/null
@@ -1,49 +0,0 @@
-import subprocess
-import textwrap
-import time
-from io import BytesIO
-from zipfile import is_zipfile
-
-content = textwrap.dedent(
- """\
- test-artifact:
- script: echo "test" > artifact.txt
- artifacts:
- untracked: true
- """
-)
-data = {
- "file_path": ".gitlab-ci.yml",
- "branch": "master",
- "content": content,
- "commit_message": "Initial commit",
-}
-
-
-def test_cli_artifacts(capsysbinary, gitlab_config, gitlab_runner, project):
- project.files.create(data)
-
- jobs = None
- while not jobs:
- jobs = project.jobs.list(scope="success")
- time.sleep(0.5)
-
- job = project.jobs.get(jobs[0].id)
- cmd = [
- "gitlab",
- "--config-file",
- gitlab_config,
- "project-job",
- "artifacts",
- "--id",
- str(job.id),
- "--project-id",
- str(project.id),
- ]
-
- with capsysbinary.disabled():
- artifacts = subprocess.check_output(cmd)
- assert isinstance(artifacts, bytes)
-
- artifacts_zip = BytesIO(artifacts)
- assert is_zipfile(artifacts_zip)
diff --git a/tests/functional/cli/test_cli_packages.py b/tests/functional/cli/test_cli_packages.py
deleted file mode 100644
index d7cdd18..0000000
--- a/tests/functional/cli/test_cli_packages.py
+++ /dev/null
@@ -1,60 +0,0 @@
-package_name = "hello-world"
-package_version = "v1.0.0"
-file_name = "hello.tar.gz"
-file_content = "package content"
-
-
-def test_list_project_packages(gitlab_cli, project):
- cmd = ["project-package", "list", "--project-id", project.id]
- ret = gitlab_cli(cmd)
-
- assert ret.success
-
-
-def test_list_group_packages(gitlab_cli, group):
- cmd = ["group-package", "list", "--group-id", group.id]
- ret = gitlab_cli(cmd)
-
- assert ret.success
-
-
-def test_upload_generic_package(tmp_path, gitlab_cli, project):
- path = tmp_path / file_name
- path.write_text(file_content)
-
- cmd = [
- "-v",
- "generic-package",
- "upload",
- "--project-id",
- project.id,
- "--package-name",
- package_name,
- "--path",
- path,
- "--package-version",
- package_version,
- "--file-name",
- file_name,
- ]
- ret = gitlab_cli(cmd)
-
- assert "201 Created" in ret.stdout
-
-
-def test_download_generic_package(gitlab_cli, project):
- cmd = [
- "generic-package",
- "download",
- "--project-id",
- project.id,
- "--package-name",
- package_name,
- "--package-version",
- package_version,
- "--file-name",
- file_name,
- ]
- ret = gitlab_cli(cmd)
-
- assert ret.stdout == file_content
diff --git a/tests/functional/cli/test_cli_v4.py b/tests/functional/cli/test_cli_v4.py
deleted file mode 100644
index a63c1b1..0000000
--- a/tests/functional/cli/test_cli_v4.py
+++ /dev/null
@@ -1,715 +0,0 @@
-import os
-import time
-
-
-def test_create_project(gitlab_cli):
- name = "test-project1"
-
- cmd = ["project", "create", "--name", name]
- ret = gitlab_cli(cmd)
-
- assert ret.success
- assert name in ret.stdout
-
-
-def test_update_project(gitlab_cli, project):
- description = "My New Description"
-
- cmd = ["project", "update", "--id", project.id, "--description", description]
- ret = gitlab_cli(cmd)
-
- assert ret.success
- assert description in ret.stdout
-
-
-def test_create_group(gitlab_cli):
- name = "test-group1"
- path = "group1"
-
- cmd = ["group", "create", "--name", name, "--path", path]
- ret = gitlab_cli(cmd)
-
- assert ret.success
- assert name in ret.stdout
- assert path in ret.stdout
-
-
-def test_update_group(gitlab_cli, gl, group):
- description = "My New Description"
-
- cmd = ["group", "update", "--id", group.id, "--description", description]
- ret = gitlab_cli(cmd)
-
- assert ret.success
-
- group = gl.groups.get(group.id)
- assert group.description == description
-
-
-def test_create_user(gitlab_cli, gl):
- email = "fake@email.com"
- username = "user1"
- name = "User One"
- password = "fakepassword"
-
- cmd = [
- "user",
- "create",
- "--email",
- email,
- "--username",
- username,
- "--name",
- name,
- "--password",
- password,
- ]
- ret = gitlab_cli(cmd)
-
- assert ret.success
-
- user = gl.users.list(username=username)[0]
-
- assert user.email == email
- assert user.username == username
- assert user.name == name
-
-
-def test_get_user_by_id(gitlab_cli, user):
- cmd = ["user", "get", "--id", user.id]
- ret = gitlab_cli(cmd)
-
- assert ret.success
- assert str(user.id) in ret.stdout
-
-
-def test_list_users_verbose_output(gitlab_cli):
- cmd = ["-v", "user", "list"]
- ret = gitlab_cli(cmd)
-
- assert ret.success
- assert "avatar-url" in ret.stdout
-
-
-def test_cli_args_not_in_output(gitlab_cli):
- cmd = ["-v", "user", "list"]
- ret = gitlab_cli(cmd)
-
- assert "config-file" not in ret.stdout
-
-
-def test_add_member_to_project(gitlab_cli, project, user):
- access_level = "40"
-
- cmd = [
- "project-member",
- "create",
- "--project-id",
- project.id,
- "--user-id",
- user.id,
- "--access-level",
- access_level,
- ]
- ret = gitlab_cli(cmd)
-
- assert ret.success
-
-
-def test_list_user_memberships(gitlab_cli, user):
- cmd = ["user-membership", "list", "--user-id", user.id]
- ret = gitlab_cli(cmd)
-
- assert ret.success
-
-
-def test_project_create_file(gitlab_cli, project):
- file_path = "README"
- branch = "master"
- content = "CONTENT"
- commit_message = "Initial commit"
-
- cmd = [
- "project-file",
- "create",
- "--project-id",
- project.id,
- "--file-path",
- file_path,
- "--branch",
- branch,
- "--content",
- content,
- "--commit-message",
- commit_message,
- ]
- ret = gitlab_cli(cmd)
-
- assert ret.success
-
-
-def test_create_project_issue(gitlab_cli, project):
- title = "my issue"
- description = "my issue description"
-
- cmd = [
- "project-issue",
- "create",
- "--project-id",
- project.id,
- "--title",
- title,
- "--description",
- description,
- ]
- ret = gitlab_cli(cmd)
-
- assert ret.success
- assert title in ret.stdout
-
-
-def test_create_issue_note(gitlab_cli, issue):
- body = "body"
-
- cmd = [
- "project-issue-note",
- "create",
- "--project-id",
- issue.project_id,
- "--issue-iid",
- issue.iid,
- "--body",
- body,
- ]
- ret = gitlab_cli(cmd)
-
- assert ret.success
-
-
-def test_create_branch(gitlab_cli, project):
- branch = "branch1"
-
- cmd = [
- "project-branch",
- "create",
- "--project-id",
- project.id,
- "--branch",
- branch,
- "--ref",
- "master",
- ]
- ret = gitlab_cli(cmd)
-
- assert ret.success
-
-
-def test_create_merge_request(gitlab_cli, project):
- branch = "branch1"
-
- cmd = [
- "project-merge-request",
- "create",
- "--project-id",
- project.id,
- "--source-branch",
- branch,
- "--target-branch",
- "master",
- "--title",
- "Update README",
- ]
- ret = gitlab_cli(cmd)
-
- assert ret.success
-
-
-def test_accept_request_merge(gitlab_cli, project):
- # MR needs at least 1 commit before we can merge
- mr = project.mergerequests.list()[0]
- file_data = {
- "branch": mr.source_branch,
- "file_path": "README2",
- "content": "Content",
- "commit_message": "Pre-merge commit",
- }
- project.files.create(file_data)
- time.sleep(2)
-
- cmd = [
- "project-merge-request",
- "merge",
- "--project-id",
- project.id,
- "--iid",
- mr.iid,
- ]
- ret = gitlab_cli(cmd)
-
- assert ret.success
-
-
-def test_revert_commit(gitlab_cli, project):
- commit = project.commits.list()[0]
-
- cmd = [
- "project-commit",
- "revert",
- "--project-id",
- project.id,
- "--id",
- commit.id,
- "--branch",
- "master",
- ]
- ret = gitlab_cli(cmd)
-
- assert ret.success
-
-
-def test_get_commit_signature_not_found(gitlab_cli, project):
- commit = project.commits.list()[0]
-
- cmd = ["project-commit", "signature", "--project-id", project.id, "--id", commit.id]
- ret = gitlab_cli(cmd)
-
- assert not ret.success
- assert "404 Signature Not Found" in ret.stderr
-
-
-def test_create_project_label(gitlab_cli, project):
- name = "prjlabel1"
- description = "prjlabel1 description"
- color = "#112233"
-
- cmd = [
- "-v",
- "project-label",
- "create",
- "--project-id",
- project.id,
- "--name",
- name,
- "--description",
- description,
- "--color",
- color,
- ]
- ret = gitlab_cli(cmd)
-
- assert ret.success
-
-
-def test_list_project_labels(gitlab_cli, project):
- cmd = ["-v", "project-label", "list", "--project-id", project.id]
- ret = gitlab_cli(cmd)
-
- assert ret.success
-
-
-def test_update_project_label(gitlab_cli, label):
- new_label = "prjlabel2"
- new_description = "prjlabel2 description"
- new_color = "#332211"
-
- cmd = [
- "-v",
- "project-label",
- "update",
- "--project-id",
- label.project_id,
- "--name",
- label.name,
- "--new-name",
- new_label,
- "--description",
- new_description,
- "--color",
- new_color,
- ]
- ret = gitlab_cli(cmd)
-
- assert ret.success
-
-
-def test_delete_project_label(gitlab_cli, label):
- # TODO: due to update above, we'd need a function-scope label fixture
- label_name = "prjlabel2"
-
- cmd = [
- "-v",
- "project-label",
- "delete",
- "--project-id",
- label.project_id,
- "--name",
- label_name,
- ]
- ret = gitlab_cli(cmd)
-
- assert ret.success
-
-
-def test_create_group_label(gitlab_cli, group):
- name = "grouplabel1"
- description = "grouplabel1 description"
- color = "#112233"
-
- cmd = [
- "-v",
- "group-label",
- "create",
- "--group-id",
- group.id,
- "--name",
- name,
- "--description",
- description,
- "--color",
- color,
- ]
- ret = gitlab_cli(cmd)
-
- assert ret.success
-
-
-def test_list_group_labels(gitlab_cli, group):
- cmd = ["-v", "group-label", "list", "--group-id", group.id]
- ret = gitlab_cli(cmd)
-
- assert ret.success
-
-
-def test_update_group_label(gitlab_cli, group_label):
- new_label = "grouplabel2"
- new_description = "grouplabel2 description"
- new_color = "#332211"
-
- cmd = [
- "-v",
- "group-label",
- "update",
- "--group-id",
- group_label.group_id,
- "--name",
- group_label.name,
- "--new-name",
- new_label,
- "--description",
- new_description,
- "--color",
- new_color,
- ]
- ret = gitlab_cli(cmd)
-
- assert ret.success
-
-
-def test_delete_group_label(gitlab_cli, group_label):
- # TODO: due to update above, we'd need a function-scope label fixture
- new_label = "grouplabel2"
-
- cmd = [
- "-v",
- "group-label",
- "delete",
- "--group-id",
- group_label.group_id,
- "--name",
- new_label,
- ]
- ret = gitlab_cli(cmd)
-
- assert ret.success
-
-
-def test_create_project_variable(gitlab_cli, project):
- key = "junk"
- value = "car"
-
- cmd = [
- "-v",
- "project-variable",
- "create",
- "--project-id",
- project.id,
- "--key",
- key,
- "--value",
- value,
- ]
- ret = gitlab_cli(cmd)
-
- assert ret.success
-
-
-def test_get_project_variable(gitlab_cli, variable):
- cmd = [
- "-v",
- "project-variable",
- "get",
- "--project-id",
- variable.project_id,
- "--key",
- variable.key,
- ]
- ret = gitlab_cli(cmd)
-
- assert ret.success
-
-
-def test_update_project_variable(gitlab_cli, variable):
- new_value = "bus"
-
- cmd = [
- "-v",
- "project-variable",
- "update",
- "--project-id",
- variable.project_id,
- "--key",
- variable.key,
- "--value",
- new_value,
- ]
- ret = gitlab_cli(cmd)
-
- assert ret.success
-
-
-def test_list_project_variables(gitlab_cli, project):
- cmd = ["-v", "project-variable", "list", "--project-id", project.id]
- ret = gitlab_cli(cmd)
-
- assert ret.success
-
-
-def test_delete_project_variable(gitlab_cli, variable):
- cmd = [
- "-v",
- "project-variable",
- "delete",
- "--project-id",
- variable.project_id,
- "--key",
- variable.key,
- ]
- ret = gitlab_cli(cmd)
-
- assert ret.success
-
-
-def test_delete_branch(gitlab_cli, project):
- # TODO: branch fixture
- branch = "branch1"
-
- cmd = ["project-branch", "delete", "--project-id", project.id, "--name", branch]
- ret = gitlab_cli(cmd)
-
- assert ret.success
-
-
-def test_project_upload_file(gitlab_cli, project):
- cmd = [
- "project",
- "upload",
- "--id",
- project.id,
- "--filename",
- __file__,
- "--filepath",
- os.path.realpath(__file__),
- ]
- ret = gitlab_cli(cmd)
-
- assert ret.success
-
-
-def test_get_application_settings(gitlab_cli):
- cmd = ["application-settings", "get"]
- ret = gitlab_cli(cmd)
-
- assert ret.success
-
-
-def test_update_application_settings(gitlab_cli):
- cmd = ["application-settings", "update", "--signup-enabled", "false"]
- ret = gitlab_cli(cmd)
-
- assert ret.success
-
-
-def test_create_project_with_values_from_file(gitlab_cli, tmpdir):
- name = "gitlab-project-from-file"
- description = "Multiline\n\nData\n"
- from_file = tmpdir.join(name)
- from_file.write(description)
- from_file_path = f"@{str(from_file)}"
-
- cmd = [
- "-v",
- "project",
- "create",
- "--name",
- name,
- "--description",
- from_file_path,
- ]
- ret = gitlab_cli(cmd)
-
- assert ret.success
- assert description in ret.stdout
-
-
-def test_create_project_deploy_token(gitlab_cli, project):
- name = "project-token"
- username = "root"
- expires_at = "2021-09-09"
- scopes = "read_registry"
-
- cmd = [
- "-v",
- "project-deploy-token",
- "create",
- "--project-id",
- project.id,
- "--name",
- name,
- "--username",
- username,
- "--expires-at",
- expires_at,
- "--scopes",
- scopes,
- ]
- ret = gitlab_cli(cmd)
-
- assert ret.success
- assert name in ret.stdout
- assert username in ret.stdout
- assert expires_at in ret.stdout
- assert scopes in ret.stdout
-
-
-def test_list_all_deploy_tokens(gitlab_cli, deploy_token):
- cmd = ["-v", "deploy-token", "list"]
- ret = gitlab_cli(cmd)
-
- assert ret.success
- assert deploy_token.name in ret.stdout
- assert str(deploy_token.id) in ret.stdout
- assert deploy_token.username in ret.stdout
- assert deploy_token.expires_at in ret.stdout
- assert deploy_token.scopes[0] in ret.stdout
-
-
-def test_list_project_deploy_tokens(gitlab_cli, deploy_token):
- cmd = [
- "-v",
- "project-deploy-token",
- "list",
- "--project-id",
- deploy_token.project_id,
- ]
- ret = gitlab_cli(cmd)
-
- assert ret.success
- assert deploy_token.name in ret.stdout
- assert str(deploy_token.id) in ret.stdout
- assert deploy_token.username in ret.stdout
- assert deploy_token.expires_at in ret.stdout
- assert deploy_token.scopes[0] in ret.stdout
-
-
-def test_delete_project_deploy_token(gitlab_cli, deploy_token):
- cmd = [
- "-v",
- "project-deploy-token",
- "delete",
- "--project-id",
- deploy_token.project_id,
- "--id",
- deploy_token.id,
- ]
- ret = gitlab_cli(cmd)
-
- assert ret.success
- # TODO assert not in list
-
-
-def test_create_group_deploy_token(gitlab_cli, group):
- name = "group-token"
- username = "root"
- expires_at = "2021-09-09"
- scopes = "read_registry"
-
- cmd = [
- "-v",
- "group-deploy-token",
- "create",
- "--group-id",
- group.id,
- "--name",
- name,
- "--username",
- username,
- "--expires-at",
- expires_at,
- "--scopes",
- scopes,
- ]
- ret = gitlab_cli(cmd)
-
- assert ret.success
- assert name in ret.stdout
- assert username in ret.stdout
- assert expires_at in ret.stdout
- assert scopes in ret.stdout
-
-
-def test_list_group_deploy_tokens(gitlab_cli, group_deploy_token):
- cmd = [
- "-v",
- "group-deploy-token",
- "list",
- "--group-id",
- group_deploy_token.group_id,
- ]
- ret = gitlab_cli(cmd)
-
- assert ret.success
- assert group_deploy_token.name in ret.stdout
- assert str(group_deploy_token.id) in ret.stdout
- assert group_deploy_token.username in ret.stdout
- assert group_deploy_token.expires_at in ret.stdout
- assert group_deploy_token.scopes[0] in ret.stdout
-
-
-def test_delete_group_deploy_token(gitlab_cli, group_deploy_token):
- cmd = [
- "-v",
- "group-deploy-token",
- "delete",
- "--group-id",
- group_deploy_token.group_id,
- "--id",
- group_deploy_token.id,
- ]
- ret = gitlab_cli(cmd)
-
- assert ret.success
- # TODO assert not in list
-
-
-def test_delete_project(gitlab_cli, project):
- cmd = ["project", "delete", "--id", project.id]
- ret = gitlab_cli(cmd)
-
- assert ret.success
-
-
-def test_delete_group(gitlab_cli, group):
- cmd = ["group", "delete", "--id", group.id]
- ret = gitlab_cli(cmd)
-
- assert ret.success
diff --git a/tests/functional/cli/test_cli_variables.py b/tests/functional/cli/test_cli_variables.py
deleted file mode 100644
index 9b1b16d..0000000
--- a/tests/functional/cli/test_cli_variables.py
+++ /dev/null
@@ -1,19 +0,0 @@
-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/tests/functional/conftest.py b/tests/functional/conftest.py
deleted file mode 100644
index 23aa583..0000000
--- a/tests/functional/conftest.py
+++ /dev/null
@@ -1,489 +0,0 @@
-import tempfile
-import time
-import uuid
-from pathlib import Path
-from subprocess import check_output
-
-import pytest
-
-import gitlab
-
-
-def reset_gitlab(gl):
- # previously tools/reset_gitlab.py
- for project in gl.projects.list():
- for deploy_token in project.deploytokens.list():
- deploy_token.delete()
- project.delete()
- for group in gl.groups.list():
- for deploy_token in group.deploytokens.list():
- deploy_token.delete()
- group.delete()
- for variable in gl.variables.list():
- variable.delete()
- for user in gl.users.list():
- if user.username != "root":
- user.delete(hard_delete=True)
-
-
-def set_token(container, rootdir):
- set_token_rb = rootdir / "fixtures" / "set_token.rb"
-
- with open(set_token_rb, "r") as f:
- set_token_command = f.read().strip()
-
- rails_command = [
- "docker",
- "exec",
- container,
- "gitlab-rails",
- "runner",
- set_token_command,
- ]
- output = check_output(rails_command).decode().strip()
-
- return output
-
-
-def pytest_report_collectionfinish(config, startdir, items):
- return [
- "",
- "Starting GitLab container.",
- "Waiting for GitLab to reconfigure.",
- "This may take a few minutes.",
- ]
-
-
-def pytest_addoption(parser):
- parser.addoption(
- "--keep-containers",
- action="store_true",
- help="Keep containers running after testing",
- )
-
-
-@pytest.fixture(scope="session")
-def temp_dir():
- return Path(tempfile.gettempdir())
-
-
-@pytest.fixture(scope="session")
-def test_dir(pytestconfig):
- return pytestconfig.rootdir / "tests" / "functional"
-
-
-@pytest.fixture(scope="session")
-def docker_compose_file(test_dir):
- return test_dir / "fixtures" / "docker-compose.yml"
-
-
-@pytest.fixture(scope="session")
-def docker_compose_project_name():
- """Set a consistent project name to enable optional reuse of containers."""
- return "pytest-python-gitlab"
-
-
-@pytest.fixture(scope="session")
-def docker_cleanup(request):
- """Conditionally keep containers around by overriding the cleanup command."""
- if request.config.getoption("--keep-containers"):
- # Print version and exit.
- return "-v"
- return "down -v"
-
-
-@pytest.fixture(scope="session")
-def check_is_alive():
- """
- Return a healthcheck function fixture for the GitLab container spinup.
- """
-
- def _check(container):
- logs = ["docker", "logs", container]
- return "gitlab Reconfigured!" in check_output(logs).decode()
-
- return _check
-
-
-@pytest.fixture
-def wait_for_sidekiq(gl):
- """
- Return a helper function to wait until there are no busy sidekiq processes.
-
- Use this with asserts for slow tasks (group/project/user creation/deletion).
- """
-
- def _wait(timeout=30, step=0.5):
- for _ in range(timeout):
- time.sleep(step)
- busy = False
- processes = gl.sidekiq.process_metrics()["processes"]
- for process in processes:
- if process["busy"]:
- busy = True
- if not busy:
- return True
- return False
-
- return _wait
-
-
-@pytest.fixture(scope="session")
-def gitlab_config(check_is_alive, docker_ip, docker_services, temp_dir, test_dir):
- config_file = temp_dir / "python-gitlab.cfg"
- port = docker_services.port_for("gitlab", 80)
-
- docker_services.wait_until_responsive(
- timeout=200, pause=5, check=lambda: check_is_alive("gitlab-test")
- )
-
- token = set_token("gitlab-test", rootdir=test_dir)
-
- config = f"""[global]
-default = local
-timeout = 60
-
-[local]
-url = http://{docker_ip}:{port}
-private_token = {token}
-api_version = 4"""
-
- with open(config_file, "w") as f:
- f.write(config)
-
- return config_file
-
-
-@pytest.fixture(scope="session")
-def gl(gitlab_config):
- """Helper instance to make fixtures and asserts directly via the API."""
-
- instance = gitlab.Gitlab.from_config("local", [gitlab_config])
- reset_gitlab(instance)
-
- return instance
-
-
-@pytest.fixture(scope="session")
-def gitlab_runner(gl):
- container = "gitlab-runner-test"
- runner_name = "python-gitlab-runner"
- token = "registration-token"
- url = "http://gitlab"
-
- docker_exec = ["docker", "exec", container, "gitlab-runner"]
- register = [
- "register",
- "--run-untagged",
- "--non-interactive",
- "--registration-token",
- token,
- "--name",
- runner_name,
- "--url",
- url,
- "--clone-url",
- url,
- "--executor",
- "shell",
- ]
- unregister = ["unregister", "--name", runner_name]
-
- yield check_output(docker_exec + register).decode()
-
- check_output(docker_exec + unregister).decode()
-
-
-@pytest.fixture(scope="module")
-def group(gl):
- """Group fixture for group API resource tests."""
- _id = uuid.uuid4().hex
- data = {
- "name": f"test-group-{_id}",
- "path": f"group-{_id}",
- }
- group = gl.groups.create(data)
-
- yield group
-
- try:
- group.delete()
- except gitlab.exceptions.GitlabDeleteError as e:
- print(f"Group already deleted: {e}")
-
-
-@pytest.fixture(scope="module")
-def project(gl):
- """Project fixture for project API resource tests."""
- _id = uuid.uuid4().hex
- name = f"test-project-{_id}"
-
- project = gl.projects.create(name=name)
-
- yield project
-
- try:
- project.delete()
- except gitlab.exceptions.GitlabDeleteError as e:
- print(f"Project already deleted: {e}")
-
-
-@pytest.fixture(scope="function")
-def merge_request(project, wait_for_sidekiq):
- """Fixture used to create a merge_request.
-
- It will create a branch, add a commit to the branch, and then create a
- merge request against project.default_branch. The MR will be returned.
-
- When finished any created merge requests and branches will be deleted.
-
- NOTE: No attempt is made to restore project.default_branch to its previous
- state. So if the merge request is merged then its content will be in the
- project.default_branch branch.
- """
-
- to_delete = []
-
- def _merge_request(*, source_branch: str):
- # Wait for processes to be done before we start...
- # NOTE(jlvillal): Sometimes the CI would give a "500 Internal Server
- # Error". Hoping that waiting until all other processes are done will
- # help with that.
- result = wait_for_sidekiq(timeout=60)
- assert result is True, "sidekiq process should have terminated but did not"
-
- project.refresh() # Gets us the current default branch
- project.branches.create(
- {"branch": source_branch, "ref": project.default_branch}
- )
- # NOTE(jlvillal): Must create a commit in the new branch before we can
- # create an MR that will work.
- project.files.create(
- {
- "file_path": f"README.{source_branch}",
- "branch": source_branch,
- "content": "Initial content",
- "commit_message": "New commit in new branch",
- }
- )
- mr = project.mergerequests.create(
- {
- "source_branch": source_branch,
- "target_branch": project.default_branch,
- "title": "Should remove source branch",
- "remove_source_branch": True,
- }
- )
- result = wait_for_sidekiq(timeout=60)
- assert result is True, "sidekiq process should have terminated but did not"
-
- mr_iid = mr.iid
- for _ in range(60):
- mr = project.mergerequests.get(mr_iid)
- if mr.merge_status != "checking":
- break
- time.sleep(0.5)
- assert mr.merge_status != "checking"
-
- to_delete.append((mr.iid, source_branch))
- return mr
-
- yield _merge_request
-
- for mr_iid, source_branch in to_delete:
- project.mergerequests.delete(mr_iid)
- try:
- project.branches.delete(source_branch)
- except gitlab.exceptions.GitlabDeleteError:
- # Ignore if branch was already deleted
- pass
-
-
-@pytest.fixture(scope="module")
-def project_file(project):
- """File fixture for tests requiring a project with files and branches."""
- project_file = project.files.create(
- {
- "file_path": "README",
- "branch": "master",
- "content": "Initial content",
- "commit_message": "Initial commit",
- }
- )
-
- return project_file
-
-
-@pytest.fixture(scope="function")
-def release(project, project_file):
- _id = uuid.uuid4().hex
- name = f"test-release-{_id}"
-
- project.refresh() # Gets us the current default branch
- release = project.releases.create(
- {
- "name": name,
- "tag_name": _id,
- "description": "description",
- "ref": project.default_branch,
- }
- )
-
- return release
-
-
-@pytest.fixture(scope="module")
-def user(gl):
- """User fixture for user API resource tests."""
- _id = uuid.uuid4().hex
- email = f"user{_id}@email.com"
- username = f"user{_id}"
- name = f"User {_id}"
- password = "fakepassword"
-
- user = gl.users.create(email=email, username=username, name=name, password=password)
-
- yield user
-
- try:
- user.delete()
- except gitlab.exceptions.GitlabDeleteError as e:
- print(f"User already deleted: {e}")
-
-
-@pytest.fixture(scope="module")
-def issue(project):
- """Issue fixture for issue API resource tests."""
- _id = uuid.uuid4().hex
- data = {"title": f"Issue {_id}", "description": f"Issue {_id} description"}
-
- return project.issues.create(data)
-
-
-@pytest.fixture(scope="module")
-def milestone(project):
- _id = uuid.uuid4().hex
- data = {"title": f"milestone{_id}"}
-
- return project.milestones.create(data)
-
-
-@pytest.fixture(scope="module")
-def label(project):
- """Label fixture for project label API resource tests."""
- _id = uuid.uuid4().hex
- data = {
- "name": f"prjlabel{_id}",
- "description": f"prjlabel1 {_id} description",
- "color": "#112233",
- }
-
- return project.labels.create(data)
-
-
-@pytest.fixture(scope="module")
-def group_label(group):
- """Label fixture for group label API resource tests."""
- _id = uuid.uuid4().hex
- data = {
- "name": f"grplabel{_id}",
- "description": f"grplabel1 {_id} description",
- "color": "#112233",
- }
-
- return group.labels.create(data)
-
-
-@pytest.fixture(scope="module")
-def variable(project):
- """Variable fixture for project variable API resource tests."""
- _id = uuid.uuid4().hex
- data = {"key": f"var{_id}", "value": f"Variable {_id}"}
-
- return project.variables.create(data)
-
-
-@pytest.fixture(scope="module")
-def deploy_token(project):
- """Deploy token fixture for project deploy token API resource tests."""
- _id = uuid.uuid4().hex
- data = {
- "name": f"token-{_id}",
- "username": "root",
- "expires_at": "2021-09-09",
- "scopes": "read_registry",
- }
-
- return project.deploytokens.create(data)
-
-
-@pytest.fixture(scope="module")
-def group_deploy_token(group):
- """Deploy token fixture for group deploy token API resource tests."""
- _id = uuid.uuid4().hex
- data = {
- "name": f"group-token-{_id}",
- "username": "root",
- "expires_at": "2021-09-09",
- "scopes": "read_registry",
- }
-
- return group.deploytokens.create(data)
-
-
-@pytest.fixture(scope="session")
-def GPG_KEY():
- return """-----BEGIN PGP PUBLIC KEY BLOCK-----
-
-mQENBFn5mzYBCADH6SDVPAp1zh/hxmTi0QplkOfExBACpuY6OhzNdIg+8/528b3g
-Y5YFR6T/HLv/PmeHskUj21end1C0PNG2T9dTx+2Vlh9ISsSG1kyF9T5fvMR3bE0x
-Dl6S489CXZrjPTS9SHk1kF+7dwjUxLJyxF9hPiSihFefDFu3NeOtG/u8vbC1mewQ
-ZyAYue+mqtqcCIFFoBz7wHKMWjIVSJSyTkXExu4OzpVvy3l2EikbvavI3qNz84b+
-Mgkv/kiBlNoCy3CVuPk99RYKZ3lX1vVtqQ0OgNGQvb4DjcpyjmbKyibuZwhDjIOh
-au6d1OyEbayTntd+dQ4j9EMSnEvm/0MJ4eXPABEBAAG0G0dpdGxhYlRlc3QxIDxm
-YWtlQGZha2UudGxkPokBNwQTAQgAIQUCWfmbNgIbAwULCQgHAgYVCAkKCwIEFgID
-AQIeAQIXgAAKCRBgxELHf8f3hF3yB/wNJlWPKY65UsB4Lo0hs1OxdxCDqXogSi0u
-6crDEIiyOte62pNZKzWy8TJcGZvznRTZ7t8hXgKFLz3PRMcl+vAiRC6quIDUj+2V
-eYfwaItd1lUfzvdCaC7Venf4TQ74f5vvNg/zoGwE6eRoSbjlLv9nqsxeA0rUBUQL
-LYikWhVMP3TrlfgfduYvh6mfgh57BDLJ9kJVpyfxxx9YLKZbaas9sPa6LgBtR555
-JziUxHmbEv8XCsUU8uoFeP1pImbNBplqE3wzJwzOMSmmch7iZzrAwfN7N2j3Wj0H
-B5kQddJ9dmB4BbU0IXGhWczvdpxboI2wdY8a1JypxOdePoph/43iuQENBFn5mzYB
-CADnTPY0Zf3d9zLjBNgIb3yDl94uOcKCq0twNmyjMhHzGqw+UMe9BScy34GL94Al
-xFRQoaL+7P8hGsnsNku29A/VDZivcI+uxTx4WQ7OLcn7V0bnHV4d76iky2ufbUt/
-GofthjDs1SonePO2N09sS4V4uK0d5N4BfCzzXgvg8etCLxNmC9BGt7AaKUUzKBO4
-2QvNNaC2C/8XEnOgNWYvR36ylAXAmo0sGFXUsBCTiq1fugS9pwtaS2JmaVpZZ3YT
-pMZlS0+SjC5BZYFqSmKCsA58oBRzCxQz57nR4h5VEflgD+Hy0HdW0UHETwz83E6/
-U0LL6YyvhwFr6KPq5GxinSvfABEBAAGJAR8EGAEIAAkFAln5mzYCGwwACgkQYMRC
-x3/H94SJgwgAlKQb10/xcL/epdDkR7vbiei7huGLBpRDb/L5fM8B5W77Qi8Xmuqj
-cCu1j99ZCA5hs/vwVn8j8iLSBGMC5gxcuaar/wtmiaEvT9fO/h6q4opG7NcuiJ8H
-wRj8ccJmRssNqDD913PLz7T40Ts62blhrEAlJozGVG/q7T3RAZcskOUHKeHfc2RI
-YzGsC/I9d7k6uxAv1L9Nm5F2HaAQDzhkdd16nKkGaPGR35cT1JLInkfl5cdm7ldN
-nxs4TLO3kZjUTgWKdhpgRNF5hwaz51ZjpebaRf/ZqRuNyX4lIRolDxzOn/+O1o8L
-qG2ZdhHHmSK2LaQLFiSprUkikStNU9BqSQ==
-=5OGa
------END PGP PUBLIC KEY BLOCK-----"""
-
-
-@pytest.fixture(scope="session")
-def SSH_KEY():
- return (
- "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDZAjAX8vTiHD7Yi3/EzuVaDChtih"
- "79HyJZ6H9dEqxFfmGA1YnncE0xujQ64TCebhkYJKzmTJCImSVkOu9C4hZgsw6eE76n"
- "+Cg3VwEeDUFy+GXlEJWlHaEyc3HWioxgOALbUp3rOezNh+d8BDwwqvENGoePEBsz5l"
- "a6WP5lTi/HJIjAl6Hu+zHgdj1XVExeH+S52EwpZf/ylTJub0Bl5gHwf/siVE48mLMI"
- "sqrukXTZ6Zg+8EHAIvIQwJ1dKcXe8P5IoLT7VKrbkgAnolS0I8J+uH7KtErZJb5oZh"
- "S4OEwsNpaXMAr+6/wWSpircV2/e7sFLlhlKBC4Iq1MpqlZ7G3p foo@bar"
- )
-
-
-@pytest.fixture(scope="session")
-def DEPLOY_KEY():
- return (
- "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFdRyjJQh+1niBpXqE2I8dzjG"
- "MXFHlRjX9yk/UfOn075IdaockdU58sw2Ai1XIWFpZpfJkW7z+P47ZNSqm1gzeXI"
- "rtKa9ZUp8A7SZe8vH4XVn7kh7bwWCUirqtn8El9XdqfkzOs/+FuViriUWoJVpA6"
- "WZsDNaqINFKIA5fj/q8XQw+BcS92L09QJg9oVUuH0VVwNYbU2M2IRmSpybgC/gu"
- "uWTrnCDMmLItksATifLvRZwgdI8dr+q6tbxbZknNcgEPrI2jT0hYN9ZcjNeWuyv"
- "rke9IepE7SPBT41C+YtUX4dfDZDmczM1cE0YL/krdUCfuZHMa4ZS2YyNd6slufc"
- "vn bar@foo"
- )
diff --git a/tests/functional/ee-test.py b/tests/functional/ee-test.py
deleted file mode 100755
index 3a99951..0000000
--- a/tests/functional/ee-test.py
+++ /dev/null
@@ -1,158 +0,0 @@
-#!/usr/bin/env python
-
-import gitlab
-
-P1 = "root/project1"
-P2 = "root/project2"
-MR_P1 = 1
-I_P1 = 1
-I_P2 = 1
-EPIC_ISSUES = [4, 5]
-G1 = "group1"
-LDAP_CN = "app1"
-LDAP_PROVIDER = "ldapmain"
-
-
-def start_log(message):
- print("Testing %s... " % message, end="")
-
-
-def end_log():
- print("OK")
-
-
-gl = gitlab.Gitlab.from_config("ee")
-project1 = gl.projects.get(P1)
-project2 = gl.projects.get(P2)
-issue_p1 = project1.issues.get(I_P1)
-issue_p2 = project2.issues.get(I_P2)
-group1 = gl.groups.get(G1)
-mr = project1.mergerequests.get(1)
-
-start_log("MR approvals")
-approval = project1.approvals.get()
-v = approval.reset_approvals_on_push
-approval.reset_approvals_on_push = not v
-approval.save()
-approval = project1.approvals.get()
-assert v != approval.reset_approvals_on_push
-project1.approvals.set_approvers(1, [1], [])
-approval = project1.approvals.get()
-assert approval.approvers[0]["user"]["id"] == 1
-
-approval = mr.approvals.get()
-approval.approvals_required = 2
-approval.save()
-approval = mr.approvals.get()
-assert approval.approvals_required == 2
-approval.approvals_required = 3
-approval.save()
-approval = mr.approvals.get()
-assert approval.approvals_required == 3
-mr.approvals.set_approvers(1, [1], [])
-approval = mr.approvals.get()
-assert approval.approvers[0]["user"]["id"] == 1
-
-ars = project1.approvalrules.list(all=True)
-assert len(ars) == 0
-project1.approvalrules.create(
- {"name": "approval-rule", "approvals_required": 1, "group_ids": [group1.id]}
-)
-ars = project1.approvalrules.list(all=True)
-assert len(ars) == 1
-assert ars[0].approvals_required == 2
-ars[0].save()
-ars = project1.approvalrules.list(all=True)
-assert len(ars) == 1
-assert ars[0].approvals_required == 2
-ars[0].delete()
-ars = project1.approvalrules.list(all=True)
-assert len(ars) == 0
-end_log()
-
-start_log("geo nodes")
-# very basic tests because we only have 1 node...
-nodes = gl.geonodes.list()
-status = gl.geonodes.status()
-end_log()
-
-start_log("issue links")
-# bit of cleanup just in case
-for link in issue_p1.links.list():
- issue_p1.links.delete(link.issue_link_id)
-
-src, dst = issue_p1.links.create({"target_project_id": P2, "target_issue_iid": I_P2})
-links = issue_p1.links.list()
-link_id = links[0].issue_link_id
-issue_p1.links.delete(link_id)
-end_log()
-
-start_log("LDAP links")
-# bit of cleanup just in case
-if hasattr(group1, "ldap_group_links"):
- for link in group1.ldap_group_links:
- group1.delete_ldap_group_link(link["cn"], link["provider"])
-assert gl.ldapgroups.list()
-group1.add_ldap_group_link(LDAP_CN, 30, LDAP_PROVIDER)
-group1.ldap_sync()
-group1.delete_ldap_group_link(LDAP_CN)
-end_log()
-
-start_log("boards")
-# bit of cleanup just in case
-for board in project1.boards.list():
- if board.name == "testboard":
- board.delete()
-board = project1.boards.create({"name": "testboard"})
-board = project1.boards.get(board.id)
-project1.boards.delete(board.id)
-
-for board in group1.boards.list():
- if board.name == "testboard":
- board.delete()
-board = group1.boards.create({"name": "testboard"})
-board = group1.boards.get(board.id)
-group1.boards.delete(board.id)
-end_log()
-
-start_log("push rules")
-pr = project1.pushrules.get()
-if pr:
- pr.delete()
-pr = project1.pushrules.create({"deny_delete_tag": True})
-pr.deny_delete_tag = False
-pr.save()
-pr = project1.pushrules.get()
-assert pr is not None
-assert pr.deny_delete_tag is False
-pr.delete()
-end_log()
-
-start_log("license")
-license = gl.get_license()
-assert "user_limit" in license
-try:
- gl.set_license("dummykey")
-except Exception as e:
- assert "The license key is invalid." in e.error_message
-end_log()
-
-start_log("epics")
-epic = group1.epics.create({"title": "Test epic"})
-epic.title = "Fixed title"
-epic.labels = ["label1", "label2"]
-epic.save()
-epic = group1.epics.get(epic.iid)
-assert epic.title == "Fixed title"
-assert len(group1.epics.list())
-
-# issues
-assert not epic.issues.list()
-for i in EPIC_ISSUES:
- epic.issues.create({"issue_id": i})
-assert len(EPIC_ISSUES) == len(epic.issues.list())
-for ei in epic.issues.list():
- ei.delete()
-
-epic.delete()
-end_log()
diff --git a/tests/functional/fixtures/.env b/tests/functional/fixtures/.env
deleted file mode 100644
index 374f7ac..0000000
--- a/tests/functional/fixtures/.env
+++ /dev/null
@@ -1,2 +0,0 @@
-GITLAB_IMAGE=gitlab/gitlab-ce
-GITLAB_TAG=14.3.2-ce.0
diff --git a/tests/functional/fixtures/avatar.png b/tests/functional/fixtures/avatar.png
deleted file mode 100644
index a3a767c..0000000
--- a/tests/functional/fixtures/avatar.png
+++ /dev/null
Binary files differ
diff --git a/tests/functional/fixtures/docker-compose.yml b/tests/functional/fixtures/docker-compose.yml
deleted file mode 100644
index 134f266..0000000
--- a/tests/functional/fixtures/docker-compose.yml
+++ /dev/null
@@ -1,46 +0,0 @@
-version: '3'
-
-networks:
- gitlab-network:
- name: gitlab-network
-
-services:
- gitlab:
- image: '${GITLAB_IMAGE}:${GITLAB_TAG}'
- container_name: 'gitlab-test'
- hostname: 'gitlab.test'
- privileged: true # Just in case https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/1350
- environment:
- GITLAB_ROOT_PASSWORD: 5iveL!fe
- GITLAB_SHARED_RUNNERS_REGISTRATION_TOKEN: registration-token
- GITLAB_OMNIBUS_CONFIG: |
- external_url 'http://gitlab.test'
- registry['enable'] = false
- nginx['redirect_http_to_https'] = false
- nginx['listen_port'] = 80
- nginx['listen_https'] = false
- pages_external_url 'http://pages.gitlab.lxd'
- gitlab_pages['enable'] = true
- gitlab_pages['inplace_chroot'] = true
- prometheus['enable'] = false
- alertmanager['enable'] = false
- node_exporter['enable'] = false
- redis_exporter['enable'] = false
- postgres_exporter['enable'] = false
- pgbouncer_exporter['enable'] = false
- gitlab_exporter['enable'] = false
- grafana['enable'] = false
- letsencrypt['enable'] = false
- ports:
- - '8080:80'
- - '2222:22'
- networks:
- - gitlab-network
-
- gitlab-runner:
- image: gitlab/gitlab-runner:latest
- container_name: 'gitlab-runner-test'
- depends_on:
- - gitlab
- networks:
- - gitlab-network
diff --git a/tests/functional/fixtures/set_token.rb b/tests/functional/fixtures/set_token.rb
deleted file mode 100644
index 503588b..0000000
--- a/tests/functional/fixtures/set_token.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#programmatically-creating-a-personal-access-token
-
-user = User.find_by_username('root')
-
-token = user.personal_access_tokens.first_or_create(scopes: [:api, :sudo], name: 'default');
-token.set_token('python-gitlab-token');
-token.save!
-
-puts token.token
diff --git a/tests/smoke/__init__.py b/tests/smoke/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tests/smoke/__init__.py
+++ /dev/null
diff --git a/tests/smoke/test_dists.py b/tests/smoke/test_dists.py
deleted file mode 100644
index 6f38ff7..0000000
--- a/tests/smoke/test_dists.py
+++ /dev/null
@@ -1,33 +0,0 @@
-import tarfile
-import zipfile
-from pathlib import Path
-from sys import version_info
-
-import pytest
-from setuptools import sandbox
-
-from gitlab import __title__, __version__
-
-DIST_DIR = Path("dist")
-TEST_DIR = "tests"
-SDIST_FILE = f"{__title__}-{__version__}.tar.gz"
-WHEEL_FILE = (
- f"{__title__.replace('-', '_')}-{__version__}-py{version_info.major}-none-any.whl"
-)
-
-
-@pytest.fixture(scope="function")
-def build():
- sandbox.run_setup("setup.py", ["clean", "--all"])
- return sandbox.run_setup("setup.py", ["sdist", "bdist_wheel"])
-
-
-def test_sdist_includes_tests(build):
- sdist = tarfile.open(DIST_DIR / SDIST_FILE, "r:gz")
- test_dir = sdist.getmember(f"{__title__}-{__version__}/{TEST_DIR}")
- assert test_dir.isdir()
-
-
-def test_wheel_excludes_tests(build):
- wheel = zipfile.ZipFile(DIST_DIR / WHEEL_FILE)
- assert [not file.startswith(TEST_DIR) for file in wheel.namelist()]
diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tests/unit/__init__.py
+++ /dev/null
diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py
deleted file mode 100644
index f58c77a..0000000
--- a/tests/unit/conftest.py
+++ /dev/null
@@ -1,84 +0,0 @@
-import pytest
-
-import gitlab
-
-
-@pytest.fixture
-def gl():
- return gitlab.Gitlab(
- "http://localhost",
- private_token="private_token",
- ssl_verify=True,
- api_version="4",
- )
-
-
-@pytest.fixture
-def gl_retry():
- return gitlab.Gitlab(
- "http://localhost",
- private_token="private_token",
- ssl_verify=True,
- api_version="4",
- retry_transient_errors=True,
- )
-
-
-# Todo: parametrize, but check what tests it's really useful for
-@pytest.fixture
-def gl_trailing():
- return gitlab.Gitlab(
- "http://localhost/", private_token="private_token", api_version="4"
- )
-
-
-@pytest.fixture
-def default_config(tmpdir):
- valid_config = """[global]
- default = one
- ssl_verify = true
- timeout = 2
-
- [one]
- url = http://one.url
- private_token = ABCDEF
- """
-
- config_path = tmpdir.join("python-gitlab.cfg")
- config_path.write(valid_config)
- return str(config_path)
-
-
-@pytest.fixture
-def tag_name():
- return "v1.0.0"
-
-
-@pytest.fixture
-def group(gl):
- return gl.groups.get(1, lazy=True)
-
-
-@pytest.fixture
-def project(gl):
- return gl.projects.get(1, lazy=True)
-
-
-@pytest.fixture
-def project_issue(project):
- return project.issues.get(1, lazy=True)
-
-
-@pytest.fixture
-def project_merge_request(project):
- return project.mergerequests.get(1, lazy=True)
-
-
-@pytest.fixture
-def release(project, tag_name):
- return project.releases.get(tag_name, lazy=True)
-
-
-@pytest.fixture
-def user(gl):
- return gl.users.get(1, lazy=True)
diff --git a/tests/unit/data/todo.json b/tests/unit/data/todo.json
deleted file mode 100644
index 93b2151..0000000
--- a/tests/unit/data/todo.json
+++ /dev/null
@@ -1,75 +0,0 @@
-[
- {
- "id": 102,
- "project": {
- "id": 2,
- "name": "Gitlab Ce",
- "name_with_namespace": "Gitlab Org / Gitlab Ce",
- "path": "gitlab-ce",
- "path_with_namespace": "gitlab-org/gitlab-ce"
- },
- "author": {
- "name": "Administrator",
- "username": "root",
- "id": 1,
- "state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
- "web_url": "https://gitlab.example.com/root"
- },
- "action_name": "marked",
- "target_type": "MergeRequest",
- "target": {
- "id": 34,
- "iid": 7,
- "project_id": 2,
- "title": "Dolores in voluptatem tenetur praesentium omnis repellendus voluptatem quaerat.",
- "description": "Et ea et omnis illum cupiditate. Dolor aspernatur tenetur ducimus facilis est nihil. Quo esse cupiditate molestiae illo corrupti qui quidem dolor.",
- "state": "opened",
- "created_at": "2016-06-17T07:49:24.419Z",
- "updated_at": "2016-06-17T07:52:43.484Z",
- "target_branch": "tutorials_git_tricks",
- "source_branch": "DNSBL_docs",
- "upvotes": 0,
- "downvotes": 0,
- "author": {
- "name": "Maxie Medhurst",
- "username": "craig_rutherford",
- "id": 12,
- "state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/a0d477b3ea21970ce6ffcbb817b0b435?s=80&d=identicon",
- "web_url": "https://gitlab.example.com/craig_rutherford"
- },
- "assignee": {
- "name": "Administrator",
- "username": "root",
- "id": 1,
- "state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
- "web_url": "https://gitlab.example.com/root"
- },
- "source_project_id": 2,
- "target_project_id": 2,
- "labels": [],
- "work_in_progress": false,
- "milestone": {
- "id": 32,
- "iid": 2,
- "project_id": 2,
- "title": "v1.0",
- "description": "Assumenda placeat ea voluptatem voluptate qui.",
- "state": "active",
- "created_at": "2016-06-17T07:47:34.163Z",
- "updated_at": "2016-06-17T07:47:34.163Z",
- "due_date": null
- },
- "merge_when_pipeline_succeeds": false,
- "merge_status": "cannot_be_merged",
- "subscribed": true,
- "user_notes_count": 7
- },
- "target_url": "https://gitlab.example.com/gitlab-org/gitlab-ce/merge_requests/7",
- "body": "Dolores in voluptatem tenetur praesentium omnis repellendus voluptatem quaerat.",
- "state": "pending",
- "created_at": "2016-06-17T07:52:35.225Z"
- }
-]
diff --git a/tests/unit/mixins/__init__.py b/tests/unit/mixins/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tests/unit/mixins/__init__.py
+++ /dev/null
diff --git a/tests/unit/mixins/test_meta_mixins.py b/tests/unit/mixins/test_meta_mixins.py
deleted file mode 100644
index 4c8845b..0000000
--- a/tests/unit/mixins/test_meta_mixins.py
+++ /dev/null
@@ -1,58 +0,0 @@
-from gitlab.mixins import (
- CreateMixin,
- CRUDMixin,
- DeleteMixin,
- GetMixin,
- ListMixin,
- NoUpdateMixin,
- RetrieveMixin,
- UpdateMixin,
-)
-
-
-def test_retrieve_mixin():
- class M(RetrieveMixin):
- pass
-
- obj = M()
- assert hasattr(obj, "list")
- assert hasattr(obj, "get")
- assert not hasattr(obj, "create")
- assert not hasattr(obj, "update")
- assert not hasattr(obj, "delete")
- assert isinstance(obj, ListMixin)
- assert isinstance(obj, GetMixin)
-
-
-def test_crud_mixin():
- class M(CRUDMixin):
- pass
-
- obj = M()
- assert hasattr(obj, "get")
- assert hasattr(obj, "list")
- assert hasattr(obj, "create")
- assert hasattr(obj, "update")
- assert hasattr(obj, "delete")
- assert isinstance(obj, ListMixin)
- assert isinstance(obj, GetMixin)
- assert isinstance(obj, CreateMixin)
- assert isinstance(obj, UpdateMixin)
- assert isinstance(obj, DeleteMixin)
-
-
-def test_no_update_mixin():
- class M(NoUpdateMixin):
- pass
-
- obj = M()
- assert hasattr(obj, "get")
- assert hasattr(obj, "list")
- assert hasattr(obj, "create")
- assert not hasattr(obj, "update")
- assert hasattr(obj, "delete")
- assert isinstance(obj, ListMixin)
- assert isinstance(obj, GetMixin)
- assert isinstance(obj, CreateMixin)
- assert not isinstance(obj, UpdateMixin)
- assert isinstance(obj, DeleteMixin)
diff --git a/tests/unit/mixins/test_mixin_methods.py b/tests/unit/mixins/test_mixin_methods.py
deleted file mode 100644
index 626230e..0000000
--- a/tests/unit/mixins/test_mixin_methods.py
+++ /dev/null
@@ -1,300 +0,0 @@
-import pytest
-from httmock import HTTMock, response, urlmatch # noqa
-
-from gitlab import base
-from gitlab.mixins import (
- CreateMixin,
- DeleteMixin,
- GetMixin,
- GetWithoutIdMixin,
- ListMixin,
- RefreshMixin,
- SaveMixin,
- SetMixin,
- UpdateMixin,
-)
-
-
-class FakeObject(base.RESTObject):
- pass
-
-
-class FakeManager(base.RESTManager):
- _path = "/tests"
- _obj_cls = FakeObject
-
-
-def test_get_mixin(gl):
- class M(GetMixin, FakeManager):
- pass
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/tests/42", method="get")
- def resp_cont(url, request):
- headers = {"Content-Type": "application/json"}
- content = '{"id": 42, "foo": "bar"}'
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- mgr = M(gl)
- obj = mgr.get(42)
- assert isinstance(obj, FakeObject)
- assert obj.foo == "bar"
- assert obj.id == 42
-
-
-def test_refresh_mixin(gl):
- class TestClass(RefreshMixin, FakeObject):
- pass
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/tests/42", method="get")
- def resp_cont(url, request):
- headers = {"Content-Type": "application/json"}
- content = '{"id": 42, "foo": "bar"}'
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- mgr = FakeManager(gl)
- obj = TestClass(mgr, {"id": 42})
- res = obj.refresh()
- assert res is None
- assert obj.foo == "bar"
- assert obj.id == 42
-
-
-def test_get_without_id_mixin(gl):
- class M(GetWithoutIdMixin, FakeManager):
- pass
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/tests", method="get")
- def resp_cont(url, request):
- headers = {"Content-Type": "application/json"}
- content = '{"foo": "bar"}'
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- mgr = M(gl)
- obj = mgr.get()
- assert isinstance(obj, FakeObject)
- assert obj.foo == "bar"
- assert not hasattr(obj, "id")
-
-
-def test_list_mixin(gl):
- class M(ListMixin, FakeManager):
- pass
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/tests", method="get")
- def resp_cont(url, request):
- headers = {"Content-Type": "application/json"}
- content = '[{"id": 42, "foo": "bar"},{"id": 43, "foo": "baz"}]'
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- # test RESTObjectList
- mgr = M(gl)
- obj_list = mgr.list(as_list=False)
- assert isinstance(obj_list, base.RESTObjectList)
- for obj in obj_list:
- assert isinstance(obj, FakeObject)
- assert obj.id in (42, 43)
-
- # test list()
- obj_list = mgr.list(all=True)
- assert isinstance(obj_list, list)
- assert obj_list[0].id == 42
- assert obj_list[1].id == 43
- assert isinstance(obj_list[0], FakeObject)
- assert len(obj_list) == 2
-
-
-def test_list_other_url(gl):
- class M(ListMixin, FakeManager):
- pass
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/others", method="get")
- def resp_cont(url, request):
- headers = {"Content-Type": "application/json"}
- content = '[{"id": 42, "foo": "bar"}]'
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- mgr = M(gl)
- obj_list = mgr.list(path="/others", as_list=False)
- assert isinstance(obj_list, base.RESTObjectList)
- obj = obj_list.next()
- assert obj.id == 42
- assert obj.foo == "bar"
- with pytest.raises(StopIteration):
- obj_list.next()
-
-
-def test_create_mixin_missing_attrs(gl):
- class M(CreateMixin, FakeManager):
- _create_attrs = base.RequiredOptional(
- required=("foo",), optional=("bar", "baz")
- )
-
- mgr = M(gl)
- data = {"foo": "bar", "baz": "blah"}
- mgr._check_missing_create_attrs(data)
-
- data = {"baz": "blah"}
- with pytest.raises(AttributeError) as error:
- mgr._check_missing_create_attrs(data)
- assert "foo" in str(error.value)
-
-
-def test_create_mixin(gl):
- class M(CreateMixin, FakeManager):
- _create_attrs = base.RequiredOptional(
- required=("foo",), optional=("bar", "baz")
- )
- _update_attrs = base.RequiredOptional(required=("foo",), optional=("bam",))
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/tests", method="post")
- def resp_cont(url, request):
- headers = {"Content-Type": "application/json"}
- content = '{"id": 42, "foo": "bar"}'
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- mgr = M(gl)
- obj = mgr.create({"foo": "bar"})
- assert isinstance(obj, FakeObject)
- assert obj.id == 42
- assert obj.foo == "bar"
-
-
-def test_create_mixin_custom_path(gl):
- class M(CreateMixin, FakeManager):
- _create_attrs = base.RequiredOptional(
- required=("foo",), optional=("bar", "baz")
- )
- _update_attrs = base.RequiredOptional(required=("foo",), optional=("bam",))
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/others", method="post")
- def resp_cont(url, request):
- headers = {"Content-Type": "application/json"}
- content = '{"id": 42, "foo": "bar"}'
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- mgr = M(gl)
- obj = mgr.create({"foo": "bar"}, path="/others")
- assert isinstance(obj, FakeObject)
- assert obj.id == 42
- assert obj.foo == "bar"
-
-
-def test_update_mixin_missing_attrs(gl):
- class M(UpdateMixin, FakeManager):
- _update_attrs = base.RequiredOptional(
- required=("foo",), optional=("bar", "baz")
- )
-
- mgr = M(gl)
- data = {"foo": "bar", "baz": "blah"}
- mgr._check_missing_update_attrs(data)
-
- data = {"baz": "blah"}
- with pytest.raises(AttributeError) as error:
- mgr._check_missing_update_attrs(data)
- assert "foo" in str(error.value)
-
-
-def test_update_mixin(gl):
- class M(UpdateMixin, FakeManager):
- _create_attrs = base.RequiredOptional(
- required=("foo",), optional=("bar", "baz")
- )
- _update_attrs = base.RequiredOptional(required=("foo",), optional=("bam",))
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/tests/42", method="put")
- def resp_cont(url, request):
- headers = {"Content-Type": "application/json"}
- content = '{"id": 42, "foo": "baz"}'
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- mgr = M(gl)
- server_data = mgr.update(42, {"foo": "baz"})
- assert isinstance(server_data, dict)
- assert server_data["id"] == 42
- assert server_data["foo"] == "baz"
-
-
-def test_update_mixin_no_id(gl):
- class M(UpdateMixin, FakeManager):
- _create_attrs = base.RequiredOptional(
- required=("foo",), optional=("bar", "baz")
- )
- _update_attrs = base.RequiredOptional(required=("foo",), optional=("bam",))
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/tests", method="put")
- def resp_cont(url, request):
- headers = {"Content-Type": "application/json"}
- content = '{"foo": "baz"}'
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- mgr = M(gl)
- server_data = mgr.update(new_data={"foo": "baz"})
- assert isinstance(server_data, dict)
- assert server_data["foo"] == "baz"
-
-
-def test_delete_mixin(gl):
- class M(DeleteMixin, FakeManager):
- pass
-
- @urlmatch(
- scheme="http", netloc="localhost", path="/api/v4/tests/42", method="delete"
- )
- def resp_cont(url, request):
- headers = {"Content-Type": "application/json"}
- content = ""
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- mgr = M(gl)
- mgr.delete(42)
-
-
-def test_save_mixin(gl):
- class M(UpdateMixin, FakeManager):
- pass
-
- class TestClass(SaveMixin, base.RESTObject):
- pass
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/tests/42", method="put")
- def resp_cont(url, request):
- headers = {"Content-Type": "application/json"}
- content = '{"id": 42, "foo": "baz"}'
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- mgr = M(gl)
- obj = TestClass(mgr, {"id": 42, "foo": "bar"})
- obj.foo = "baz"
- obj.save()
- assert obj._attrs["foo"] == "baz"
- assert obj._updated_attrs == {}
-
-
-def test_set_mixin(gl):
- class M(SetMixin, FakeManager):
- pass
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/tests/foo", method="put")
- def resp_cont(url, request):
- headers = {"Content-Type": "application/json"}
- content = '{"key": "foo", "value": "bar"}'
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- mgr = M(gl)
- obj = mgr.set("foo", "bar")
- assert isinstance(obj, FakeObject)
- assert obj.key == "foo"
- assert obj.value == "bar"
diff --git a/tests/unit/mixins/test_object_mixins_attributes.py b/tests/unit/mixins/test_object_mixins_attributes.py
deleted file mode 100644
index d54fa3a..0000000
--- a/tests/unit/mixins/test_object_mixins_attributes.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2014 Mika Mäenpää <mika.j.maenpaa@tut.fi>,
-# Tampere University of Technology
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-from gitlab.mixins import (
- AccessRequestMixin,
- SetMixin,
- SubscribableMixin,
- TimeTrackingMixin,
- TodoMixin,
- UserAgentDetailMixin,
-)
-
-
-def test_access_request_mixin():
- class TestClass(AccessRequestMixin):
- pass
-
- obj = TestClass()
- assert hasattr(obj, "approve")
-
-
-def test_subscribable_mixin():
- class TestClass(SubscribableMixin):
- pass
-
- obj = TestClass()
- assert hasattr(obj, "subscribe")
- assert hasattr(obj, "unsubscribe")
-
-
-def test_todo_mixin():
- class TestClass(TodoMixin):
- pass
-
- obj = TestClass()
- assert hasattr(obj, "todo")
-
-
-def test_time_tracking_mixin():
- class TestClass(TimeTrackingMixin):
- pass
-
- obj = TestClass()
- assert hasattr(obj, "time_stats")
- assert hasattr(obj, "time_estimate")
- assert hasattr(obj, "reset_time_estimate")
- assert hasattr(obj, "add_spent_time")
- assert hasattr(obj, "reset_spent_time")
-
-
-def test_set_mixin():
- class TestClass(SetMixin):
- pass
-
- obj = TestClass()
- assert hasattr(obj, "set")
-
-
-def test_user_agent_detail_mixin():
- class TestClass(UserAgentDetailMixin):
- pass
-
- obj = TestClass()
- assert hasattr(obj, "user_agent_detail")
diff --git a/tests/unit/objects/__init__.py b/tests/unit/objects/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tests/unit/objects/__init__.py
+++ /dev/null
diff --git a/tests/unit/objects/conftest.py b/tests/unit/objects/conftest.py
deleted file mode 100644
index d8a40d9..0000000
--- a/tests/unit/objects/conftest.py
+++ /dev/null
@@ -1,70 +0,0 @@
-"""Common mocks for resources in gitlab.v4.objects"""
-
-import re
-
-import pytest
-import responses
-
-
-@pytest.fixture
-def binary_content():
- return b"binary content"
-
-
-@pytest.fixture
-def accepted_content():
- return {"message": "202 Accepted"}
-
-
-@pytest.fixture
-def created_content():
- return {"message": "201 Created"}
-
-
-@pytest.fixture
-def no_content():
- return {"message": "204 No Content"}
-
-
-@pytest.fixture
-def resp_export(accepted_content, binary_content):
- """Common fixture for group and project exports."""
- export_status_content = {
- "id": 1,
- "description": "Itaque perspiciatis minima aspernatur",
- "name": "Gitlab Test",
- "name_with_namespace": "Gitlab Org / Gitlab Test",
- "path": "gitlab-test",
- "path_with_namespace": "gitlab-org/gitlab-test",
- "created_at": "2017-08-29T04:36:44.383Z",
- "export_status": "finished",
- "_links": {
- "api_url": "https://gitlab.test/api/v4/projects/1/export/download",
- "web_url": "https://gitlab.test/gitlab-test/download_export",
- },
- }
-
- with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
- rsps.add(
- method=responses.POST,
- url=re.compile(r".*/api/v4/(groups|projects)/1/export"),
- json=accepted_content,
- content_type="application/json",
- status=202,
- )
- rsps.add(
- method=responses.GET,
- url=re.compile(r".*/api/v4/(groups|projects)/1/export/download"),
- body=binary_content,
- content_type="application/octet-stream",
- status=200,
- )
- # Currently only project export supports status checks
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/export",
- json=export_status_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
diff --git a/tests/unit/objects/test_appearance.py b/tests/unit/objects/test_appearance.py
deleted file mode 100644
index 0de6524..0000000
--- a/tests/unit/objects/test_appearance.py
+++ /dev/null
@@ -1,65 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ce/api/appearance.html
-"""
-
-import pytest
-import responses
-
-title = "GitLab Test Instance"
-description = "gitlab-test.example.com"
-new_title = "new-title"
-new_description = "new-description"
-
-
-@pytest.fixture
-def resp_application_appearance():
- content = {
- "title": title,
- "description": description,
- "logo": "/uploads/-/system/appearance/logo/1/logo.png",
- "header_logo": "/uploads/-/system/appearance/header_logo/1/header.png",
- "favicon": "/uploads/-/system/appearance/favicon/1/favicon.png",
- "new_project_guidelines": "Please read the FAQs for help.",
- "header_message": "",
- "footer_message": "",
- "message_background_color": "#e75e40",
- "message_font_color": "#ffffff",
- "email_header_and_footer_enabled": False,
- }
-
- with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/application/appearance",
- json=content,
- content_type="application/json",
- status=200,
- )
-
- updated_content = dict(content)
- updated_content["title"] = new_title
- updated_content["description"] = new_description
-
- rsps.add(
- method=responses.PUT,
- url="http://localhost/api/v4/application/appearance",
- json=updated_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-def test_get_update_appearance(gl, resp_application_appearance):
- appearance = gl.appearance.get()
- assert appearance.title == title
- assert appearance.description == description
- appearance.title = new_title
- appearance.description = new_description
- appearance.save()
- assert appearance.title == new_title
- assert appearance.description == new_description
-
-
-def test_update_appearance(gl, resp_application_appearance):
- gl.appearance.update(title=new_title, description=new_description)
diff --git a/tests/unit/objects/test_applications.py b/tests/unit/objects/test_applications.py
deleted file mode 100644
index 61de019..0000000
--- a/tests/unit/objects/test_applications.py
+++ /dev/null
@@ -1,44 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ce/api/applications.html
-"""
-
-import pytest
-import responses
-
-title = "GitLab Test Instance"
-description = "gitlab-test.example.com"
-new_title = "new-title"
-new_description = "new-description"
-
-
-@pytest.fixture
-def resp_application_create():
- content = {
- "name": "test_app",
- "redirect_uri": "http://localhost:8080",
- "scopes": ["api", "email"],
- }
-
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.POST,
- url="http://localhost/api/v4/applications",
- json=content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-def test_create_application(gl, resp_application_create):
- application = gl.applications.create(
- {
- "name": "test_app",
- "redirect_uri": "http://localhost:8080",
- "scopes": ["api", "email"],
- "confidential": False,
- }
- )
- assert application.name == "test_app"
- assert application.redirect_uri == "http://localhost:8080"
- assert application.scopes == ["api", "email"]
diff --git a/tests/unit/objects/test_audit_events.py b/tests/unit/objects/test_audit_events.py
deleted file mode 100644
index aba778b..0000000
--- a/tests/unit/objects/test_audit_events.py
+++ /dev/null
@@ -1,109 +0,0 @@
-"""
-GitLab API:
-https://docs.gitlab.com/ee/api/audit_events.html#project-audit-events
-"""
-
-import re
-
-import pytest
-import responses
-
-from gitlab.v4.objects.audit_events import (
- AuditEvent,
- GroupAuditEvent,
- ProjectAuditEvent,
-)
-
-id = 5
-
-audit_events_content = {
- "id": 5,
- "author_id": 1,
- "entity_id": 7,
- "entity_type": "Project",
- "details": {
- "change": "prevent merge request approval from reviewers",
- "from": "",
- "to": "true",
- "author_name": "Administrator",
- "target_id": 7,
- "target_type": "Project",
- "target_details": "twitter/typeahead-js",
- "ip_address": "127.0.0.1",
- "entity_path": "twitter/typeahead-js",
- },
- "created_at": "2020-05-26T22:55:04.230Z",
-}
-
-audit_events_url = re.compile(
- r"http://localhost/api/v4/((groups|projects)/1/)?audit_events"
-)
-
-audit_events_url_id = re.compile(
- rf"http://localhost/api/v4/((groups|projects)/1/)?audit_events/{id}"
-)
-
-
-@pytest.fixture
-def resp_list_audit_events():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url=audit_events_url,
- json=[audit_events_content],
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_get_audit_event():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url=audit_events_url_id,
- json=audit_events_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-def test_list_instance_audit_events(gl, resp_list_audit_events):
- audit_events = gl.audit_events.list()
- assert isinstance(audit_events, list)
- assert isinstance(audit_events[0], AuditEvent)
- assert audit_events[0].id == id
-
-
-def test_get_instance_audit_events(gl, resp_get_audit_event):
- audit_event = gl.audit_events.get(id)
- assert isinstance(audit_event, AuditEvent)
- assert audit_event.id == id
-
-
-def test_list_group_audit_events(group, resp_list_audit_events):
- audit_events = group.audit_events.list()
- assert isinstance(audit_events, list)
- assert isinstance(audit_events[0], GroupAuditEvent)
- assert audit_events[0].id == id
-
-
-def test_get_group_audit_events(group, resp_get_audit_event):
- audit_event = group.audit_events.get(id)
- assert isinstance(audit_event, GroupAuditEvent)
- assert audit_event.id == id
-
-
-def test_list_project_audit_events(project, resp_list_audit_events):
- audit_events = project.audit_events.list()
- assert isinstance(audit_events, list)
- assert isinstance(audit_events[0], ProjectAuditEvent)
- assert audit_events[0].id == id
-
-
-def test_get_project_audit_events(project, resp_get_audit_event):
- audit_event = project.audit_events.get(id)
- assert isinstance(audit_event, ProjectAuditEvent)
- assert audit_event.id == id
diff --git a/tests/unit/objects/test_badges.py b/tests/unit/objects/test_badges.py
deleted file mode 100644
index e226684..0000000
--- a/tests/unit/objects/test_badges.py
+++ /dev/null
@@ -1,210 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ee/api/project_badges.html
-GitLab API: https://docs.gitlab.com/ee/api/group_badges.html
-"""
-import re
-
-import pytest
-import responses
-
-from gitlab.v4.objects import GroupBadge, ProjectBadge
-
-link_url = (
- "http://example.com/ci_status.svg?project=example-org/example-project&ref=master"
-)
-image_url = "https://example.io/my/badge"
-
-rendered_link_url = (
- "http://example.com/ci_status.svg?project=example-org/example-project&ref=master"
-)
-rendered_image_url = "https://example.io/my/badge"
-
-new_badge = {
- "link_url": link_url,
- "image_url": image_url,
-}
-
-badge_content = {
- "name": "Coverage",
- "id": 1,
- "link_url": link_url,
- "image_url": image_url,
- "rendered_link_url": rendered_image_url,
- "rendered_image_url": rendered_image_url,
-}
-
-preview_badge_content = {
- "link_url": link_url,
- "image_url": image_url,
- "rendered_link_url": rendered_link_url,
- "rendered_image_url": rendered_image_url,
-}
-
-
-@pytest.fixture()
-def resp_get_badge():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url=re.compile(r"http://localhost/api/v4/(projects|groups)/1/badges/1"),
- json=badge_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture()
-def resp_list_badges():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url=re.compile(r"http://localhost/api/v4/(projects|groups)/1/badges"),
- json=[badge_content],
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture()
-def resp_create_badge():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.POST,
- url=re.compile(r"http://localhost/api/v4/(projects|groups)/1/badges"),
- json=badge_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture()
-def resp_update_badge():
- updated_content = dict(badge_content)
- updated_content["link_url"] = "http://link_url"
-
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.PUT,
- url=re.compile(r"http://localhost/api/v4/(projects|groups)/1/badges/1"),
- json=updated_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture()
-def resp_delete_badge(no_content):
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.DELETE,
- url=re.compile(r"http://localhost/api/v4/(projects|groups)/1/badges/1"),
- json=no_content,
- content_type="application/json",
- status=204,
- )
- yield rsps
-
-
-@pytest.fixture()
-def resp_preview_badge():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url=re.compile(
- r"http://localhost/api/v4/(projects|groups)/1/badges/render"
- ),
- json=preview_badge_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-def test_list_project_badges(project, resp_list_badges):
- badges = project.badges.list()
- assert isinstance(badges, list)
- assert isinstance(badges[0], ProjectBadge)
-
-
-def test_list_group_badges(group, resp_list_badges):
- badges = group.badges.list()
- assert isinstance(badges, list)
- assert isinstance(badges[0], GroupBadge)
-
-
-def test_get_project_badge(project, resp_get_badge):
- badge = project.badges.get(1)
- assert isinstance(badge, ProjectBadge)
- assert badge.name == "Coverage"
- assert badge.id == 1
-
-
-def test_get_group_badge(group, resp_get_badge):
- badge = group.badges.get(1)
- assert isinstance(badge, GroupBadge)
- assert badge.name == "Coverage"
- assert badge.id == 1
-
-
-def test_delete_project_badge(project, resp_delete_badge):
- badge = project.badges.get(1, lazy=True)
- badge.delete()
-
-
-def test_delete_group_badge(group, resp_delete_badge):
- badge = group.badges.get(1, lazy=True)
- badge.delete()
-
-
-def test_create_project_badge(project, resp_create_badge):
- badge = project.badges.create(new_badge)
- assert isinstance(badge, ProjectBadge)
- assert badge.image_url == image_url
-
-
-def test_create_group_badge(group, resp_create_badge):
- badge = group.badges.create(new_badge)
- assert isinstance(badge, GroupBadge)
- assert badge.image_url == image_url
-
-
-def test_preview_project_badge(project, resp_preview_badge):
- output = project.badges.render(
- link_url=link_url,
- image_url=image_url,
- )
- assert isinstance(output, dict)
- assert "rendered_link_url" in output
- assert "rendered_image_url" in output
- assert output["link_url"] == output["rendered_link_url"]
- assert output["image_url"] == output["rendered_image_url"]
-
-
-def test_preview_group_badge(group, resp_preview_badge):
- output = group.badges.render(
- link_url=link_url,
- image_url=image_url,
- )
- assert isinstance(output, dict)
- assert "rendered_link_url" in output
- assert "rendered_image_url" in output
- assert output["link_url"] == output["rendered_link_url"]
- assert output["image_url"] == output["rendered_image_url"]
-
-
-def test_update_project_badge(project, resp_update_badge):
- badge = project.badges.get(1, lazy=True)
- badge.link_url = "http://link_url"
- badge.save()
- assert badge.link_url == "http://link_url"
-
-
-def test_update_group_badge(group, resp_update_badge):
- badge = group.badges.get(1, lazy=True)
- badge.link_url = "http://link_url"
- badge.save()
- assert badge.link_url == "http://link_url"
diff --git a/tests/unit/objects/test_bridges.py b/tests/unit/objects/test_bridges.py
deleted file mode 100644
index 4d39186..0000000
--- a/tests/unit/objects/test_bridges.py
+++ /dev/null
@@ -1,109 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ee/api/jobs.html#list-pipeline-bridges
-"""
-import pytest
-import responses
-
-from gitlab.v4.objects import ProjectPipelineBridge
-
-
-@pytest.fixture
-def resp_list_bridges():
- export_bridges_content = {
- "commit": {
- "author_email": "admin@example.com",
- "author_name": "Administrator",
- "created_at": "2015-12-24T16:51:14.000+01:00",
- "id": "0ff3ae198f8601a285adcf5c0fff204ee6fba5fd",
- "message": "Test the CI integration.",
- "short_id": "0ff3ae19",
- "title": "Test the CI integration.",
- },
- "allow_failure": False,
- "created_at": "2015-12-24T15:51:21.802Z",
- "started_at": "2015-12-24T17:54:27.722Z",
- "finished_at": "2015-12-24T17:58:27.895Z",
- "duration": 240,
- "id": 7,
- "name": "teaspoon",
- "pipeline": {
- "id": 6,
- "ref": "master",
- "sha": "0ff3ae198f8601a285adcf5c0fff204ee6fba5fd",
- "status": "pending",
- "created_at": "2015-12-24T15:50:16.123Z",
- "updated_at": "2015-12-24T18:00:44.432Z",
- "web_url": "https://example.com/foo/bar/pipelines/6",
- },
- "ref": "master",
- "stage": "test",
- "status": "pending",
- "tag": False,
- "web_url": "https://example.com/foo/bar/-/jobs/7",
- "user": {
- "id": 1,
- "name": "Administrator",
- "username": "root",
- "state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
- "web_url": "http://gitlab.dev/root",
- "created_at": "2015-12-21T13:14:24.077Z",
- "public_email": "",
- "skype": "",
- "linkedin": "",
- "twitter": "",
- "website_url": "",
- "organization": "",
- },
- "downstream_pipeline": {
- "id": 5,
- "sha": "f62a4b2fb89754372a346f24659212eb8da13601",
- "ref": "master",
- "status": "pending",
- "created_at": "2015-12-24T17:54:27.722Z",
- "updated_at": "2015-12-24T17:58:27.896Z",
- "web_url": "https://example.com/diaspora/diaspora-client/pipelines/5",
- },
- }
-
- export_pipelines_content = [
- {
- "id": 6,
- "status": "pending",
- "ref": "new-pipeline",
- "sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a",
- "web_url": "https://example.com/foo/bar/pipelines/47",
- "created_at": "2016-08-11T11:28:34.085Z",
- "updated_at": "2016-08-11T11:32:35.169Z",
- },
- ]
-
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/pipelines/6/bridges",
- json=[export_bridges_content],
- content_type="application/json",
- status=200,
- )
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/pipelines",
- json=export_pipelines_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-def test_list_projects_pipelines_bridges(project, resp_list_bridges):
- pipeline = project.pipelines.list()[0]
- bridges = pipeline.bridges.list()
-
- assert isinstance(bridges, list)
- assert isinstance(bridges[0], ProjectPipelineBridge)
- assert bridges[0].downstream_pipeline["id"] == 5
- assert (
- bridges[0].downstream_pipeline["sha"]
- == "f62a4b2fb89754372a346f24659212eb8da13601"
- )
diff --git a/tests/unit/objects/test_commits.py b/tests/unit/objects/test_commits.py
deleted file mode 100644
index 6b98117..0000000
--- a/tests/unit/objects/test_commits.py
+++ /dev/null
@@ -1,115 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ce/api/commits.html
-"""
-
-import pytest
-import responses
-
-
-@pytest.fixture
-def resp_create_commit():
- content = {
- "id": "ed899a2f4b50b4370feeea94676502b42383c746",
- "short_id": "ed899a2f",
- "title": "Commit message",
- }
-
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.POST,
- url="http://localhost/api/v4/projects/1/repository/commits",
- json=content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_commit():
- get_content = {
- "id": "6b2257eabcec3db1f59dafbd84935e3caea04235",
- "short_id": "6b2257ea",
- "title": "Initial commit",
- }
- revert_content = {
- "id": "8b090c1b79a14f2bd9e8a738f717824ff53aebad",
- "short_id": "8b090c1b",
- "title": 'Revert "Initial commit"',
- }
-
- with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/repository/commits/6b2257ea",
- json=get_content,
- content_type="application/json",
- status=200,
- )
- rsps.add(
- method=responses.POST,
- url="http://localhost/api/v4/projects/1/repository/commits/6b2257ea/revert",
- json=revert_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_get_commit_gpg_signature():
- content = {
- "gpg_key_id": 1,
- "gpg_key_primary_keyid": "8254AAB3FBD54AC9",
- "gpg_key_user_name": "John Doe",
- "gpg_key_user_email": "johndoe@example.com",
- "verification_status": "verified",
- "gpg_key_subkey_id": None,
- }
-
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/repository/commits/6b2257ea/signature",
- json=content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-def test_get_commit(project, resp_commit):
- commit = project.commits.get("6b2257ea")
- assert commit.short_id == "6b2257ea"
- assert commit.title == "Initial commit"
-
-
-def test_create_commit(project, resp_create_commit):
- data = {
- "branch": "master",
- "commit_message": "Commit message",
- "actions": [
- {
- "action": "create",
- "file_path": "README",
- "content": "",
- }
- ],
- }
- commit = project.commits.create(data)
- assert commit.short_id == "ed899a2f"
- assert commit.title == data["commit_message"]
-
-
-def test_revert_commit(project, resp_commit):
- commit = project.commits.get("6b2257ea", lazy=True)
- revert_commit = commit.revert(branch="master")
- assert revert_commit["short_id"] == "8b090c1b"
- assert revert_commit["title"] == 'Revert "Initial commit"'
-
-
-def test_get_commit_gpg_signature(project, resp_get_commit_gpg_signature):
- commit = project.commits.get("6b2257ea", lazy=True)
- signature = commit.signature()
- assert signature["gpg_key_primary_keyid"] == "8254AAB3FBD54AC9"
- assert signature["verification_status"] == "verified"
diff --git a/tests/unit/objects/test_deploy_tokens.py b/tests/unit/objects/test_deploy_tokens.py
deleted file mode 100644
index 66a79fa..0000000
--- a/tests/unit/objects/test_deploy_tokens.py
+++ /dev/null
@@ -1,45 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ce/api/deploy_tokens.html
-"""
-import pytest
-import responses
-
-from gitlab.v4.objects import ProjectDeployToken
-
-create_content = {
- "id": 1,
- "name": "test_deploy_token",
- "username": "custom-user",
- "expires_at": "2022-01-01T00:00:00.000Z",
- "token": "jMRvtPNxrn3crTAGukpZ",
- "scopes": ["read_repository"],
-}
-
-
-@pytest.fixture
-def resp_deploy_token_create():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.POST,
- url="http://localhost/api/v4/projects/1/deploy_tokens",
- json=create_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-def test_deploy_tokens(gl, resp_deploy_token_create):
- deploy_token = gl.projects.get(1, lazy=True).deploytokens.create(
- {
- "name": "test_deploy_token",
- "expires_at": "2022-01-01T00:00:00.000Z",
- "username": "custom-user",
- "scopes": ["read_repository"],
- }
- )
- assert isinstance(deploy_token, ProjectDeployToken)
- assert deploy_token.id == 1
- assert deploy_token.expires_at == "2022-01-01T00:00:00.000Z"
- assert deploy_token.username == "custom-user"
- assert deploy_token.scopes == ["read_repository"]
diff --git a/tests/unit/objects/test_deployments.py b/tests/unit/objects/test_deployments.py
deleted file mode 100644
index 3cde8fe..0000000
--- a/tests/unit/objects/test_deployments.py
+++ /dev/null
@@ -1,50 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ce/api/deployments.html
-"""
-import pytest
-import responses
-
-
-@pytest.fixture
-def resp_deployment():
- content = {"id": 42, "status": "success", "ref": "master"}
-
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.POST,
- url="http://localhost/api/v4/projects/1/deployments",
- json=content,
- content_type="application/json",
- status=200,
- )
-
- updated_content = dict(content)
- updated_content["status"] = "failed"
-
- rsps.add(
- method=responses.PUT,
- url="http://localhost/api/v4/projects/1/deployments/42",
- json=updated_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-def test_deployment(project, resp_deployment):
- deployment = project.deployments.create(
- {
- "environment": "Test",
- "sha": "1agf4gs",
- "ref": "master",
- "tag": False,
- "status": "created",
- }
- )
- assert deployment.id == 42
- assert deployment.status == "success"
- assert deployment.ref == "master"
-
- deployment.status = "failed"
- deployment.save()
- assert deployment.status == "failed"
diff --git a/tests/unit/objects/test_environments.py b/tests/unit/objects/test_environments.py
deleted file mode 100644
index b49a1db..0000000
--- a/tests/unit/objects/test_environments.py
+++ /dev/null
@@ -1,30 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ce/api/environments.html
-"""
-import pytest
-import responses
-
-from gitlab.v4.objects import ProjectEnvironment
-
-
-@pytest.fixture
-def resp_get_environment():
- content = {"name": "environment_name", "id": 1, "last_deployment": "sometime"}
-
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/environments/1",
- json=content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-def test_project_environments(project, resp_get_environment):
- environment = project.environments.get(1)
- assert isinstance(environment, ProjectEnvironment)
- assert environment.id == 1
- assert environment.last_deployment == "sometime"
- assert environment.name == "environment_name"
diff --git a/tests/unit/objects/test_groups.py b/tests/unit/objects/test_groups.py
deleted file mode 100644
index 37023d8..0000000
--- a/tests/unit/objects/test_groups.py
+++ /dev/null
@@ -1,155 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ce/api/groups.html
-"""
-
-import re
-
-import pytest
-import responses
-
-import gitlab
-from gitlab.v4.objects import GroupDescendantGroup, GroupSubgroup
-
-subgroup_descgroup_content = [
- {
- "id": 2,
- "name": "Bar Group",
- "path": "foo/bar",
- "description": "A subgroup of Foo Group",
- "visibility": "public",
- "share_with_group_lock": False,
- "require_two_factor_authentication": False,
- "two_factor_grace_period": 48,
- "project_creation_level": "developer",
- "auto_devops_enabled": None,
- "subgroup_creation_level": "owner",
- "emails_disabled": None,
- "mentions_disabled": None,
- "lfs_enabled": True,
- "default_branch_protection": 2,
- "avatar_url": "http://gitlab.example.com/uploads/group/avatar/1/bar.jpg",
- "web_url": "http://gitlab.example.com/groups/foo/bar",
- "request_access_enabled": False,
- "full_name": "Bar Group",
- "full_path": "foo/bar",
- "file_template_project_id": 1,
- "parent_id": 123,
- "created_at": "2020-01-15T12:36:29.590Z",
- },
-]
-
-
-@pytest.fixture
-def resp_groups():
- content = {"name": "name", "id": 1, "path": "path"}
-
- with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/groups/1",
- json=content,
- content_type="application/json",
- status=200,
- )
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/groups",
- json=[content],
- content_type="application/json",
- status=200,
- )
- rsps.add(
- method=responses.POST,
- url="http://localhost/api/v4/groups",
- json=content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_list_subgroups_descendant_groups():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url=re.compile(
- r"http://localhost/api/v4/groups/1/(subgroups|descendant_groups)"
- ),
- json=subgroup_descgroup_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_create_import(accepted_content):
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.POST,
- url="http://localhost/api/v4/groups/import",
- json=accepted_content,
- content_type="application/json",
- status=202,
- )
- yield rsps
-
-
-def test_get_group(gl, resp_groups):
- data = gl.groups.get(1)
- assert isinstance(data, gitlab.v4.objects.Group)
- assert data.name == "name"
- assert data.path == "path"
- assert data.id == 1
-
-
-def test_create_group(gl, resp_groups):
- name, path = "name", "path"
- data = gl.groups.create({"name": name, "path": path})
- assert isinstance(data, gitlab.v4.objects.Group)
- assert data.name == name
- assert data.path == path
-
-
-def test_create_group_export(group, resp_export):
- export = group.exports.create()
- assert export.message == "202 Accepted"
-
-
-def test_list_group_subgroups(group, resp_list_subgroups_descendant_groups):
- subgroups = group.subgroups.list()
- assert isinstance(subgroups[0], GroupSubgroup)
- assert subgroups[0].path == subgroup_descgroup_content[0]["path"]
-
-
-def test_list_group_descendant_groups(group, resp_list_subgroups_descendant_groups):
- descendant_groups = group.descendant_groups.list()
- assert isinstance(descendant_groups[0], GroupDescendantGroup)
- assert descendant_groups[0].path == subgroup_descgroup_content[0]["path"]
-
-
-@pytest.mark.skip("GitLab API endpoint not implemented")
-def test_refresh_group_export_status(group, resp_export):
- export = group.exports.create()
- export.refresh()
- assert export.export_status == "finished"
-
-
-def test_download_group_export(group, resp_export, binary_content):
- export = group.exports.create()
- download = export.download()
- assert isinstance(download, bytes)
- assert download == binary_content
-
-
-def test_import_group(gl, resp_create_import):
- group_import = gl.groups.import_group("file", "api-group", "API Group")
- assert group_import["message"] == "202 Accepted"
-
-
-@pytest.mark.skip("GitLab API endpoint not implemented")
-def test_refresh_group_import_status(group, resp_groups):
- group_import = group.imports.get()
- group_import.refresh()
- assert group_import.import_status == "finished"
diff --git a/tests/unit/objects/test_hooks.py b/tests/unit/objects/test_hooks.py
deleted file mode 100644
index 0f9dbe2..0000000
--- a/tests/unit/objects/test_hooks.py
+++ /dev/null
@@ -1,209 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ce/api/system_hooks.html
-GitLab API: https://docs.gitlab.com/ce/api/groups.html#hooks
-GitLab API: https://docs.gitlab.com/ee/api/projects.html#hooks
-"""
-
-import re
-
-import pytest
-import responses
-
-from gitlab.v4.objects import GroupHook, Hook, ProjectHook
-
-hooks_content = [
- {
- "id": 1,
- "url": "testurl",
- "push_events": True,
- "tag_push_events": True,
- },
- {
- "id": 2,
- "url": "testurl_second",
- "push_events": False,
- "tag_push_events": False,
- },
-]
-
-hook_content = hooks_content[0]
-
-
-@pytest.fixture
-def resp_hooks_list():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url=re.compile(r"http://localhost/api/v4/((groups|projects)/1/|)hooks"),
- json=hooks_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_hook_get():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url=re.compile(r"http://localhost/api/v4/((groups|projects)/1/|)hooks/1"),
- json=hook_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_hook_create():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.POST,
- url=re.compile(r"http://localhost/api/v4/((groups|projects)/1/|)hooks"),
- json=hook_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_hook_update():
- with responses.RequestsMock() as rsps:
- pattern = re.compile(r"http://localhost/api/v4/((groups|projects)/1/|)hooks/1")
- rsps.add(
- method=responses.GET,
- url=pattern,
- json=hook_content,
- content_type="application/json",
- status=200,
- )
- rsps.add(
- method=responses.PUT,
- url=pattern,
- json=hook_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_hook_delete():
- with responses.RequestsMock() as rsps:
- pattern = re.compile(r"http://localhost/api/v4/((groups|projects)/1/|)hooks/1")
- rsps.add(
- method=responses.GET,
- url=pattern,
- json=hook_content,
- content_type="application/json",
- status=200,
- )
- rsps.add(
- method=responses.DELETE,
- url=pattern,
- status=204,
- )
- yield rsps
-
-
-def test_list_system_hooks(gl, resp_hooks_list):
- hooks = gl.hooks.list()
- assert hooks[0].id == 1
- assert hooks[0].url == "testurl"
- assert hooks[1].id == 2
- assert hooks[1].url == "testurl_second"
-
-
-def test_get_system_hook(gl, resp_hook_get):
- data = gl.hooks.get(1)
- assert isinstance(data, Hook)
- assert data.url == "testurl"
- assert data.id == 1
-
-
-def test_create_system_hook(gl, resp_hook_create):
- hook = gl.hooks.create(hook_content)
- assert hook.url == "testurl"
- assert hook.push_events is True
- assert hook.tag_push_events is True
-
-
-# there is no update method for system hooks
-
-
-def test_delete_system_hook(gl, resp_hook_delete):
- hook = gl.hooks.get(1)
- hook.delete()
- gl.hooks.delete(1)
-
-
-def test_list_group_hooks(group, resp_hooks_list):
- hooks = group.hooks.list()
- assert hooks[0].id == 1
- assert hooks[0].url == "testurl"
- assert hooks[1].id == 2
- assert hooks[1].url == "testurl_second"
-
-
-def test_get_group_hook(group, resp_hook_get):
- data = group.hooks.get(1)
- assert isinstance(data, GroupHook)
- assert data.url == "testurl"
- assert data.id == 1
-
-
-def test_create_group_hook(group, resp_hook_create):
- hook = group.hooks.create(hook_content)
- assert hook.url == "testurl"
- assert hook.push_events is True
- assert hook.tag_push_events is True
-
-
-def test_update_group_hook(group, resp_hook_update):
- hook = group.hooks.get(1)
- assert hook.id == 1
- hook.url = "testurl_more"
- hook.save()
-
-
-def test_delete_group_hook(group, resp_hook_delete):
- hook = group.hooks.get(1)
- hook.delete()
- group.hooks.delete(1)
-
-
-def test_list_project_hooks(project, resp_hooks_list):
- hooks = project.hooks.list()
- assert hooks[0].id == 1
- assert hooks[0].url == "testurl"
- assert hooks[1].id == 2
- assert hooks[1].url == "testurl_second"
-
-
-def test_get_project_hook(project, resp_hook_get):
- data = project.hooks.get(1)
- assert isinstance(data, ProjectHook)
- assert data.url == "testurl"
- assert data.id == 1
-
-
-def test_create_project_hook(project, resp_hook_create):
- hook = project.hooks.create(hook_content)
- assert hook.url == "testurl"
- assert hook.push_events is True
- assert hook.tag_push_events is True
-
-
-def test_update_project_hook(project, resp_hook_update):
- hook = project.hooks.get(1)
- assert hook.id == 1
- hook.url = "testurl_more"
- hook.save()
-
-
-def test_delete_project_hook(project, resp_hook_delete):
- hook = project.hooks.get(1)
- hook.delete()
- project.hooks.delete(1)
diff --git a/tests/unit/objects/test_issues.py b/tests/unit/objects/test_issues.py
deleted file mode 100644
index a4e1454..0000000
--- a/tests/unit/objects/test_issues.py
+++ /dev/null
@@ -1,88 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ce/api/issues.html
-"""
-import re
-
-import pytest
-import responses
-
-from gitlab.v4.objects import (
- GroupIssuesStatistics,
- IssuesStatistics,
- ProjectIssuesStatistics,
-)
-
-
-@pytest.fixture
-def resp_list_issues():
- content = [{"name": "name", "id": 1}, {"name": "other_name", "id": 2}]
-
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/issues",
- json=content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_get_issue():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/issues/1",
- json={"name": "name", "id": 1},
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_issue_statistics():
- content = {"statistics": {"counts": {"all": 20, "closed": 5, "opened": 15}}}
-
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url=re.compile(
- r"http://localhost/api/v4/((groups|projects)/1/)?issues_statistics"
- ),
- json=content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-def test_list_issues(gl, resp_list_issues):
- data = gl.issues.list()
- assert data[1].id == 2
- assert data[1].name == "other_name"
-
-
-def test_get_issue(gl, resp_get_issue):
- issue = gl.issues.get(1)
- assert issue.id == 1
- assert issue.name == "name"
-
-
-def test_get_issues_statistics(gl, resp_issue_statistics):
- statistics = gl.issues_statistics.get()
- assert isinstance(statistics, IssuesStatistics)
- assert statistics.statistics["counts"]["all"] == 20
-
-
-def test_get_group_issues_statistics(group, resp_issue_statistics):
- statistics = group.issues_statistics.get()
- assert isinstance(statistics, GroupIssuesStatistics)
- assert statistics.statistics["counts"]["all"] == 20
-
-
-def test_get_project_issues_statistics(project, resp_issue_statistics):
- statistics = project.issues_statistics.get()
- assert isinstance(statistics, ProjectIssuesStatistics)
- assert statistics.statistics["counts"]["all"] == 20
diff --git a/tests/unit/objects/test_job_artifacts.py b/tests/unit/objects/test_job_artifacts.py
deleted file mode 100644
index 7c5f1df..0000000
--- a/tests/unit/objects/test_job_artifacts.py
+++ /dev/null
@@ -1,30 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ee/api/job_artifacts.html
-"""
-
-import pytest
-import responses
-
-ref_name = "master"
-job = "build"
-
-
-@pytest.fixture
-def resp_artifacts_by_ref_name(binary_content):
- url = f"http://localhost/api/v4/projects/1/jobs/artifacts/{ref_name}/download?job={job}"
-
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url=url,
- body=binary_content,
- content_type="application/octet-stream",
- status=200,
- )
- yield rsps
-
-
-def test_download_artifacts_by_ref_name(gl, binary_content, resp_artifacts_by_ref_name):
- project = gl.projects.get(1, lazy=True)
- artifacts = project.artifacts(ref_name=ref_name, job=job)
- assert artifacts == binary_content
diff --git a/tests/unit/objects/test_jobs.py b/tests/unit/objects/test_jobs.py
deleted file mode 100644
index 104d59d..0000000
--- a/tests/unit/objects/test_jobs.py
+++ /dev/null
@@ -1,96 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ee/api/jobs.html
-"""
-import pytest
-import responses
-
-from gitlab.v4.objects import ProjectJob
-
-job_content = {
- "commit": {
- "author_email": "admin@example.com",
- "author_name": "Administrator",
- },
- "coverage": None,
- "allow_failure": False,
- "created_at": "2015-12-24T15:51:21.880Z",
- "started_at": "2015-12-24T17:54:30.733Z",
- "finished_at": "2015-12-24T17:54:31.198Z",
- "duration": 0.465,
- "queued_duration": 0.010,
- "artifacts_expire_at": "2016-01-23T17:54:31.198Z",
- "tag_list": ["docker runner", "macos-10.15"],
- "id": 1,
- "name": "rubocop",
- "pipeline": {
- "id": 1,
- "project_id": 1,
- },
- "ref": "master",
- "artifacts": [],
- "runner": None,
- "stage": "test",
- "status": "failed",
- "tag": False,
- "web_url": "https://example.com/foo/bar/-/jobs/1",
- "user": {"id": 1},
-}
-
-
-@pytest.fixture
-def resp_get_job():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/jobs/1",
- json=job_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_cancel_job():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.POST,
- url="http://localhost/api/v4/projects/1/jobs/1/cancel",
- json=job_content,
- content_type="application/json",
- status=201,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_retry_job():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.POST,
- url="http://localhost/api/v4/projects/1/jobs/1/retry",
- json=job_content,
- content_type="application/json",
- status=201,
- )
- yield rsps
-
-
-def test_get_project_job(project, resp_get_job):
- job = project.jobs.get(1)
- assert isinstance(job, ProjectJob)
- assert job.ref == "master"
-
-
-def test_cancel_project_job(project, resp_cancel_job):
- job = project.jobs.get(1, lazy=True)
-
- output = job.cancel()
- assert output["ref"] == "master"
-
-
-def test_retry_project_job(project, resp_retry_job):
- job = project.jobs.get(1, lazy=True)
-
- output = job.retry()
- assert output["ref"] == "master"
diff --git a/tests/unit/objects/test_keys.py b/tests/unit/objects/test_keys.py
deleted file mode 100644
index 187a309..0000000
--- a/tests/unit/objects/test_keys.py
+++ /dev/null
@@ -1,54 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ce/api/keys.html
-"""
-import pytest
-import responses
-
-from gitlab.v4.objects import Key
-
-key_content = {"id": 1, "title": "title", "key": "ssh-keytype AAAAC3Nza/key comment"}
-
-
-@pytest.fixture
-def resp_get_key_by_id():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/keys/1",
- json=key_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_get_key_by_fingerprint():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/keys?fingerprint=foo",
- json=key_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-def test_get_key_by_id(gl, resp_get_key_by_id):
- key = gl.keys.get(1)
- assert isinstance(key, Key)
- assert key.id == 1
- assert key.title == "title"
-
-
-def test_get_key_by_fingerprint(gl, resp_get_key_by_fingerprint):
- key = gl.keys.get(fingerprint="foo")
- assert isinstance(key, Key)
- assert key.id == 1
- assert key.title == "title"
-
-
-def test_get_key_missing_attrs(gl):
- with pytest.raises(AttributeError):
- gl.keys.get()
diff --git a/tests/unit/objects/test_members.py b/tests/unit/objects/test_members.py
deleted file mode 100644
index 6a39369..0000000
--- a/tests/unit/objects/test_members.py
+++ /dev/null
@@ -1,58 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ee/api/members.html
-"""
-import pytest
-import responses
-
-from gitlab.v4.objects import GroupBillableMember
-
-billable_members_content = [
- {
- "id": 1,
- "username": "raymond_smith",
- "name": "Raymond Smith",
- "state": "active",
- "avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",
- "web_url": "http://192.168.1.8:3000/root",
- "last_activity_on": "2021-01-27",
- "membership_type": "group_member",
- "removable": True,
- }
-]
-
-
-@pytest.fixture
-def resp_list_billable_group_members():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/groups/1/billable_members",
- json=billable_members_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_delete_billable_group_member(no_content):
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.DELETE,
- url="http://localhost/api/v4/groups/1/billable_members/1",
- json=no_content,
- content_type="application/json",
- status=204,
- )
- yield rsps
-
-
-def test_list_group_billable_members(group, resp_list_billable_group_members):
- billable_members = group.billable_members.list()
- assert isinstance(billable_members, list)
- assert isinstance(billable_members[0], GroupBillableMember)
- assert billable_members[0].removable is True
-
-
-def test_delete_group_billable_member(group, resp_delete_billable_group_member):
- group.billable_members.delete(1)
diff --git a/tests/unit/objects/test_merge_request_pipelines.py b/tests/unit/objects/test_merge_request_pipelines.py
deleted file mode 100644
index 04b04a8..0000000
--- a/tests/unit/objects/test_merge_request_pipelines.py
+++ /dev/null
@@ -1,53 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ee/api/merge_requests.html#list-mr-pipelines
-"""
-import pytest
-import responses
-
-from gitlab.v4.objects import ProjectMergeRequestPipeline
-
-pipeline_content = {
- "id": 1,
- "sha": "959e04d7c7a30600c894bd3c0cd0e1ce7f42c11d",
- "ref": "master",
- "status": "success",
-}
-
-
-@pytest.fixture()
-def resp_list_merge_request_pipelines():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/merge_requests/1/pipelines",
- json=[pipeline_content],
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture()
-def resp_create_merge_request_pipeline():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.POST,
- url="http://localhost/api/v4/projects/1/merge_requests/1/pipelines",
- json=pipeline_content,
- content_type="application/json",
- status=201,
- )
- yield rsps
-
-
-def test_list_merge_requests_pipelines(project, resp_list_merge_request_pipelines):
- pipelines = project.mergerequests.get(1, lazy=True).pipelines.list()
- assert len(pipelines) == 1
- assert isinstance(pipelines[0], ProjectMergeRequestPipeline)
- assert pipelines[0].sha == pipeline_content["sha"]
-
-
-def test_create_merge_requests_pipelines(project, resp_create_merge_request_pipeline):
- pipeline = project.mergerequests.get(1, lazy=True).pipelines.create()
- assert isinstance(pipeline, ProjectMergeRequestPipeline)
- assert pipeline.sha == pipeline_content["sha"]
diff --git a/tests/unit/objects/test_merge_requests.py b/tests/unit/objects/test_merge_requests.py
deleted file mode 100644
index ee11f8a..0000000
--- a/tests/unit/objects/test_merge_requests.py
+++ /dev/null
@@ -1,56 +0,0 @@
-"""
-GitLab API:
-https://docs.gitlab.com/ce/api/merge_requests.html
-https://docs.gitlab.com/ee/api/deployments.html#list-of-merge-requests-associated-with-a-deployment
-"""
-import re
-
-import pytest
-import responses
-
-from gitlab.v4.objects import ProjectDeploymentMergeRequest, ProjectMergeRequest
-
-mr_content = {
- "id": 1,
- "iid": 1,
- "project_id": 3,
- "title": "test1",
- "description": "fixed login page css paddings",
- "state": "merged",
- "merged_by": {
- "id": 87854,
- "name": "Douwe Maan",
- "username": "DouweM",
- "state": "active",
- "avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png",
- "web_url": "https://gitlab.com/DouweM",
- },
-}
-
-
-@pytest.fixture
-def resp_list_merge_requests():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url=re.compile(
- r"http://localhost/api/v4/projects/1/(deployments/1/)?merge_requests"
- ),
- json=[mr_content],
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-def test_list_project_merge_requests(project, resp_list_merge_requests):
- mrs = project.mergerequests.list()
- assert isinstance(mrs[0], ProjectMergeRequest)
- assert mrs[0].iid == mr_content["iid"]
-
-
-def test_list_deployment_merge_requests(project, resp_list_merge_requests):
- deployment = project.deployments.get(1, lazy=True)
- mrs = deployment.mergerequests.list()
- assert isinstance(mrs[0], ProjectDeploymentMergeRequest)
- assert mrs[0].iid == mr_content["iid"]
diff --git a/tests/unit/objects/test_mro.py b/tests/unit/objects/test_mro.py
deleted file mode 100644
index 8f67b77..0000000
--- a/tests/unit/objects/test_mro.py
+++ /dev/null
@@ -1,122 +0,0 @@
-"""
-Ensure objects defined in gitlab.v4.objects have REST* as last item in class
-definition
-
-Original notes by John L. Villalovos
-
-An example of an incorrect definition:
- class ProjectPipeline(RESTObject, RefreshMixin, ObjectDeleteMixin):
- ^^^^^^^^^^ This should be at the end.
-
-Correct way would be:
- class ProjectPipeline(RefreshMixin, ObjectDeleteMixin, RESTObject):
- Correctly at the end ^^^^^^^^^^
-
-
-Why this is an issue:
-
- When we do type-checking for gitlab/mixins.py we make RESTObject or
- RESTManager the base class for the mixins
-
- Here is how our classes look when type-checking:
-
- class RESTObject(object):
- def __init__(self, manager: "RESTManager", attrs: Dict[str, Any]) -> None:
- ...
-
- class Mixin(RESTObject):
- ...
-
- # Wrong ordering here
- class Wrongv4Object(RESTObject, RefreshMixin):
- ...
-
- If we actually ran this in Python we would get the following error:
- class Wrongv4Object(RESTObject, Mixin):
- TypeError: Cannot create a consistent method resolution
- order (MRO) for bases RESTObject, Mixin
-
- When we are type-checking it fails to understand the class Wrongv4Object
- and thus we can't type check it correctly.
-
-Almost all classes in gitlab/v4/objects/*py were already correct before this
-check was added.
-"""
-import inspect
-
-import pytest
-
-import gitlab.v4.objects
-
-
-def test_show_issue():
- """Test case to demonstrate the TypeError that occurs"""
-
- class RESTObject(object):
- def __init__(self, manager: str, attrs: int) -> None:
- ...
-
- class Mixin(RESTObject):
- ...
-
- with pytest.raises(TypeError) as exc_info:
- # Wrong ordering here
- class Wrongv4Object(RESTObject, Mixin):
- ...
-
- # The error message in the exception should be:
- # TypeError: Cannot create a consistent method resolution
- # order (MRO) for bases RESTObject, Mixin
-
- # Make sure the exception string contains "MRO"
- assert "MRO" in exc_info.exconly()
-
- # Correctly ordered class, no exception
- class Correctv4Object(Mixin, RESTObject):
- ...
-
-
-def test_mros():
- """Ensure objects defined in gitlab.v4.objects have REST* as last item in
- class definition.
-
- We do this as we need to ensure the MRO (Method Resolution Order) is
- correct.
- """
-
- failed_messages = []
- for module_name, module_value in inspect.getmembers(gitlab.v4.objects):
- if not inspect.ismodule(module_value):
- # We only care about the modules
- continue
- # Iterate through all the classes in our module
- for class_name, class_value in inspect.getmembers(module_value):
- if not inspect.isclass(class_value):
- continue
-
- # Ignore imported classes from gitlab.base
- if class_value.__module__ == "gitlab.base":
- continue
-
- mro = class_value.mro()
-
- # We only check classes which have a 'gitlab.base' class in their MRO
- has_base = False
- for count, obj in enumerate(mro, start=1):
- if obj.__module__ == "gitlab.base":
- has_base = True
- base_classname = obj.__name__
- if has_base:
- filename = inspect.getfile(class_value)
- # NOTE(jlvillal): The very last item 'mro[-1]' is always going
- # to be 'object'. That is why we are checking 'mro[-2]'.
- if mro[-2].__module__ != "gitlab.base":
- failed_messages.append(
- (
- f"class definition for {class_name!r} in file {filename!r} "
- f"must have {base_classname!r} as the last class in the "
- f"class definition"
- )
- )
- failed_msg = "\n".join(failed_messages)
- assert not failed_messages, failed_msg
diff --git a/tests/unit/objects/test_packages.py b/tests/unit/objects/test_packages.py
deleted file mode 100644
index 687054f..0000000
--- a/tests/unit/objects/test_packages.py
+++ /dev/null
@@ -1,252 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ce/api/packages.html
-"""
-import re
-from urllib.parse import quote_plus
-
-import pytest
-import responses
-
-from gitlab.v4.objects import (
- GenericPackage,
- GroupPackage,
- ProjectPackage,
- ProjectPackageFile,
-)
-
-package_content = {
- "id": 1,
- "name": "com/mycompany/my-app",
- "version": "1.0-SNAPSHOT",
- "package_type": "maven",
- "_links": {
- "web_path": "/namespace1/project1/-/packages/1",
- "delete_api_path": "/namespace1/project1/-/packages/1",
- },
- "created_at": "2019-11-27T03:37:38.711Z",
- "pipeline": {
- "id": 123,
- "status": "pending",
- "ref": "new-pipeline",
- "sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a",
- "web_url": "https://example.com/foo/bar/pipelines/47",
- "created_at": "2016-08-11T11:28:34.085Z",
- "updated_at": "2016-08-11T11:32:35.169Z",
- "user": {
- "name": "Administrator",
- "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
- },
- },
- "versions": [
- {
- "id": 2,
- "version": "2.0-SNAPSHOT",
- "created_at": "2020-04-28T04:42:11.573Z",
- "pipeline": {
- "id": 234,
- "status": "pending",
- "ref": "new-pipeline",
- "sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a",
- "web_url": "https://example.com/foo/bar/pipelines/58",
- "created_at": "2016-08-11T11:28:34.085Z",
- "updated_at": "2016-08-11T11:32:35.169Z",
- "user": {
- "name": "Administrator",
- "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
- },
- },
- }
- ],
-}
-
-package_file_content = [
- {
- "id": 25,
- "package_id": 1,
- "created_at": "2018-11-07T15:25:52.199Z",
- "file_name": "my-app-1.5-20181107.152550-1.jar",
- "size": 2421,
- "file_md5": "58e6a45a629910c6ff99145a688971ac",
- "file_sha1": "ebd193463d3915d7e22219f52740056dfd26cbfe",
- "pipelines": [
- {
- "id": 123,
- "status": "pending",
- "ref": "new-pipeline",
- "sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a",
- "web_url": "https://example.com/foo/bar/pipelines/47",
- "created_at": "2016-08-11T11:28:34.085Z",
- "updated_at": "2016-08-11T11:32:35.169Z",
- "user": {
- "name": "Administrator",
- "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
- },
- }
- ],
- },
- {
- "id": 26,
- "package_id": 1,
- "created_at": "2018-11-07T15:25:56.776Z",
- "file_name": "my-app-1.5-20181107.152550-1.pom",
- "size": 1122,
- "file_md5": "d90f11d851e17c5513586b4a7e98f1b2",
- "file_sha1": "9608d068fe88aff85781811a42f32d97feb440b5",
- },
- {
- "id": 27,
- "package_id": 1,
- "created_at": "2018-11-07T15:26:00.556Z",
- "file_name": "maven-metadata.xml",
- "size": 767,
- "file_md5": "6dfd0cce1203145a927fef5e3a1c650c",
- "file_sha1": "d25932de56052d320a8ac156f745ece73f6a8cd2",
- },
-]
-
-package_name = "hello-world"
-package_version = "v1.0.0"
-file_name = "hello.tar.gz"
-file_content = "package content"
-package_url = "http://localhost/api/v4/projects/1/packages/generic/{}/{}/{}".format(
- # https://datatracker.ietf.org/doc/html/rfc3986.html#section-2.3 :(
- quote_plus(package_name).replace(".", "%2E"),
- quote_plus(package_version).replace(".", "%2E"),
- quote_plus(file_name).replace(".", "%2E"),
-)
-
-
-@pytest.fixture
-def resp_list_packages():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url=re.compile(r"http://localhost/api/v4/(groups|projects)/1/packages"),
- json=[package_content],
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_get_package():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/packages/1",
- json=package_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_delete_package(no_content):
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.DELETE,
- url="http://localhost/api/v4/projects/1/packages/1",
- json=no_content,
- content_type="application/json",
- status=204,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_list_package_files():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url=re.compile(
- r"http://localhost/api/v4/projects/1/packages/1/package_files"
- ),
- json=package_file_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_upload_generic_package(created_content):
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.PUT,
- url=package_url,
- json=created_content,
- content_type="application/json",
- status=201,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_download_generic_package(created_content):
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url=package_url,
- body=file_content,
- content_type="application/octet-stream",
- status=200,
- )
- yield rsps
-
-
-def test_list_project_packages(project, resp_list_packages):
- packages = project.packages.list()
- assert isinstance(packages, list)
- assert isinstance(packages[0], ProjectPackage)
- assert packages[0].version == "1.0-SNAPSHOT"
-
-
-def test_list_group_packages(group, resp_list_packages):
- packages = group.packages.list()
- assert isinstance(packages, list)
- assert isinstance(packages[0], GroupPackage)
- assert packages[0].version == "1.0-SNAPSHOT"
-
-
-def test_get_project_package(project, resp_get_package):
- package = project.packages.get(1)
- assert isinstance(package, ProjectPackage)
- assert package.version == "1.0-SNAPSHOT"
-
-
-def test_delete_project_package(project, resp_delete_package):
- package = project.packages.get(1, lazy=True)
- package.delete()
-
-
-def test_list_project_package_files(project, resp_list_package_files):
- package = project.packages.get(1, lazy=True)
- package_files = package.package_files.list()
- assert isinstance(package_files, list)
- assert isinstance(package_files[0], ProjectPackageFile)
- assert package_files[0].id == 25
-
-
-def test_upload_generic_package(tmp_path, project, resp_upload_generic_package):
- path = tmp_path / file_name
- path.write_text(file_content)
- package = project.generic_packages.upload(
- package_name=package_name,
- package_version=package_version,
- file_name=file_name,
- path=path,
- )
-
- assert isinstance(package, GenericPackage)
-
-
-def test_download_generic_package(project, resp_download_generic_package):
- package = project.generic_packages.download(
- package_name=package_name,
- package_version=package_version,
- file_name=file_name,
- )
-
- assert isinstance(package, bytes)
diff --git a/tests/unit/objects/test_personal_access_tokens.py b/tests/unit/objects/test_personal_access_tokens.py
deleted file mode 100644
index 065b5c8..0000000
--- a/tests/unit/objects/test_personal_access_tokens.py
+++ /dev/null
@@ -1,94 +0,0 @@
-"""
-GitLab API:
-https://docs.gitlab.com/ee/api/personal_access_tokens.html
-https://docs.gitlab.com/ee/api/users.html#create-a-personal-access-token
-"""
-
-import pytest
-import responses
-
-user_id = 1
-token_id = 1
-token_name = "Test Token"
-
-token_url = "http://localhost/api/v4/personal_access_tokens"
-single_token_url = f"{token_url}/{token_id}"
-user_token_url = f"http://localhost/api/v4/users/{user_id}/personal_access_tokens"
-
-content = {
- "id": token_id,
- "name": token_name,
- "revoked": False,
- "created_at": "2020-07-23T14:31:47.729Z",
- "scopes": ["api"],
- "active": True,
- "user_id": user_id,
- "expires_at": None,
-}
-
-
-@pytest.fixture
-def resp_create_user_personal_access_token():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.POST,
- url=user_token_url,
- json=content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_personal_access_token(no_content):
- with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
- rsps.add(
- method=responses.GET,
- url=token_url,
- json=[content],
- content_type="application/json",
- status=200,
- )
- rsps.add(
- method=responses.DELETE,
- url=single_token_url,
- json=no_content,
- content_type="application/json",
- status=204,
- )
- yield rsps
-
-
-def test_create_personal_access_token(gl, resp_create_user_personal_access_token):
- user = gl.users.get(1, lazy=True)
- access_token = user.personal_access_tokens.create(
- {"name": token_name, "scopes": "api"}
- )
- assert access_token.revoked is False
- assert access_token.name == token_name
-
-
-def test_list_personal_access_tokens(gl, resp_personal_access_token):
- access_tokens = gl.personal_access_tokens.list()
- assert len(access_tokens) == 1
- assert access_tokens[0].revoked is False
- assert access_tokens[0].name == token_name
-
-
-def test_list_personal_access_tokens_filter(gl, resp_personal_access_token):
- access_tokens = gl.personal_access_tokens.list(user_id=user_id)
- assert len(access_tokens) == 1
- assert access_tokens[0].revoked is False
- assert access_tokens[0].user_id == user_id
-
-
-def test_revoke_personal_access_token(gl, resp_personal_access_token):
- access_token = gl.personal_access_tokens.list(user_id=user_id)[0]
- access_token.delete()
- assert resp_personal_access_token.assert_call_count(single_token_url, 1)
-
-
-def test_revoke_personal_access_token_by_id(gl, resp_personal_access_token):
- gl.personal_access_tokens.delete(token_id)
- assert resp_personal_access_token.assert_call_count(single_token_url, 1)
diff --git a/tests/unit/objects/test_pipeline_schedules.py b/tests/unit/objects/test_pipeline_schedules.py
deleted file mode 100644
index c5dcc76..0000000
--- a/tests/unit/objects/test_pipeline_schedules.py
+++ /dev/null
@@ -1,62 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ce/api/pipeline_schedules.html
-"""
-import pytest
-import responses
-
-
-@pytest.fixture
-def resp_project_pipeline_schedule(created_content):
- content = {
- "id": 14,
- "description": "Build packages",
- "ref": "master",
- "cron": "0 1 * * 5",
- "cron_timezone": "UTC",
- "next_run_at": "2017-05-26T01:00:00.000Z",
- "active": True,
- "created_at": "2017-05-19T13:43:08.169Z",
- "updated_at": "2017-05-19T13:43:08.169Z",
- "last_pipeline": None,
- "owner": {
- "name": "Administrator",
- "username": "root",
- "id": 1,
- "state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
- "web_url": "https://gitlab.example.com/root",
- },
- }
-
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.POST,
- url="http://localhost/api/v4/projects/1/pipeline_schedules",
- json=content,
- content_type="application/json",
- status=200,
- )
- rsps.add(
- method=responses.POST,
- url="http://localhost/api/v4/projects/1/pipeline_schedules/14/play",
- json=created_content,
- content_type="application/json",
- status=201,
- )
- yield rsps
-
-
-def test_project_pipeline_schedule_play(project, resp_project_pipeline_schedule):
- description = "Build packages"
- cronline = "0 1 * * 5"
- sched = project.pipelineschedules.create(
- {"ref": "master", "description": description, "cron": cronline}
- )
- assert sched is not None
- assert description == sched.description
- assert cronline == sched.cron
-
- play_result = sched.play()
- assert play_result is not None
- assert "message" in play_result
- assert play_result["message"] == "201 Created"
diff --git a/tests/unit/objects/test_pipelines.py b/tests/unit/objects/test_pipelines.py
deleted file mode 100644
index c0b87f2..0000000
--- a/tests/unit/objects/test_pipelines.py
+++ /dev/null
@@ -1,146 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ee/api/pipelines.html
-"""
-import pytest
-import responses
-
-from gitlab.v4.objects import ProjectPipeline, ProjectPipelineTestReport
-
-pipeline_content = {
- "id": 46,
- "project_id": 1,
- "status": "pending",
- "ref": "master",
- "sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a",
- "before_sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a",
- "tag": False,
- "yaml_errors": None,
- "user": {
- "name": "Administrator",
- "username": "root",
- "id": 1,
- "state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
- "web_url": "http://localhost:3000/root",
- },
- "created_at": "2016-08-11T11:28:34.085Z",
- "updated_at": "2016-08-11T11:32:35.169Z",
- "started_at": None,
- "finished_at": "2016-08-11T11:32:35.145Z",
- "committed_at": None,
- "duration": None,
- "queued_duration": 0.010,
- "coverage": None,
- "web_url": "https://example.com/foo/bar/pipelines/46",
-}
-
-
-test_report_content = {
- "total_time": 5,
- "total_count": 1,
- "success_count": 1,
- "failed_count": 0,
- "skipped_count": 0,
- "error_count": 0,
- "test_suites": [
- {
- "name": "Secure",
- "total_time": 5,
- "total_count": 1,
- "success_count": 1,
- "failed_count": 0,
- "skipped_count": 0,
- "error_count": 0,
- "test_cases": [
- {
- "status": "success",
- "name": "Security Reports can create an auto-remediation MR",
- "classname": "vulnerability_management_spec",
- "execution_time": 5,
- "system_output": None,
- "stack_trace": None,
- }
- ],
- }
- ],
-}
-
-
-@pytest.fixture
-def resp_get_pipeline():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/pipelines/1",
- json=pipeline_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_cancel_pipeline():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.POST,
- url="http://localhost/api/v4/projects/1/pipelines/1/cancel",
- json=pipeline_content,
- content_type="application/json",
- status=201,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_retry_pipeline():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.POST,
- url="http://localhost/api/v4/projects/1/pipelines/1/retry",
- json=pipeline_content,
- content_type="application/json",
- status=201,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_get_pipeline_test_report():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/pipelines/1/test_report",
- json=test_report_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-def test_get_project_pipeline(project, resp_get_pipeline):
- pipeline = project.pipelines.get(1)
- assert isinstance(pipeline, ProjectPipeline)
- assert pipeline.ref == "master"
-
-
-def test_cancel_project_pipeline(project, resp_cancel_pipeline):
- pipeline = project.pipelines.get(1, lazy=True)
-
- output = pipeline.cancel()
- assert output["ref"] == "master"
-
-
-def test_retry_project_pipeline(project, resp_retry_pipeline):
- pipeline = project.pipelines.get(1, lazy=True)
-
- output = pipeline.retry()
- assert output["ref"] == "master"
-
-
-def test_get_project_pipeline_test_report(project, resp_get_pipeline_test_report):
- pipeline = project.pipelines.get(1, lazy=True)
- test_report = pipeline.test_report.get()
- assert isinstance(test_report, ProjectPipelineTestReport)
- assert test_report.total_time == 5
- assert test_report.test_suites[0]["name"] == "Secure"
diff --git a/tests/unit/objects/test_project_access_tokens.py b/tests/unit/objects/test_project_access_tokens.py
deleted file mode 100644
index 4d4788d..0000000
--- a/tests/unit/objects/test_project_access_tokens.py
+++ /dev/null
@@ -1,113 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ee/api/resource_access_tokens.html
-"""
-
-import pytest
-import responses
-
-
-@pytest.fixture
-def resp_list_project_access_token():
- content = [
- {
- "user_id": 141,
- "scopes": ["api"],
- "name": "token",
- "expires_at": "2021-01-31",
- "id": 42,
- "active": True,
- "created_at": "2021-01-20T22:11:48.151Z",
- "revoked": False,
- }
- ]
-
- with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/access_tokens",
- json=content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_create_project_access_token():
- content = {
- "user_id": 141,
- "scopes": ["api"],
- "name": "token",
- "expires_at": "2021-01-31",
- "id": 42,
- "active": True,
- "created_at": "2021-01-20T22:11:48.151Z",
- "revoked": False,
- }
-
- with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
- rsps.add(
- method=responses.POST,
- url="http://localhost/api/v4/projects/1/access_tokens",
- json=content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_revoke_project_access_token():
- content = [
- {
- "user_id": 141,
- "scopes": ["api"],
- "name": "token",
- "expires_at": "2021-01-31",
- "id": 42,
- "active": True,
- "created_at": "2021-01-20T22:11:48.151Z",
- "revoked": False,
- }
- ]
-
- with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
- rsps.add(
- method=responses.DELETE,
- url="http://localhost/api/v4/projects/1/access_tokens/42",
- json=content,
- content_type="application/json",
- status=204,
- )
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/access_tokens",
- json=content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-def test_list_project_access_tokens(gl, resp_list_project_access_token):
- access_tokens = gl.projects.get(1, lazy=True).access_tokens.list()
- assert len(access_tokens) == 1
- assert access_tokens[0].revoked is False
- assert access_tokens[0].name == "token"
-
-
-def test_create_project_access_token(gl, resp_create_project_access_token):
- access_tokens = gl.projects.get(1, lazy=True).access_tokens.create(
- {"name": "test", "scopes": ["api"]}
- )
- assert access_tokens.revoked is False
- assert access_tokens.user_id == 141
- assert access_tokens.expires_at == "2021-01-31"
-
-
-def test_revoke_project_access_token(
- gl, resp_list_project_access_token, resp_revoke_project_access_token
-):
- gl.projects.get(1, lazy=True).access_tokens.delete(42)
- access_token = gl.projects.get(1, lazy=True).access_tokens.list()[0]
- access_token.delete()
diff --git a/tests/unit/objects/test_project_import_export.py b/tests/unit/objects/test_project_import_export.py
deleted file mode 100644
index 78e51b1..0000000
--- a/tests/unit/objects/test_project_import_export.py
+++ /dev/null
@@ -1,112 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ce/api/project_import_export.html
-"""
-import pytest
-import responses
-
-
-@pytest.fixture
-def resp_import_project():
- content = {
- "id": 1,
- "description": None,
- "name": "api-project",
- "name_with_namespace": "Administrator / api-project",
- "path": "api-project",
- "path_with_namespace": "root/api-project",
- "created_at": "2018-02-13T09:05:58.023Z",
- "import_status": "scheduled",
- }
-
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.POST,
- url="http://localhost/api/v4/projects/import",
- json=content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_import_status():
- content = {
- "id": 1,
- "description": "Itaque perspiciatis minima aspernatur corporis consequatur.",
- "name": "Gitlab Test",
- "name_with_namespace": "Gitlab Org / Gitlab Test",
- "path": "gitlab-test",
- "path_with_namespace": "gitlab-org/gitlab-test",
- "created_at": "2017-08-29T04:36:44.383Z",
- "import_status": "finished",
- }
-
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/import",
- json=content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_import_github():
- content = {
- "id": 27,
- "name": "my-repo",
- "full_path": "/root/my-repo",
- "full_name": "Administrator / my-repo",
- }
-
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.POST,
- url="http://localhost/api/v4/import/github",
- json=content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-def test_import_project(gl, resp_import_project):
- project_import = gl.projects.import_project("file", "api-project")
- assert project_import["import_status"] == "scheduled"
-
-
-def test_refresh_project_import_status(project, resp_import_status):
- project_import = project.imports.get()
- project_import.refresh()
- assert project_import.import_status == "finished"
-
-
-def test_import_github(gl, resp_import_github):
- base_path = "/root"
- name = "my-repo"
- ret = gl.projects.import_github("githubkey", 1234, base_path, name)
- assert isinstance(ret, dict)
- assert ret["name"] == name
- assert ret["full_path"] == "/".join((base_path, name))
- assert ret["full_name"].endswith(name)
-
-
-def test_create_project_export(project, resp_export):
- export = project.exports.create()
- assert export.message == "202 Accepted"
-
-
-def test_refresh_project_export_status(project, resp_export):
- export = project.exports.create()
- export.refresh()
- assert export.export_status == "finished"
-
-
-def test_download_project_export(project, resp_export, binary_content):
- export = project.exports.create()
- download = export.download()
- assert isinstance(download, bytes)
- assert download == binary_content
diff --git a/tests/unit/objects/test_project_merge_request_approvals.py b/tests/unit/objects/test_project_merge_request_approvals.py
deleted file mode 100644
index 16d58bd..0000000
--- a/tests/unit/objects/test_project_merge_request_approvals.py
+++ /dev/null
@@ -1,317 +0,0 @@
-"""
-Gitlab API: https://docs.gitlab.com/ee/api/merge_request_approvals.html
-"""
-
-import copy
-
-import pytest
-import responses
-
-import gitlab
-
-approval_rule_id = 1
-approval_rule_name = "security"
-approvals_required = 3
-user_ids = [5, 50]
-group_ids = [5]
-
-new_approval_rule_name = "new approval rule"
-new_approval_rule_user_ids = user_ids
-new_approval_rule_approvals_required = 2
-
-updated_approval_rule_user_ids = [5]
-updated_approval_rule_approvals_required = 1
-
-
-@pytest.fixture
-def resp_snippet():
- merge_request_content = [
- {
- "id": 1,
- "iid": 1,
- "project_id": 1,
- "title": "test1",
- "description": "fixed login page css paddings",
- "state": "merged",
- "merged_by": {
- "id": 87854,
- "name": "Douwe Maan",
- "username": "DouweM",
- "state": "active",
- "avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png",
- "web_url": "https://gitlab.com/DouweM",
- },
- "merged_at": "2018-09-07T11:16:17.520Z",
- "closed_by": None,
- "closed_at": None,
- "created_at": "2017-04-29T08:46:00Z",
- "updated_at": "2017-04-29T08:46:00Z",
- "target_branch": "master",
- "source_branch": "test1",
- "upvotes": 0,
- "downvotes": 0,
- "author": {
- "id": 1,
- "name": "Administrator",
- "username": "admin",
- "state": "active",
- "avatar_url": None,
- "web_url": "https://gitlab.example.com/admin",
- },
- "assignee": {
- "id": 1,
- "name": "Administrator",
- "username": "admin",
- "state": "active",
- "avatar_url": None,
- "web_url": "https://gitlab.example.com/admin",
- },
- "assignees": [
- {
- "name": "Miss Monserrate Beier",
- "username": "axel.block",
- "id": 12,
- "state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/46f6f7dc858ada7be1853f7fb96e81da?s=80&d=identicon",
- "web_url": "https://gitlab.example.com/axel.block",
- }
- ],
- "source_project_id": 2,
- "target_project_id": 3,
- "labels": ["Community contribution", "Manage"],
- "work_in_progress": None,
- "milestone": {
- "id": 5,
- "iid": 1,
- "project_id": 3,
- "title": "v2.0",
- "description": "Assumenda aut placeat expedita exercitationem labore sunt enim earum.",
- "state": "closed",
- "created_at": "2015-02-02T19:49:26.013Z",
- "updated_at": "2015-02-02T19:49:26.013Z",
- "due_date": "2018-09-22",
- "start_date": "2018-08-08",
- "web_url": "https://gitlab.example.com/my-group/my-project/milestones/1",
- },
- "merge_when_pipeline_succeeds": None,
- "merge_status": "can_be_merged",
- "sha": "8888888888888888888888888888888888888888",
- "merge_commit_sha": None,
- "squash_commit_sha": None,
- "user_notes_count": 1,
- "discussion_locked": None,
- "should_remove_source_branch": True,
- "force_remove_source_branch": False,
- "allow_collaboration": False,
- "allow_maintainer_to_push": False,
- "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
- "references": {
- "short": "!1",
- "relative": "my-group/my-project!1",
- "full": "my-group/my-project!1",
- },
- "time_stats": {
- "time_estimate": 0,
- "total_time_spent": 0,
- "human_time_estimate": None,
- "human_total_time_spent": None,
- },
- "squash": False,
- "task_completion_status": {"count": 0, "completed_count": 0},
- }
- ]
- mr_ars_content = [
- {
- "id": approval_rule_id,
- "name": approval_rule_name,
- "rule_type": "regular",
- "eligible_approvers": [
- {
- "id": user_ids[0],
- "name": "John Doe",
- "username": "jdoe",
- "state": "active",
- "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon",
- "web_url": "http://localhost/jdoe",
- },
- {
- "id": user_ids[1],
- "name": "Group Member 1",
- "username": "group_member_1",
- "state": "active",
- "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon",
- "web_url": "http://localhost/group_member_1",
- },
- ],
- "approvals_required": approvals_required,
- "source_rule": None,
- "users": [
- {
- "id": 5,
- "name": "John Doe",
- "username": "jdoe",
- "state": "active",
- "avatar_url": "https://www.gravatar.com/avatar/0?s=80&d=identicon",
- "web_url": "http://localhost/jdoe",
- }
- ],
- "groups": [
- {
- "id": 5,
- "name": "group1",
- "path": "group1",
- "description": "",
- "visibility": "public",
- "lfs_enabled": False,
- "avatar_url": None,
- "web_url": "http://localhost/groups/group1",
- "request_access_enabled": False,
- "full_name": "group1",
- "full_path": "group1",
- "parent_id": None,
- "ldap_cn": None,
- "ldap_access": None,
- }
- ],
- "contains_hidden_groups": False,
- "overridden": False,
- }
- ]
-
- with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/merge_requests",
- json=merge_request_content,
- content_type="application/json",
- status=200,
- )
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/merge_requests/1",
- json=merge_request_content[0],
- content_type="application/json",
- status=200,
- )
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/merge_requests/1/approval_rules",
- json=mr_ars_content,
- content_type="application/json",
- status=200,
- )
-
- new_mr_ars_content = dict(mr_ars_content[0])
- new_mr_ars_content["name"] = new_approval_rule_name
- new_mr_ars_content["approvals_required"] = new_approval_rule_approvals_required
-
- rsps.add(
- method=responses.POST,
- url="http://localhost/api/v4/projects/1/merge_requests/1/approval_rules",
- json=new_mr_ars_content,
- content_type="application/json",
- status=200,
- )
-
- updated_mr_ars_content = copy.deepcopy(mr_ars_content[0])
- updated_mr_ars_content["eligible_approvers"] = [
- mr_ars_content[0]["eligible_approvers"][0]
- ]
-
- updated_mr_ars_content[
- "approvals_required"
- ] = updated_approval_rule_approvals_required
-
- rsps.add(
- method=responses.PUT,
- url="http://localhost/api/v4/projects/1/merge_requests/1/approval_rules/1",
- json=updated_mr_ars_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-def test_project_approval_manager_update_uses_post(project, resp_snippet):
- """Ensure the
- gitlab.v4.objects.merge_request_approvals.ProjectApprovalManager object has
- _update_uses_post set to True"""
- approvals = project.approvals
- assert isinstance(
- approvals, gitlab.v4.objects.merge_request_approvals.ProjectApprovalManager
- )
- assert approvals._update_uses_post is True
-
-
-def test_list_merge_request_approval_rules(project, resp_snippet):
- approval_rules = project.mergerequests.get(1).approval_rules.list()
- assert len(approval_rules) == 1
- assert approval_rules[0].name == approval_rule_name
- assert approval_rules[0].id == approval_rule_id
-
-
-def test_update_merge_request_approvals_set_approvers(project, resp_snippet):
- approvals = project.mergerequests.get(1).approvals
- assert isinstance(
- approvals,
- gitlab.v4.objects.merge_request_approvals.ProjectMergeRequestApprovalManager,
- )
- assert approvals._update_uses_post is True
- response = approvals.set_approvers(
- updated_approval_rule_approvals_required,
- approver_ids=updated_approval_rule_user_ids,
- approver_group_ids=group_ids,
- approval_rule_name=approval_rule_name,
- )
-
- assert response.approvals_required == updated_approval_rule_approvals_required
- assert len(response.eligible_approvers) == len(updated_approval_rule_user_ids)
- assert response.eligible_approvers[0]["id"] == updated_approval_rule_user_ids[0]
- assert response.name == approval_rule_name
-
-
-def test_create_merge_request_approvals_set_approvers(project, resp_snippet):
- approvals = project.mergerequests.get(1).approvals
- assert isinstance(
- approvals,
- gitlab.v4.objects.merge_request_approvals.ProjectMergeRequestApprovalManager,
- )
- assert approvals._update_uses_post is True
- response = approvals.set_approvers(
- new_approval_rule_approvals_required,
- approver_ids=new_approval_rule_user_ids,
- approver_group_ids=group_ids,
- approval_rule_name=new_approval_rule_name,
- )
- assert response.approvals_required == new_approval_rule_approvals_required
- assert len(response.eligible_approvers) == len(new_approval_rule_user_ids)
- assert response.eligible_approvers[0]["id"] == new_approval_rule_user_ids[0]
- assert response.name == new_approval_rule_name
-
-
-def test_create_merge_request_approval_rule(project, resp_snippet):
- approval_rules = project.mergerequests.get(1).approval_rules
- data = {
- "name": new_approval_rule_name,
- "approvals_required": new_approval_rule_approvals_required,
- "rule_type": "regular",
- "user_ids": new_approval_rule_user_ids,
- "group_ids": group_ids,
- }
- response = approval_rules.create(data)
- assert response.approvals_required == new_approval_rule_approvals_required
- assert len(response.eligible_approvers) == len(new_approval_rule_user_ids)
- assert response.eligible_approvers[0]["id"] == new_approval_rule_user_ids[0]
- assert response.name == new_approval_rule_name
-
-
-def test_update_merge_request_approval_rule(project, resp_snippet):
- approval_rules = project.mergerequests.get(1).approval_rules
- ar_1 = approval_rules.list()[0]
- ar_1.user_ids = updated_approval_rule_user_ids
- ar_1.approvals_required = updated_approval_rule_approvals_required
- ar_1.save()
-
- assert ar_1.approvals_required == updated_approval_rule_approvals_required
- assert len(ar_1.eligible_approvers) == len(updated_approval_rule_user_ids)
- assert ar_1.eligible_approvers[0]["id"] == updated_approval_rule_user_ids[0]
diff --git a/tests/unit/objects/test_project_statistics.py b/tests/unit/objects/test_project_statistics.py
deleted file mode 100644
index 50d9a6d..0000000
--- a/tests/unit/objects/test_project_statistics.py
+++ /dev/null
@@ -1,28 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ce/api/project_statistics.html
-"""
-import pytest
-import responses
-
-from gitlab.v4.objects import ProjectAdditionalStatistics
-
-
-@pytest.fixture
-def resp_project_statistics():
- content = {"fetches": {"total": 50, "days": [{"count": 10, "date": "2018-01-10"}]}}
-
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/statistics",
- json=content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-def test_project_additional_statistics(project, resp_project_statistics):
- statistics = project.additionalstatistics.get()
- assert isinstance(statistics, ProjectAdditionalStatistics)
- assert statistics.fetches["total"] == 50
diff --git a/tests/unit/objects/test_projects.py b/tests/unit/objects/test_projects.py
deleted file mode 100644
index 039d5ec..0000000
--- a/tests/unit/objects/test_projects.py
+++ /dev/null
@@ -1,237 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ce/api/projects.html
-"""
-
-import pytest
-import responses
-
-from gitlab.v4.objects import Project
-
-project_content = {"name": "name", "id": 1}
-import_content = {
- "id": 1,
- "name": "project",
- "import_status": "scheduled",
-}
-
-
-@pytest.fixture
-def resp_get_project():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1",
- json=project_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_list_projects():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects",
- json=[project_content],
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_import_bitbucket_server():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.POST,
- url="http://localhost/api/v4/import/bitbucket_server",
- json=import_content,
- content_type="application/json",
- status=201,
- )
- yield rsps
-
-
-def test_get_project(gl, resp_get_project):
- data = gl.projects.get(1)
- assert isinstance(data, Project)
- assert data.name == "name"
- assert data.id == 1
-
-
-def test_list_projects(gl, resp_list_projects):
- projects = gl.projects.list()
- assert isinstance(projects[0], Project)
- assert projects[0].name == "name"
-
-
-def test_import_bitbucket_server(gl, resp_import_bitbucket_server):
- res = gl.projects.import_bitbucket_server(
- bitbucket_server_project="project",
- bitbucket_server_repo="repo",
- bitbucket_server_url="url",
- bitbucket_server_username="username",
- personal_access_token="token",
- new_name="new_name",
- target_namespace="namespace",
- )
- assert res["id"] == 1
- assert res["name"] == "project"
- assert res["import_status"] == "scheduled"
-
-
-@pytest.mark.skip(reason="missing test")
-def test_list_user_projects(gl):
- pass
-
-
-@pytest.mark.skip(reason="missing test")
-def test_list_user_starred_projects(gl):
- pass
-
-
-@pytest.mark.skip(reason="missing test")
-def test_list_project_users(gl):
- pass
-
-
-@pytest.mark.skip(reason="missing test")
-def test_create_project(gl):
- pass
-
-
-@pytest.mark.skip(reason="missing test")
-def test_create_user_project(gl):
- pass
-
-
-@pytest.mark.skip(reason="missing test")
-def test_update_project(gl):
- pass
-
-
-@pytest.mark.skip(reason="missing test")
-def test_fork_project(gl):
- pass
-
-
-@pytest.mark.skip(reason="missing test")
-def test_list_project_forks(gl):
- pass
-
-
-@pytest.mark.skip(reason="missing test")
-def test_star_project(gl):
- pass
-
-
-@pytest.mark.skip(reason="missing test")
-def test_unstar_project(gl):
- pass
-
-
-@pytest.mark.skip(reason="missing test")
-def test_list_project_starrers(gl):
- pass
-
-
-@pytest.mark.skip(reason="missing test")
-def test_get_project_languages(gl):
- pass
-
-
-@pytest.mark.skip(reason="missing test")
-def test_archive_project(gl):
- pass
-
-
-@pytest.mark.skip(reason="missing test")
-def test_unarchive_project(gl):
- pass
-
-
-@pytest.mark.skip(reason="missing test")
-def test_remove_project(gl):
- pass
-
-
-@pytest.mark.skip(reason="missing test")
-def test_restore_project(gl):
- pass
-
-
-@pytest.mark.skip(reason="missing test")
-def test_upload_file(gl):
- pass
-
-
-@pytest.mark.skip(reason="missing test")
-def test_share_project(gl):
- pass
-
-
-@pytest.mark.skip(reason="missing test")
-def test_delete_shared_project_link(gl):
- pass
-
-
-@pytest.mark.skip(reason="missing test")
-def test_create_forked_from_relationship(gl):
- pass
-
-
-@pytest.mark.skip(reason="missing test")
-def test_delete_forked_from_relationship(gl):
- pass
-
-
-@pytest.mark.skip(reason="missing test")
-def test_search_projects_by_name(gl):
- pass
-
-
-@pytest.mark.skip(reason="missing test")
-def test_project_housekeeping(gl):
- pass
-
-
-@pytest.mark.skip(reason="missing test")
-def test_get_project_push_rules(gl):
- pass
-
-
-@pytest.mark.skip(reason="missing test")
-def test_create_project_push_rule(gl):
- pass
-
-
-@pytest.mark.skip(reason="missing test")
-def test_update_project_push_rule(gl):
- pass
-
-
-@pytest.mark.skip(reason="missing test")
-def test_delete_project_push_rule(gl):
- pass
-
-
-@pytest.mark.skip(reason="missing test")
-def test_transfer_project(gl):
- pass
-
-
-@pytest.mark.skip(reason="missing test")
-def test_project_pull_mirror(gl):
- pass
-
-
-@pytest.mark.skip(reason="missing test")
-def test_project_snapshot(gl):
- pass
-
-
-@pytest.mark.skip(reason="missing test")
-def test_import_github(gl):
- pass
diff --git a/tests/unit/objects/test_releases.py b/tests/unit/objects/test_releases.py
deleted file mode 100644
index 58ab5d0..0000000
--- a/tests/unit/objects/test_releases.py
+++ /dev/null
@@ -1,170 +0,0 @@
-"""
-GitLab API:
-https://docs.gitlab.com/ee/api/releases/index.html
-https://docs.gitlab.com/ee/api/releases/links.html
-"""
-import re
-
-import pytest
-import responses
-
-from gitlab.v4.objects import ProjectReleaseLink
-
-tag_name = "v1.0.0"
-encoded_tag_name = "v1%2E0%2E0"
-release_name = "demo-release"
-release_description = "my-rel-desc"
-released_at = "2019-03-15T08:00:00Z"
-link_name = "hello-world"
-link_url = "https://gitlab.example.com/group/hello/-/jobs/688/artifacts/raw/bin/hello-darwin-amd64"
-direct_url = f"https://gitlab.example.com/group/hello/-/releases/{encoded_tag_name}/downloads/hello-world"
-new_link_type = "package"
-link_content = {
- "id": 2,
- "name": link_name,
- "url": link_url,
- "direct_asset_url": direct_url,
- "external": False,
- "link_type": "other",
-}
-
-release_content = {
- "id": 3,
- "tag_name": tag_name,
- "name": release_name,
- "description": release_description,
- "milestones": [],
- "released_at": released_at,
-}
-
-release_url = re.compile(
- rf"http://localhost/api/v4/projects/1/releases/{encoded_tag_name}"
-)
-links_url = re.compile(
- rf"http://localhost/api/v4/projects/1/releases/{encoded_tag_name}/assets/links"
-)
-link_id_url = re.compile(
- rf"http://localhost/api/v4/projects/1/releases/{encoded_tag_name}/assets/links/1"
-)
-
-
-@pytest.fixture
-def resp_list_links():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url=links_url,
- json=[link_content],
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_get_link():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url=link_id_url,
- json=link_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_create_link():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.POST,
- url=links_url,
- json=link_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_update_link():
- updated_content = dict(link_content)
- updated_content["link_type"] = new_link_type
-
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.PUT,
- url=link_id_url,
- json=updated_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_delete_link(no_content):
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.DELETE,
- url=link_id_url,
- json=link_content,
- content_type="application/json",
- status=204,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_update_release():
- updated_content = dict(release_content)
-
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.PUT,
- url=release_url,
- json=updated_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-def test_list_release_links(release, resp_list_links):
- links = release.links.list()
- assert isinstance(links, list)
- assert isinstance(links[0], ProjectReleaseLink)
- assert links[0].url == link_url
-
-
-def test_get_release_link(release, resp_get_link):
- link = release.links.get(1)
- assert isinstance(link, ProjectReleaseLink)
- assert link.url == link_url
-
-
-def test_create_release_link(release, resp_create_link):
- link = release.links.create({"url": link_url, "name": link_name})
- assert isinstance(link, ProjectReleaseLink)
- assert link.url == link_url
-
-
-def test_update_release_link(release, resp_update_link):
- link = release.links.get(1, lazy=True)
- link.link_type = new_link_type
- link.save()
- assert link.link_type == new_link_type
-
-
-def test_delete_release_link(release, resp_delete_link):
- link = release.links.get(1, lazy=True)
- link.delete()
-
-
-def test_update_release(release, resp_update_release):
- release.name = release_name
- release.description = release_description
- release.save()
- assert release.name == release_name
- assert release.description == release_description
diff --git a/tests/unit/objects/test_remote_mirrors.py b/tests/unit/objects/test_remote_mirrors.py
deleted file mode 100644
index 1ac35a2..0000000
--- a/tests/unit/objects/test_remote_mirrors.py
+++ /dev/null
@@ -1,72 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ce/api/remote_mirrors.html
-"""
-
-import pytest
-import responses
-
-from gitlab.v4.objects import ProjectRemoteMirror
-
-
-@pytest.fixture
-def resp_remote_mirrors():
- content = {
- "enabled": True,
- "id": 1,
- "last_error": None,
- "last_successful_update_at": "2020-01-06T17:32:02.823Z",
- "last_update_at": "2020-01-06T17:32:02.823Z",
- "last_update_started_at": "2020-01-06T17:31:55.864Z",
- "only_protected_branches": True,
- "update_status": "none",
- "url": "https://*****:*****@gitlab.com/gitlab-org/security/gitlab.git",
- }
-
- with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/remote_mirrors",
- json=[content],
- content_type="application/json",
- status=200,
- )
- rsps.add(
- method=responses.POST,
- url="http://localhost/api/v4/projects/1/remote_mirrors",
- json=content,
- content_type="application/json",
- status=200,
- )
-
- updated_content = dict(content)
- updated_content["update_status"] = "finished"
-
- rsps.add(
- method=responses.PUT,
- url="http://localhost/api/v4/projects/1/remote_mirrors/1",
- json=updated_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-def test_list_project_remote_mirrors(project, resp_remote_mirrors):
- mirrors = project.remote_mirrors.list()
- assert isinstance(mirrors, list)
- assert isinstance(mirrors[0], ProjectRemoteMirror)
- assert mirrors[0].enabled
-
-
-def test_create_project_remote_mirror(project, resp_remote_mirrors):
- mirror = project.remote_mirrors.create({"url": "https://example.com"})
- assert isinstance(mirror, ProjectRemoteMirror)
- assert mirror.update_status == "none"
-
-
-def test_update_project_remote_mirror(project, resp_remote_mirrors):
- mirror = project.remote_mirrors.create({"url": "https://example.com"})
- mirror.only_protected_branches = True
- mirror.save()
- assert mirror.update_status == "finished"
- assert mirror.only_protected_branches
diff --git a/tests/unit/objects/test_repositories.py b/tests/unit/objects/test_repositories.py
deleted file mode 100644
index 7c4d77d..0000000
--- a/tests/unit/objects/test_repositories.py
+++ /dev/null
@@ -1,49 +0,0 @@
-"""
-GitLab API:
-https://docs.gitlab.com/ee/api/repositories.html
-https://docs.gitlab.com/ee/api/repository_files.html
-"""
-from urllib.parse import quote
-
-import pytest
-import responses
-
-from gitlab.v4.objects import ProjectFile
-
-file_path = "app/models/key.rb"
-ref = "main"
-
-
-@pytest.fixture
-def resp_get_repository_file():
- file_response = {
- "file_name": "key.rb",
- "file_path": file_path,
- "size": 1476,
- "encoding": "base64",
- "content": "IyA9PSBTY2hlbWEgSW5mb3...",
- "content_sha256": "4c294617b60715c1d218e61164a3abd4808a4284cbc30e6728a01ad9aada4481",
- "ref": ref,
- "blob_id": "79f7bbd25901e8334750839545a9bd021f0e4c83",
- "commit_id": "d5a3ff139356ce33e37e73add446f16869741b50",
- "last_commit_id": "570e7b2abdd848b95f2f578043fc23bd6f6fd24d",
- }
-
- # requests also encodes `.`
- encoded_path = quote(file_path, safe="").replace(".", "%2E")
-
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url=f"http://localhost/api/v4/projects/1/repository/files/{encoded_path}",
- json=file_response,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-def test_get_repository_file(project, resp_get_repository_file):
- file = project.files.get(file_path, ref=ref)
- assert isinstance(file, ProjectFile)
- assert file.file_path == file_path
diff --git a/tests/unit/objects/test_resource_label_events.py b/tests/unit/objects/test_resource_label_events.py
deleted file mode 100644
index deea8a0..0000000
--- a/tests/unit/objects/test_resource_label_events.py
+++ /dev/null
@@ -1,105 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ee/api/resource_label_events.html
-"""
-
-import pytest
-import responses
-
-from gitlab.v4.objects import (
- GroupEpicResourceLabelEvent,
- ProjectIssueResourceLabelEvent,
- ProjectMergeRequestResourceLabelEvent,
-)
-
-
-@pytest.fixture()
-def resp_group_epic_request_label_events():
- epic_content = {"id": 1}
- events_content = {"id": 1, "resource_type": "Epic"}
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/groups/1/epics",
- json=[epic_content],
- content_type="application/json",
- status=200,
- )
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/groups/1/epics/1/resource_label_events",
- json=[events_content],
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture()
-def resp_merge_request_label_events():
- mr_content = {"iid": 1}
- events_content = {"id": 1, "resource_type": "MergeRequest"}
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/merge_requests",
- json=[mr_content],
- content_type="application/json",
- status=200,
- )
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/merge_requests/1/resource_label_events",
- json=[events_content],
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture()
-def resp_project_issue_label_events():
- issue_content = {"iid": 1}
- events_content = {"id": 1, "resource_type": "Issue"}
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/issues",
- json=[issue_content],
- content_type="application/json",
- status=200,
- )
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/issues/1/resource_label_events",
- json=[events_content],
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-def test_project_issue_label_events(project, resp_project_issue_label_events):
- issue = project.issues.list()[0]
- label_events = issue.resourcelabelevents.list()
- assert isinstance(label_events, list)
- label_event = label_events[0]
- assert isinstance(label_event, ProjectIssueResourceLabelEvent)
- assert label_event.resource_type == "Issue"
-
-
-def test_merge_request_label_events(project, resp_merge_request_label_events):
- mr = project.mergerequests.list()[0]
- label_events = mr.resourcelabelevents.list()
- assert isinstance(label_events, list)
- label_event = label_events[0]
- assert isinstance(label_event, ProjectMergeRequestResourceLabelEvent)
- assert label_event.resource_type == "MergeRequest"
-
-
-def test_group_epic_request_label_events(group, resp_group_epic_request_label_events):
- epic = group.epics.list()[0]
- label_events = epic.resourcelabelevents.list()
- assert isinstance(label_events, list)
- label_event = label_events[0]
- assert isinstance(label_event, GroupEpicResourceLabelEvent)
- assert label_event.resource_type == "Epic"
diff --git a/tests/unit/objects/test_resource_milestone_events.py b/tests/unit/objects/test_resource_milestone_events.py
deleted file mode 100644
index 99faeaa..0000000
--- a/tests/unit/objects/test_resource_milestone_events.py
+++ /dev/null
@@ -1,73 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ee/api/resource_milestone_events.html
-"""
-
-import pytest
-import responses
-
-from gitlab.v4.objects import (
- ProjectIssueResourceMilestoneEvent,
- ProjectMergeRequestResourceMilestoneEvent,
-)
-
-
-@pytest.fixture()
-def resp_merge_request_milestone_events():
- mr_content = {"iid": 1}
- events_content = {"id": 1, "resource_type": "MergeRequest"}
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/merge_requests",
- json=[mr_content],
- content_type="application/json",
- status=200,
- )
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/merge_requests/1/resource_milestone_events",
- json=[events_content],
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture()
-def resp_project_issue_milestone_events():
- issue_content = {"iid": 1}
- events_content = {"id": 1, "resource_type": "Issue"}
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/issues",
- json=[issue_content],
- content_type="application/json",
- status=200,
- )
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/issues/1/resource_milestone_events",
- json=[events_content],
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-def test_project_issue_milestone_events(project, resp_project_issue_milestone_events):
- issue = project.issues.list()[0]
- milestone_events = issue.resourcemilestoneevents.list()
- assert isinstance(milestone_events, list)
- milestone_event = milestone_events[0]
- assert isinstance(milestone_event, ProjectIssueResourceMilestoneEvent)
- assert milestone_event.resource_type == "Issue"
-
-
-def test_merge_request_milestone_events(project, resp_merge_request_milestone_events):
- mr = project.mergerequests.list()[0]
- milestone_events = mr.resourcemilestoneevents.list()
- assert isinstance(milestone_events, list)
- milestone_event = milestone_events[0]
- assert isinstance(milestone_event, ProjectMergeRequestResourceMilestoneEvent)
- assert milestone_event.resource_type == "MergeRequest"
diff --git a/tests/unit/objects/test_resource_state_events.py b/tests/unit/objects/test_resource_state_events.py
deleted file mode 100644
index bf18193..0000000
--- a/tests/unit/objects/test_resource_state_events.py
+++ /dev/null
@@ -1,104 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ee/api/resource_state_events.html
-"""
-
-import pytest
-import responses
-
-from gitlab.v4.objects import (
- ProjectIssueResourceStateEvent,
- ProjectMergeRequestResourceStateEvent,
-)
-
-issue_event_content = {"id": 1, "resource_type": "Issue"}
-mr_event_content = {"id": 1, "resource_type": "MergeRequest"}
-
-
-@pytest.fixture()
-def resp_list_project_issue_state_events():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/issues/1/resource_state_events",
- json=[issue_event_content],
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture()
-def resp_get_project_issue_state_event():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/issues/1/resource_state_events/1",
- json=issue_event_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture()
-def resp_list_merge_request_state_events():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/merge_requests/1/resource_state_events",
- json=[mr_event_content],
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture()
-def resp_get_merge_request_state_event():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/merge_requests/1/resource_state_events/1",
- json=mr_event_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-def test_list_project_issue_state_events(
- project_issue, resp_list_project_issue_state_events
-):
- state_events = project_issue.resourcestateevents.list()
- assert isinstance(state_events, list)
-
- state_event = state_events[0]
- assert isinstance(state_event, ProjectIssueResourceStateEvent)
- assert state_event.resource_type == "Issue"
-
-
-def test_get_project_issue_state_event(
- project_issue, resp_get_project_issue_state_event
-):
- state_event = project_issue.resourcestateevents.get(1)
- assert isinstance(state_event, ProjectIssueResourceStateEvent)
- assert state_event.resource_type == "Issue"
-
-
-def test_list_merge_request_state_events(
- project_merge_request, resp_list_merge_request_state_events
-):
- state_events = project_merge_request.resourcestateevents.list()
- assert isinstance(state_events, list)
-
- state_event = state_events[0]
- assert isinstance(state_event, ProjectMergeRequestResourceStateEvent)
- assert state_event.resource_type == "MergeRequest"
-
-
-def test_get_merge_request_state_event(
- project_merge_request, resp_get_merge_request_state_event
-):
- state_event = project_merge_request.resourcestateevents.get(1)
- assert isinstance(state_event, ProjectMergeRequestResourceStateEvent)
- assert state_event.resource_type == "MergeRequest"
diff --git a/tests/unit/objects/test_runners.py b/tests/unit/objects/test_runners.py
deleted file mode 100644
index 686eec2..0000000
--- a/tests/unit/objects/test_runners.py
+++ /dev/null
@@ -1,282 +0,0 @@
-import re
-
-import pytest
-import responses
-
-import gitlab
-
-runner_detail = {
- "active": True,
- "architecture": "amd64",
- "description": "test-1-20150125",
- "id": 6,
- "ip_address": "127.0.0.1",
- "is_shared": False,
- "contacted_at": "2016-01-25T16:39:48.066Z",
- "name": "test-runner",
- "online": True,
- "status": "online",
- "platform": "linux",
- "projects": [
- {
- "id": 1,
- "name": "GitLab Community Edition",
- "name_with_namespace": "GitLab.org / GitLab Community Edition",
- "path": "gitlab-foss",
- "path_with_namespace": "gitlab-org/gitlab-foss",
- }
- ],
- "revision": "5nj35",
- "tag_list": ["ruby", "mysql"],
- "version": "v13.0.0",
- "access_level": "ref_protected",
- "maximum_timeout": 3600,
-}
-
-runner_shortinfo = {
- "active": True,
- "description": "test-1-20150125",
- "id": 6,
- "is_shared": False,
- "ip_address": "127.0.0.1",
- "name": "test-name",
- "online": True,
- "status": "online",
-}
-
-runner_jobs = [
- {
- "id": 6,
- "ip_address": "127.0.0.1",
- "status": "running",
- "stage": "test",
- "name": "test",
- "ref": "master",
- "tag": False,
- "coverage": "99%",
- "created_at": "2017-11-16T08:50:29.000Z",
- "started_at": "2017-11-16T08:51:29.000Z",
- "finished_at": "2017-11-16T08:53:29.000Z",
- "duration": 120,
- "user": {
- "id": 1,
- "name": "John Doe2",
- "username": "user2",
- "state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/c922747a93b40d1ea88262bf1aebee62?s=80&d=identicon",
- "web_url": "http://localhost/user2",
- "created_at": "2017-11-16T18:38:46.000Z",
- "bio": None,
- "location": None,
- "public_email": "",
- "skype": "",
- "linkedin": "",
- "twitter": "",
- "website_url": "",
- "organization": None,
- },
- }
-]
-
-
-@pytest.fixture
-def resp_get_runners_jobs():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/runners/6/jobs",
- json=runner_jobs,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_get_runners_list():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url=re.compile(r".*?(/runners(/all)?|/(groups|projects)/1/runners)"),
- json=[runner_shortinfo],
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_runner_detail():
- with responses.RequestsMock() as rsps:
- pattern = re.compile(r".*?/runners/6")
- rsps.add(
- method=responses.GET,
- url=pattern,
- json=runner_detail,
- content_type="application/json",
- status=200,
- )
- rsps.add(
- method=responses.PUT,
- url=pattern,
- json=runner_detail,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_runner_register():
- with responses.RequestsMock() as rsps:
- pattern = re.compile(r".*?/runners")
- rsps.add(
- method=responses.POST,
- url=pattern,
- json={"id": "6", "token": "6337ff461c94fd3fa32ba3b1ff4125"},
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_runner_enable():
- with responses.RequestsMock() as rsps:
- pattern = re.compile(r".*?(projects|groups)/1/runners")
- rsps.add(
- method=responses.POST,
- url=pattern,
- json=runner_shortinfo,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_runner_delete():
- with responses.RequestsMock() as rsps:
- pattern = re.compile(r".*?/runners/6")
- rsps.add(
- method=responses.GET,
- url=pattern,
- json=runner_detail,
- content_type="application/json",
- status=200,
- )
- rsps.add(
- method=responses.DELETE,
- url=pattern,
- status=204,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_runner_disable():
- with responses.RequestsMock() as rsps:
- pattern = re.compile(r".*?/(groups|projects)/1/runners/6")
- rsps.add(
- method=responses.DELETE,
- url=pattern,
- status=204,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_runner_verify():
- with responses.RequestsMock() as rsps:
- pattern = re.compile(r".*?/runners/verify")
- rsps.add(
- method=responses.POST,
- url=pattern,
- status=200,
- )
- yield rsps
-
-
-def test_owned_runners_list(gl: gitlab.Gitlab, resp_get_runners_list):
- runners = gl.runners.list()
- assert runners[0].active is True
- assert runners[0].id == 6
- assert runners[0].name == "test-name"
- assert len(runners) == 1
-
-
-def test_project_runners_list(gl: gitlab.Gitlab, resp_get_runners_list):
- runners = gl.projects.get(1, lazy=True).runners.list()
- assert runners[0].active is True
- assert runners[0].id == 6
- assert runners[0].name == "test-name"
- assert len(runners) == 1
-
-
-def test_group_runners_list(gl: gitlab.Gitlab, resp_get_runners_list):
- runners = gl.groups.get(1, lazy=True).runners.list()
- assert runners[0].active is True
- assert runners[0].id == 6
- assert runners[0].name == "test-name"
- assert len(runners) == 1
-
-
-def test_all_runners_list(gl: gitlab.Gitlab, resp_get_runners_list):
- runners = gl.runners.all()
- assert runners[0].active is True
- assert runners[0].id == 6
- assert runners[0].name == "test-name"
- assert len(runners) == 1
-
-
-def test_create_runner(gl: gitlab.Gitlab, resp_runner_register):
- runner = gl.runners.create({"token": "token"})
- assert runner.id == "6"
- assert runner.token == "6337ff461c94fd3fa32ba3b1ff4125"
-
-
-def test_get_update_runner(gl: gitlab.Gitlab, resp_runner_detail):
- runner = gl.runners.get(6)
- assert runner.active is True
- runner.tag_list.append("new")
- runner.save()
-
-
-def test_remove_runner(gl: gitlab.Gitlab, resp_runner_delete):
- runner = gl.runners.get(6)
- runner.delete()
- gl.runners.delete(6)
-
-
-def test_disable_project_runner(gl: gitlab.Gitlab, resp_runner_disable):
- gl.projects.get(1, lazy=True).runners.delete(6)
-
-
-def test_disable_group_runner(gl: gitlab.Gitlab, resp_runner_disable):
- gl.groups.get(1, lazy=True).runners.delete(6)
-
-
-def test_enable_project_runner(gl: gitlab.Gitlab, resp_runner_enable):
- runner = gl.projects.get(1, lazy=True).runners.create({"runner_id": 6})
- assert runner.active is True
- assert runner.id == 6
- assert runner.name == "test-name"
-
-
-def test_enable_group_runner(gl: gitlab.Gitlab, resp_runner_enable):
- runner = gl.groups.get(1, lazy=True).runners.create({"runner_id": 6})
- assert runner.active is True
- assert runner.id == 6
- assert runner.name == "test-name"
-
-
-def test_verify_runner(gl: gitlab.Gitlab, resp_runner_verify):
- gl.runners.verify("token")
-
-
-def test_runner_jobs(gl: gitlab.Gitlab, resp_get_runners_jobs):
- jobs = gl.runners.get(6, lazy=True).jobs.list()
- assert jobs[0].duration == 120
- assert jobs[0].name == "test"
- assert jobs[0].user.get("name") == "John Doe2"
- assert len(jobs) == 1
diff --git a/tests/unit/objects/test_services.py b/tests/unit/objects/test_services.py
deleted file mode 100644
index 5b2bcb8..0000000
--- a/tests/unit/objects/test_services.py
+++ /dev/null
@@ -1,93 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ce/api/services.html
-"""
-
-import pytest
-import responses
-
-from gitlab.v4.objects import ProjectService
-
-
-@pytest.fixture
-def resp_service():
- content = {
- "id": 100152,
- "title": "Pipelines emails",
- "slug": "pipelines-email",
- "created_at": "2019-01-14T08:46:43.637+01:00",
- "updated_at": "2019-07-01T14:10:36.156+02:00",
- "active": True,
- "commit_events": True,
- "push_events": True,
- "issues_events": True,
- "confidential_issues_events": True,
- "merge_requests_events": True,
- "tag_push_events": True,
- "note_events": True,
- "confidential_note_events": True,
- "pipeline_events": True,
- "wiki_page_events": True,
- "job_events": True,
- "comment_on_event_enabled": True,
- "project_id": 1,
- }
-
- with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/services",
- json=[content],
- content_type="application/json",
- status=200,
- )
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/services",
- json=content,
- content_type="application/json",
- status=200,
- )
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/services/pipelines-email",
- json=content,
- content_type="application/json",
- status=200,
- )
- updated_content = dict(content)
- updated_content["issues_events"] = False
- rsps.add(
- method=responses.PUT,
- url="http://localhost/api/v4/projects/1/services/pipelines-email",
- json=updated_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-def test_list_active_services(project, resp_service):
- services = project.services.list()
- assert isinstance(services, list)
- assert isinstance(services[0], ProjectService)
- assert services[0].active
- assert services[0].push_events
-
-
-def test_list_available_services(project, resp_service):
- services = project.services.available()
- assert isinstance(services, list)
- assert isinstance(services[0], str)
-
-
-def test_get_service(project, resp_service):
- service = project.services.get("pipelines-email")
- assert isinstance(service, ProjectService)
- assert service.push_events is True
-
-
-def test_update_service(project, resp_service):
- service = project.services.get("pipelines-email")
- service.issues_events = False
- service.save()
- assert service.issues_events is False
diff --git a/tests/unit/objects/test_snippets.py b/tests/unit/objects/test_snippets.py
deleted file mode 100644
index 2540fc3..0000000
--- a/tests/unit/objects/test_snippets.py
+++ /dev/null
@@ -1,89 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ce/api/project_snippets.html
- https://docs.gitlab.com/ee/api/snippets.html (todo)
-"""
-
-import pytest
-import responses
-
-title = "Example Snippet Title"
-visibility = "private"
-new_title = "new-title"
-
-
-@pytest.fixture
-def resp_snippet():
- content = {
- "title": title,
- "description": "More verbose snippet description",
- "file_name": "example.txt",
- "content": "source code with multiple lines",
- "visibility": visibility,
- }
-
- with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/snippets",
- json=[content],
- content_type="application/json",
- status=200,
- )
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/projects/1/snippets/1",
- json=content,
- content_type="application/json",
- status=200,
- )
- rsps.add(
- method=responses.POST,
- url="http://localhost/api/v4/projects/1/snippets",
- json=content,
- content_type="application/json",
- status=200,
- )
-
- updated_content = dict(content)
- updated_content["title"] = new_title
- updated_content["visibility"] = visibility
-
- rsps.add(
- method=responses.PUT,
- url="http://localhost/api/v4/projects/1/snippets",
- json=updated_content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-def test_list_project_snippets(project, resp_snippet):
- snippets = project.snippets.list()
- assert len(snippets) == 1
- assert snippets[0].title == title
- assert snippets[0].visibility == visibility
-
-
-def test_get_project_snippet(project, resp_snippet):
- snippet = project.snippets.get(1)
- assert snippet.title == title
- assert snippet.visibility == visibility
-
-
-def test_create_update_project_snippets(project, resp_snippet):
- snippet = project.snippets.create(
- {
- "title": title,
- "file_name": title,
- "content": title,
- "visibility": visibility,
- }
- )
- assert snippet.title == title
- assert snippet.visibility == visibility
-
- snippet.title = new_title
- snippet.save()
- assert snippet.title == new_title
- assert snippet.visibility == visibility
diff --git a/tests/unit/objects/test_submodules.py b/tests/unit/objects/test_submodules.py
deleted file mode 100644
index 69c1cd7..0000000
--- a/tests/unit/objects/test_submodules.py
+++ /dev/null
@@ -1,46 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ce/api/repository_submodules.html
-"""
-import pytest
-import responses
-
-
-@pytest.fixture
-def resp_update_submodule():
- content = {
- "id": "ed899a2f4b50b4370feeea94676502b42383c746",
- "short_id": "ed899a2f4b5",
- "title": "Message",
- "author_name": "Author",
- "author_email": "author@example.com",
- "committer_name": "Author",
- "committer_email": "author@example.com",
- "created_at": "2018-09-20T09:26:24.000-07:00",
- "message": "Message",
- "parent_ids": ["ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba"],
- "committed_date": "2018-09-20T09:26:24.000-07:00",
- "authored_date": "2018-09-20T09:26:24.000-07:00",
- "status": None,
- }
-
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.PUT,
- url="http://localhost/api/v4/projects/1/repository/submodules/foo%2Fbar",
- json=content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-def test_update_submodule(project, resp_update_submodule):
- ret = project.update_submodule(
- submodule="foo/bar",
- branch="master",
- commit_sha="4c3674f66071e30b3311dac9b9ccc90502a72664",
- commit_message="Message",
- )
- assert isinstance(ret, dict)
- assert ret["message"] == "Message"
- assert ret["id"] == "ed899a2f4b50b4370feeea94676502b42383c746"
diff --git a/tests/unit/objects/test_todos.py b/tests/unit/objects/test_todos.py
deleted file mode 100644
index 058fe33..0000000
--- a/tests/unit/objects/test_todos.py
+++ /dev/null
@@ -1,62 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ce/api/todos.html
-"""
-
-import json
-import os
-
-import pytest
-import responses
-
-from gitlab.v4.objects import Todo
-
-with open(os.path.dirname(__file__) + "/../data/todo.json", "r") as json_file:
- todo_content = json_file.read()
- json_content = json.loads(todo_content)
-
-
-@pytest.fixture
-def resp_todo():
- with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/todos",
- json=json_content,
- content_type="application/json",
- status=200,
- )
- rsps.add(
- method=responses.POST,
- url="http://localhost/api/v4/todos/102/mark_as_done",
- json=json_content[0],
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_mark_all_as_done():
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.POST,
- url="http://localhost/api/v4/todos/mark_as_done",
- json={},
- content_type="application/json",
- status=204,
- )
- yield rsps
-
-
-def test_todo(gl, resp_todo):
- todo = gl.todos.list()[0]
- assert isinstance(todo, Todo)
- assert todo.id == 102
- assert todo.target_type == "MergeRequest"
- assert todo.target["assignee"]["username"] == "root"
-
- todo.mark_as_done()
-
-
-def test_todo_mark_all_as_done(gl, resp_mark_all_as_done):
- gl.todos.mark_all_as_done()
diff --git a/tests/unit/objects/test_users.py b/tests/unit/objects/test_users.py
deleted file mode 100644
index e46a315..0000000
--- a/tests/unit/objects/test_users.py
+++ /dev/null
@@ -1,217 +0,0 @@
-"""
-GitLab API: https://docs.gitlab.com/ce/api/users.html
-"""
-import pytest
-import responses
-
-from gitlab.v4.objects import User, UserMembership, UserStatus
-
-
-@pytest.fixture
-def resp_get_user():
- content = {
- "name": "name",
- "id": 1,
- "password": "password",
- "username": "username",
- "email": "email",
- }
-
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/users/1",
- json=content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_get_user_memberships():
- content = [
- {
- "source_id": 1,
- "source_name": "Project one",
- "source_type": "Project",
- "access_level": "20",
- },
- {
- "source_id": 3,
- "source_name": "Group three",
- "source_type": "Namespace",
- "access_level": "20",
- },
- ]
-
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/users/1/memberships",
- json=content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_activate():
- with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
- rsps.add(
- method=responses.POST,
- url="http://localhost/api/v4/users/1/activate",
- json={},
- content_type="application/json",
- status=201,
- )
- rsps.add(
- method=responses.POST,
- url="http://localhost/api/v4/users/1/deactivate",
- json={},
- content_type="application/json",
- status=201,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_get_user_status():
- content = {
- "message": "test",
- "message_html": "<h1>Message</h1>",
- "emoji": "thumbsup",
- }
-
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/users/1/status",
- json=content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_delete_user_identity(no_content):
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.DELETE,
- url="http://localhost/api/v4/users/1/identities/test_provider",
- json=no_content,
- content_type="application/json",
- status=204,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_follow_unfollow():
- user = {
- "id": 1,
- "username": "john_smith",
- "name": "John Smith",
- "state": "active",
- "avatar_url": "http://localhost:3000/uploads/user/avatar/1/cd8.jpeg",
- "web_url": "http://localhost:3000/john_smith",
- }
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.POST,
- url="http://localhost/api/v4/users/1/follow",
- json=user,
- content_type="application/json",
- status=201,
- )
- rsps.add(
- method=responses.POST,
- url="http://localhost/api/v4/users/1/unfollow",
- json=user,
- content_type="application/json",
- status=201,
- )
- yield rsps
-
-
-@pytest.fixture
-def resp_followers_following():
- content = [
- {
- "id": 2,
- "name": "Lennie Donnelly",
- "username": "evette.kilback",
- "state": "active",
- "avatar_url": "https://www.gravatar.com/avatar/7955171a55ac4997ed81e5976287890a?s=80&d=identicon",
- "web_url": "http://127.0.0.1:3000/evette.kilback",
- },
- {
- "id": 4,
- "name": "Serena Bradtke",
- "username": "cammy",
- "state": "active",
- "avatar_url": "https://www.gravatar.com/avatar/a2daad869a7b60d3090b7b9bef4baf57?s=80&d=identicon",
- "web_url": "http://127.0.0.1:3000/cammy",
- },
- ]
- with responses.RequestsMock() as rsps:
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/users/1/followers",
- json=content,
- content_type="application/json",
- status=200,
- )
- rsps.add(
- method=responses.GET,
- url="http://localhost/api/v4/users/1/following",
- json=content,
- content_type="application/json",
- status=200,
- )
- yield rsps
-
-
-def test_get_user(gl, resp_get_user):
- user = gl.users.get(1)
- assert isinstance(user, User)
- assert user.name == "name"
- assert user.id == 1
-
-
-def test_user_memberships(user, resp_get_user_memberships):
- memberships = user.memberships.list()
- assert isinstance(memberships[0], UserMembership)
- assert memberships[0].source_type == "Project"
-
-
-def test_user_status(user, resp_get_user_status):
- status = user.status.get()
- assert isinstance(status, UserStatus)
- assert status.message == "test"
- assert status.emoji == "thumbsup"
-
-
-def test_user_activate_deactivate(user, resp_activate):
- user.activate()
- user.deactivate()
-
-
-def test_delete_user_identity(user, resp_delete_user_identity):
- user.identityproviders.delete("test_provider")
-
-
-def test_user_follow_unfollow(user, resp_follow_unfollow):
- user.follow()
- user.unfollow()
-
-
-def test_list_followers(user, resp_followers_following):
- followers = user.followers_users.list()
- followings = user.following_users.list()
- assert isinstance(followers[0], User)
- assert followers[0].id == 2
- assert isinstance(followings[0], User)
- assert followings[1].id == 4
diff --git a/tests/unit/objects/test_variables.py b/tests/unit/objects/test_variables.py
deleted file mode 100644
index fae37a8..0000000
--- a/tests/unit/objects/test_variables.py
+++ /dev/null
@@ -1,192 +0,0 @@
-"""
-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/tests/unit/test_base.py b/tests/unit/test_base.py
deleted file mode 100644
index cccdfad..0000000
--- a/tests/unit/test_base.py
+++ /dev/null
@@ -1,179 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2017 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-import pickle
-
-import pytest
-
-import gitlab
-from gitlab import base
-
-
-class FakeGitlab(object):
- pass
-
-
-class FakeObject(base.RESTObject):
- pass
-
-
-class FakeManager(base.RESTManager):
- _obj_cls = FakeObject
- _path = "/tests"
-
-
-@pytest.fixture
-def fake_gitlab():
- return FakeGitlab()
-
-
-@pytest.fixture
-def fake_manager(fake_gitlab):
- return FakeManager(fake_gitlab)
-
-
-class TestRESTManager:
- def test_computed_path_simple(self):
- class MGR(base.RESTManager):
- _path = "/tests"
- _obj_cls = object
-
- mgr = MGR(FakeGitlab())
- assert mgr._computed_path == "/tests"
-
- def test_computed_path_with_parent(self):
- class MGR(base.RESTManager):
- _path = "/tests/%(test_id)s/cases"
- _obj_cls = object
- _from_parent_attrs = {"test_id": "id"}
-
- class Parent(object):
- id = 42
-
- mgr = MGR(FakeGitlab(), parent=Parent())
- assert mgr._computed_path == "/tests/42/cases"
-
- def test_path_property(self):
- class MGR(base.RESTManager):
- _path = "/tests"
- _obj_cls = object
-
- mgr = MGR(FakeGitlab())
- assert mgr.path == "/tests"
-
-
-class TestRESTObject:
- def test_instantiate(self, fake_gitlab, fake_manager):
- obj = FakeObject(fake_manager, {"foo": "bar"})
-
- assert {"foo": "bar"} == obj._attrs
- assert {} == obj._updated_attrs
- assert obj._create_managers() is None
- assert fake_manager == obj.manager
- assert fake_gitlab == obj.manager.gitlab
-
- def test_instantiate_non_dict(self, fake_gitlab, fake_manager):
- with pytest.raises(gitlab.exceptions.GitlabParsingError):
- FakeObject(fake_manager, ["a", "list", "fails"])
-
- def test_picklability(self, fake_manager):
- obj = FakeObject(fake_manager, {"foo": "bar"})
- original_obj_module = obj._module
- pickled = pickle.dumps(obj)
- unpickled = pickle.loads(pickled)
- assert isinstance(unpickled, FakeObject)
- assert hasattr(unpickled, "_module")
- assert unpickled._module == original_obj_module
- pickle.dumps(unpickled)
-
- def test_attrs(self, fake_manager):
- obj = FakeObject(fake_manager, {"foo": "bar"})
-
- assert "bar" == obj.foo
- with pytest.raises(AttributeError):
- getattr(obj, "bar")
-
- obj.bar = "baz"
- assert "baz" == obj.bar
- assert {"foo": "bar"} == obj._attrs
- assert {"bar": "baz"} == obj._updated_attrs
-
- def test_get_id(self, fake_manager):
- obj = FakeObject(fake_manager, {"foo": "bar"})
- obj.id = 42
- assert 42 == obj.get_id()
-
- obj.id = None
- assert obj.get_id() is None
-
- def test_custom_id_attr(self, fake_manager):
- class OtherFakeObject(FakeObject):
- _id_attr = "foo"
-
- obj = OtherFakeObject(fake_manager, {"foo": "bar"})
- assert "bar" == obj.get_id()
-
- def test_update_attrs(self, fake_manager):
- obj = FakeObject(fake_manager, {"foo": "bar"})
- obj.bar = "baz"
- obj._update_attrs({"foo": "foo", "bar": "bar"})
- assert {"foo": "foo", "bar": "bar"} == obj._attrs
- assert {} == obj._updated_attrs
-
- def test_update_attrs_deleted(self, fake_manager):
- obj = FakeObject(fake_manager, {"foo": "foo", "bar": "bar"})
- obj.bar = "baz"
- obj._update_attrs({"foo": "foo"})
- assert {"foo": "foo"} == obj._attrs
- assert {} == obj._updated_attrs
-
- def test_dir_unique(self, fake_manager):
- obj = FakeObject(fake_manager, {"manager": "foo"})
- assert len(dir(obj)) == len(set(dir(obj)))
-
- def test_create_managers(self, fake_gitlab, fake_manager):
- class ObjectWithManager(FakeObject):
- fakes: "FakeManager"
-
- obj = ObjectWithManager(fake_manager, {"foo": "bar"})
- obj.id = 42
- assert isinstance(obj.fakes, FakeManager)
- assert obj.fakes.gitlab == fake_gitlab
- assert obj.fakes._parent == obj
-
- def test_equality(self, fake_manager):
- obj1 = FakeObject(fake_manager, {"id": "foo"})
- obj2 = FakeObject(fake_manager, {"id": "foo", "other_attr": "bar"})
- assert obj1 == obj2
-
- def test_equality_custom_id(self, fake_manager):
- class OtherFakeObject(FakeObject):
- _id_attr = "foo"
-
- obj1 = OtherFakeObject(fake_manager, {"foo": "bar"})
- obj2 = OtherFakeObject(fake_manager, {"foo": "bar", "other_attr": "baz"})
- assert obj1 == obj2
-
- def test_inequality(self, fake_manager):
- obj1 = FakeObject(fake_manager, {"id": "foo"})
- obj2 = FakeObject(fake_manager, {"id": "bar"})
- assert obj1 != obj2
-
- def test_inequality_no_id(self, fake_manager):
- obj1 = FakeObject(fake_manager, {"attr1": "foo"})
- obj2 = FakeObject(fake_manager, {"attr1": "bar"})
- assert obj1 != obj2
diff --git a/tests/unit/test_cli.py b/tests/unit/test_cli.py
deleted file mode 100644
index a9ca958..0000000
--- a/tests/unit/test_cli.py
+++ /dev/null
@@ -1,157 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2016-2017 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-import argparse
-import io
-import os
-import tempfile
-from contextlib import redirect_stderr # noqa: H302
-
-import pytest
-
-from gitlab import cli
-
-
-@pytest.mark.parametrize(
- "what,expected_class",
- [
- ("class", "Class"),
- ("test-class", "TestClass"),
- ("test-longer-class", "TestLongerClass"),
- ("current-user-gpg-key", "CurrentUserGPGKey"),
- ("user-gpg-key", "UserGPGKey"),
- ("ldap-group", "LDAPGroup"),
- ],
-)
-def test_what_to_cls(what, expected_class):
- def _namespace():
- pass
-
- ExpectedClass = type(expected_class, (), {})
- _namespace.__dict__[expected_class] = ExpectedClass
-
- assert cli.what_to_cls(what, _namespace) == ExpectedClass
-
-
-@pytest.mark.parametrize(
- "class_name,expected_what",
- [
- ("Class", "class"),
- ("TestClass", "test-class"),
- ("TestUPPERCASEClass", "test-uppercase-class"),
- ("UPPERCASETestClass", "uppercase-test-class"),
- ("CurrentUserGPGKey", "current-user-gpg-key"),
- ("UserGPGKey", "user-gpg-key"),
- ("LDAPGroup", "ldap-group"),
- ],
-)
-def test_cls_to_what(class_name, expected_what):
- TestClass = type(class_name, (), {})
-
- assert cli.cls_to_what(TestClass) == expected_what
-
-
-def test_die():
- fl = io.StringIO()
- with redirect_stderr(fl):
- with pytest.raises(SystemExit) as test:
- cli.die("foobar")
- assert fl.getvalue() == "foobar\n"
- assert test.value.code == 1
-
-
-def test_parse_value():
- ret = cli._parse_value("foobar")
- assert ret == "foobar"
-
- ret = cli._parse_value(True)
- assert ret is True
-
- ret = cli._parse_value(1)
- assert ret == 1
-
- ret = cli._parse_value(None)
- assert ret is None
-
- fd, temp_path = tempfile.mkstemp()
- os.write(fd, b"content")
- os.close(fd)
- ret = cli._parse_value("@%s" % temp_path)
- assert ret == "content"
- os.unlink(temp_path)
-
- fl = io.StringIO()
- with redirect_stderr(fl):
- with pytest.raises(SystemExit) as exc:
- cli._parse_value("@/thisfileprobablydoesntexist")
- assert (
- fl.getvalue() == "[Errno 2] No such file or directory:"
- " '/thisfileprobablydoesntexist'\n"
- )
- assert exc.value.code == 1
-
-
-def test_base_parser():
- parser = cli._get_base_parser()
- args = parser.parse_args(["-v", "-g", "gl_id", "-c", "foo.cfg", "-c", "bar.cfg"])
- assert args.verbose
- assert args.gitlab == "gl_id"
- assert args.config_file == ["foo.cfg", "bar.cfg"]
-
-
-def test_v4_parse_args():
- parser = cli._get_parser()
- args = parser.parse_args(["project", "list"])
- assert args.what == "project"
- assert args.whaction == "list"
-
-
-def test_v4_parser():
- parser = cli._get_parser()
- subparsers = next(
- action
- for action in parser._actions
- if isinstance(action, argparse._SubParsersAction)
- )
- assert subparsers is not None
- assert "project" in subparsers.choices
-
- user_subparsers = next(
- action
- for action in subparsers.choices["project"]._actions
- if isinstance(action, argparse._SubParsersAction)
- )
- assert user_subparsers is not None
- assert "list" in user_subparsers.choices
- assert "get" in user_subparsers.choices
- assert "delete" in user_subparsers.choices
- assert "update" in user_subparsers.choices
- assert "create" in user_subparsers.choices
- assert "archive" in user_subparsers.choices
- assert "unarchive" in user_subparsers.choices
-
- actions = user_subparsers.choices["create"]._option_string_actions
- assert not actions["--description"].required
-
- user_subparsers = next(
- action
- for action in subparsers.choices["group"]._actions
- if isinstance(action, argparse._SubParsersAction)
- )
- actions = user_subparsers.choices["create"]._option_string_actions
- assert actions["--name"].required
diff --git a/tests/unit/test_config.py b/tests/unit/test_config.py
deleted file mode 100644
index a62106b..0000000
--- a/tests/unit/test_config.py
+++ /dev/null
@@ -1,317 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2016-2017 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-import io
-import os
-from textwrap import dedent
-
-import mock
-import pytest
-
-from gitlab import config, USER_AGENT
-
-custom_user_agent = "my-package/1.0.0"
-
-valid_config = u"""[global]
-default = one
-ssl_verify = true
-timeout = 2
-
-[one]
-url = http://one.url
-private_token = ABCDEF
-
-[two]
-url = https://two.url
-private_token = GHIJKL
-ssl_verify = false
-timeout = 10
-
-[three]
-url = https://three.url
-private_token = MNOPQR
-ssl_verify = /path/to/CA/bundle.crt
-per_page = 50
-
-[four]
-url = https://four.url
-oauth_token = STUV
-"""
-
-custom_user_agent_config = """[global]
-default = one
-user_agent = {}
-
-[one]
-url = http://one.url
-private_token = ABCDEF
-""".format(
- custom_user_agent
-)
-
-no_default_config = u"""[global]
-[there]
-url = http://there.url
-private_token = ABCDEF
-"""
-
-missing_attr_config = u"""[global]
-[one]
-url = http://one.url
-
-[two]
-private_token = ABCDEF
-
-[three]
-meh = hem
-
-[four]
-url = http://four.url
-private_token = ABCDEF
-per_page = 200
-"""
-
-
-def global_retry_transient_errors(value: bool) -> str:
- return u"""[global]
-default = one
-retry_transient_errors={}
-[one]
-url = http://one.url
-private_token = ABCDEF""".format(
- value
- )
-
-
-def global_and_gitlab_retry_transient_errors(
- global_value: bool, gitlab_value: bool
-) -> str:
- return u"""[global]
- default = one
- retry_transient_errors={global_value}
- [one]
- url = http://one.url
- private_token = ABCDEF
- retry_transient_errors={gitlab_value}""".format(
- global_value=global_value, gitlab_value=gitlab_value
- )
-
-
-@mock.patch.dict(os.environ, {"PYTHON_GITLAB_CFG": "/some/path"})
-def test_env_config_present():
- assert ["/some/path"] == config._env_config()
-
-
-@mock.patch.dict(os.environ, {}, clear=True)
-def test_env_config_missing():
- assert [] == config._env_config()
-
-
-@mock.patch("os.path.exists")
-def test_missing_config(path_exists):
- path_exists.return_value = False
- with pytest.raises(config.GitlabConfigMissingError):
- config.GitlabConfigParser("test")
-
-
-@mock.patch("os.path.exists")
-@mock.patch("builtins.open")
-def test_invalid_id(m_open, path_exists):
- fd = io.StringIO(no_default_config)
- fd.close = mock.Mock(return_value=None)
- m_open.return_value = fd
- path_exists.return_value = True
- config.GitlabConfigParser("there")
- with pytest.raises(config.GitlabIDError):
- config.GitlabConfigParser()
-
- fd = io.StringIO(valid_config)
- fd.close = mock.Mock(return_value=None)
- m_open.return_value = fd
- with pytest.raises(config.GitlabDataError):
- config.GitlabConfigParser(gitlab_id="not_there")
-
-
-@mock.patch("os.path.exists")
-@mock.patch("builtins.open")
-def test_invalid_data(m_open, path_exists):
- fd = io.StringIO(missing_attr_config)
- fd.close = mock.Mock(return_value=None, side_effect=lambda: fd.seek(0))
- m_open.return_value = fd
- path_exists.return_value = True
-
- config.GitlabConfigParser("one")
- config.GitlabConfigParser("one")
- with pytest.raises(config.GitlabDataError):
- config.GitlabConfigParser(gitlab_id="two")
- with pytest.raises(config.GitlabDataError):
- config.GitlabConfigParser(gitlab_id="three")
- with pytest.raises(config.GitlabDataError) as emgr:
- config.GitlabConfigParser("four")
- assert "Unsupported per_page number: 200" == emgr.value.args[0]
-
-
-@mock.patch("os.path.exists")
-@mock.patch("builtins.open")
-def test_valid_data(m_open, path_exists):
- fd = io.StringIO(valid_config)
- fd.close = mock.Mock(return_value=None)
- m_open.return_value = fd
- path_exists.return_value = True
-
- cp = config.GitlabConfigParser()
- assert "one" == cp.gitlab_id
- assert "http://one.url" == cp.url
- assert "ABCDEF" == cp.private_token
- assert cp.oauth_token is None
- assert 2 == cp.timeout
- assert cp.ssl_verify is True
- assert cp.per_page is None
-
- fd = io.StringIO(valid_config)
- fd.close = mock.Mock(return_value=None)
- m_open.return_value = fd
- cp = config.GitlabConfigParser(gitlab_id="two")
- assert "two" == cp.gitlab_id
- assert "https://two.url" == cp.url
- assert "GHIJKL" == cp.private_token
- assert cp.oauth_token is None
- assert 10 == cp.timeout
- assert cp.ssl_verify is False
-
- fd = io.StringIO(valid_config)
- fd.close = mock.Mock(return_value=None)
- m_open.return_value = fd
- cp = config.GitlabConfigParser(gitlab_id="three")
- assert "three" == cp.gitlab_id
- assert "https://three.url" == cp.url
- assert "MNOPQR" == cp.private_token
- assert cp.oauth_token is None
- assert 2 == cp.timeout
- assert "/path/to/CA/bundle.crt" == cp.ssl_verify
- assert 50 == cp.per_page
-
- fd = io.StringIO(valid_config)
- fd.close = mock.Mock(return_value=None)
- m_open.return_value = fd
- cp = config.GitlabConfigParser(gitlab_id="four")
- assert "four" == cp.gitlab_id
- assert "https://four.url" == cp.url
- assert cp.private_token is None
- assert "STUV" == cp.oauth_token
- assert 2 == cp.timeout
- assert cp.ssl_verify is True
-
-
-@mock.patch("os.path.exists")
-@mock.patch("builtins.open")
-def test_data_from_helper(m_open, path_exists, tmp_path):
- helper = tmp_path / "helper.sh"
- helper.write_text(
- dedent(
- """\
- #!/bin/sh
- echo "secret"
- """
- )
- )
- helper.chmod(0o755)
-
- fd = io.StringIO(
- dedent(
- """\
- [global]
- default = helper
-
- [helper]
- url = https://helper.url
- oauth_token = helper: %s
- """
- )
- % helper
- )
-
- fd.close = mock.Mock(return_value=None)
- m_open.return_value = fd
- cp = config.GitlabConfigParser(gitlab_id="helper")
- assert "helper" == cp.gitlab_id
- assert "https://helper.url" == cp.url
- assert cp.private_token is None
- assert "secret" == cp.oauth_token
-
-
-@mock.patch("os.path.exists")
-@mock.patch("builtins.open")
-@pytest.mark.parametrize(
- "config_string,expected_agent",
- [
- (valid_config, USER_AGENT),
- (custom_user_agent_config, custom_user_agent),
- ],
-)
-def test_config_user_agent(m_open, path_exists, config_string, expected_agent):
- fd = io.StringIO(config_string)
- fd.close = mock.Mock(return_value=None)
- m_open.return_value = fd
-
- cp = config.GitlabConfigParser()
- assert cp.user_agent == expected_agent
-
-
-@mock.patch("os.path.exists")
-@mock.patch("builtins.open")
-@pytest.mark.parametrize(
- "config_string,expected",
- [
- pytest.param(valid_config, False, id="default_value"),
- pytest.param(
- global_retry_transient_errors(True), True, id="global_config_true"
- ),
- pytest.param(
- global_retry_transient_errors(False), False, id="global_config_false"
- ),
- pytest.param(
- global_and_gitlab_retry_transient_errors(False, True),
- True,
- id="gitlab_overrides_global_true",
- ),
- pytest.param(
- global_and_gitlab_retry_transient_errors(True, False),
- False,
- id="gitlab_overrides_global_false",
- ),
- pytest.param(
- global_and_gitlab_retry_transient_errors(True, True),
- True,
- id="gitlab_equals_global_true",
- ),
- pytest.param(
- global_and_gitlab_retry_transient_errors(False, False),
- False,
- id="gitlab_equals_global_false",
- ),
- ],
-)
-def test_config_retry_transient_errors_when_global_config_is_set(
- m_open, path_exists, config_string, expected
-):
- fd = io.StringIO(config_string)
- fd.close = mock.Mock(return_value=None)
- m_open.return_value = fd
-
- cp = config.GitlabConfigParser()
- assert cp.retry_transient_errors == expected
diff --git a/tests/unit/test_exceptions.py b/tests/unit/test_exceptions.py
deleted file mode 100644
index 57b394b..0000000
--- a/tests/unit/test_exceptions.py
+++ /dev/null
@@ -1,18 +0,0 @@
-import pytest
-
-from gitlab import exceptions
-
-
-def test_error_raises_from_http_error():
- """Methods decorated with @on_http_error should raise from GitlabHttpError."""
-
- class TestError(Exception):
- pass
-
- @exceptions.on_http_error(TestError)
- def raise_error_from_http_error():
- raise exceptions.GitlabHttpError
-
- with pytest.raises(TestError) as context:
- raise_error_from_http_error()
- assert isinstance(context.value.__cause__, exceptions.GitlabHttpError)
diff --git a/tests/unit/test_gitlab.py b/tests/unit/test_gitlab.py
deleted file mode 100644
index 2bd7d4d..0000000
--- a/tests/unit/test_gitlab.py
+++ /dev/null
@@ -1,196 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2014 Mika Mäenpää <mika.j.maenpaa@tut.fi>,
-# Tampere University of Technology
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or`
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-import pickle
-
-import pytest
-from httmock import HTTMock, response, urlmatch, with_httmock # noqa
-
-from gitlab import DEFAULT_URL, Gitlab, GitlabList, USER_AGENT
-from gitlab.v4.objects import CurrentUser
-
-localhost = "http://localhost"
-username = "username"
-user_id = 1
-token = "abc123"
-
-
-@urlmatch(scheme="http", netloc="localhost", path="/api/v4/user", method="get")
-def resp_get_user(url, request):
- headers = {"content-type": "application/json"}
- content = '{{"id": {0:d}, "username": "{1:s}"}}'.format(user_id, username).encode(
- "utf-8"
- )
- return response(200, content, headers, None, 5, request)
-
-
-@urlmatch(scheme="http", netloc="localhost", path="/api/v4/tests", method="get")
-def resp_page_1(url, request):
- headers = {
- "content-type": "application/json",
- "X-Page": 1,
- "X-Next-Page": 2,
- "X-Per-Page": 1,
- "X-Total-Pages": 2,
- "X-Total": 2,
- "Link": ("<http://localhost/api/v4/tests?per_page=1&page=2>;" ' rel="next"'),
- }
- content = '[{"a": "b"}]'
- return response(200, content, headers, None, 5, request)
-
-
-@urlmatch(
- scheme="http",
- netloc="localhost",
- path="/api/v4/tests",
- method="get",
- query=r".*page=2",
-)
-def resp_page_2(url, request):
- headers = {
- "content-type": "application/json",
- "X-Page": 2,
- "X-Next-Page": 2,
- "X-Per-Page": 1,
- "X-Total-Pages": 2,
- "X-Total": 2,
- }
- content = '[{"c": "d"}]'
- return response(200, content, headers, None, 5, request)
-
-
-def test_gitlab_build_list(gl):
- with HTTMock(resp_page_1):
- obj = gl.http_list("/tests", as_list=False)
- assert len(obj) == 2
- assert obj._next_url == "http://localhost/api/v4/tests?per_page=1&page=2"
- assert obj.current_page == 1
- assert obj.prev_page is None
- assert obj.next_page == 2
- assert obj.per_page == 1
- assert obj.total_pages == 2
- assert obj.total == 2
-
- with HTTMock(resp_page_2):
- test_list = list(obj)
- assert len(test_list) == 2
- assert test_list[0]["a"] == "b"
- assert test_list[1]["c"] == "d"
-
-
-@with_httmock(resp_page_1, resp_page_2)
-def test_gitlab_all_omitted_when_as_list(gl):
- result = gl.http_list("/tests", as_list=False, all=True)
- assert isinstance(result, GitlabList)
-
-
-def test_gitlab_strip_base_url(gl_trailing):
- assert gl_trailing.url == "http://localhost"
-
-
-def test_gitlab_strip_api_url(gl_trailing):
- assert gl_trailing.api_url == "http://localhost/api/v4"
-
-
-def test_gitlab_build_url(gl_trailing):
- r = gl_trailing._build_url("/projects")
- assert r == "http://localhost/api/v4/projects"
-
-
-def test_gitlab_pickability(gl):
- original_gl_objects = gl._objects
- pickled = pickle.dumps(gl)
- unpickled = pickle.loads(pickled)
- assert isinstance(unpickled, Gitlab)
- assert hasattr(unpickled, "_objects")
- assert unpickled._objects == original_gl_objects
-
-
-@with_httmock(resp_get_user)
-def test_gitlab_token_auth(gl, callback=None):
- gl.auth()
- assert gl.user.username == username
- assert gl.user.id == user_id
- assert isinstance(gl.user, CurrentUser)
-
-
-def test_gitlab_default_url():
- gl = Gitlab()
- assert gl.url == DEFAULT_URL
-
-
-@pytest.mark.parametrize(
- "args, kwargs, expected_url, expected_private_token, expected_oauth_token",
- [
- ([], {}, DEFAULT_URL, None, None),
- ([None, token], {}, DEFAULT_URL, token, None),
- ([localhost], {}, localhost, None, None),
- ([localhost, token], {}, localhost, token, None),
- ([localhost, None, token], {}, localhost, None, token),
- ([], {"private_token": token}, DEFAULT_URL, token, None),
- ([], {"oauth_token": token}, DEFAULT_URL, None, token),
- ([], {"url": localhost}, localhost, None, None),
- ([], {"url": localhost, "private_token": token}, localhost, token, None),
- ([], {"url": localhost, "oauth_token": token}, localhost, None, token),
- ],
- ids=[
- "no_args",
- "args_private_token",
- "args_url",
- "args_url_private_token",
- "args_url_oauth_token",
- "kwargs_private_token",
- "kwargs_oauth_token",
- "kwargs_url",
- "kwargs_url_private_token",
- "kwargs_url_oauth_token",
- ],
-)
-def test_gitlab_args_kwargs(
- args, kwargs, expected_url, expected_private_token, expected_oauth_token
-):
- gl = Gitlab(*args, **kwargs)
- assert gl.url == expected_url
- assert gl.private_token == expected_private_token
- assert gl.oauth_token == expected_oauth_token
-
-
-def test_gitlab_from_config(default_config):
- config_path = default_config
- Gitlab.from_config("one", [config_path])
-
-
-def test_gitlab_subclass_from_config(default_config):
- class MyGitlab(Gitlab):
- pass
-
- config_path = default_config
- gl = MyGitlab.from_config("one", [config_path])
- assert isinstance(gl, MyGitlab)
-
-
-@pytest.mark.parametrize(
- "kwargs,expected_agent",
- [
- ({}, USER_AGENT),
- ({"user_agent": "my-package/1.0.0"}, "my-package/1.0.0"),
- ],
-)
-def test_gitlab_user_agent(kwargs, expected_agent):
- gl = Gitlab("http://localhost", **kwargs)
- assert gl.headers["User-Agent"] == expected_agent
diff --git a/tests/unit/test_gitlab_auth.py b/tests/unit/test_gitlab_auth.py
deleted file mode 100644
index 314fbed..0000000
--- a/tests/unit/test_gitlab_auth.py
+++ /dev/null
@@ -1,85 +0,0 @@
-import pytest
-import requests
-
-from gitlab import Gitlab
-
-
-def test_invalid_auth_args():
- with pytest.raises(ValueError):
- Gitlab(
- "http://localhost",
- api_version="4",
- private_token="private_token",
- oauth_token="bearer",
- )
- with pytest.raises(ValueError):
- Gitlab(
- "http://localhost",
- api_version="4",
- oauth_token="bearer",
- http_username="foo",
- http_password="bar",
- )
- with pytest.raises(ValueError):
- Gitlab(
- "http://localhost",
- api_version="4",
- private_token="private_token",
- http_password="bar",
- )
- with pytest.raises(ValueError):
- Gitlab(
- "http://localhost",
- api_version="4",
- private_token="private_token",
- http_username="foo",
- )
-
-
-def test_private_token_auth():
- gl = Gitlab("http://localhost", private_token="private_token", api_version="4")
- assert gl.private_token == "private_token"
- assert gl.oauth_token is None
- assert gl.job_token is None
- assert gl._http_auth is None
- assert "Authorization" not in gl.headers
- assert gl.headers["PRIVATE-TOKEN"] == "private_token"
- assert "JOB-TOKEN" not in gl.headers
-
-
-def test_oauth_token_auth():
- gl = Gitlab("http://localhost", oauth_token="oauth_token", api_version="4")
- assert gl.private_token is None
- assert gl.oauth_token == "oauth_token"
- assert gl.job_token is None
- assert gl._http_auth is None
- assert gl.headers["Authorization"] == "Bearer oauth_token"
- assert "PRIVATE-TOKEN" not in gl.headers
- assert "JOB-TOKEN" not in gl.headers
-
-
-def test_job_token_auth():
- gl = Gitlab("http://localhost", job_token="CI_JOB_TOKEN", api_version="4")
- assert gl.private_token is None
- assert gl.oauth_token is None
- assert gl.job_token == "CI_JOB_TOKEN"
- assert gl._http_auth is None
- assert "Authorization" not in gl.headers
- assert "PRIVATE-TOKEN" not in gl.headers
- assert gl.headers["JOB-TOKEN"] == "CI_JOB_TOKEN"
-
-
-def test_http_auth():
- gl = Gitlab(
- "http://localhost",
- private_token="private_token",
- http_username="foo",
- http_password="bar",
- api_version="4",
- )
- assert gl.private_token == "private_token"
- assert gl.oauth_token is None
- assert gl.job_token is None
- assert isinstance(gl._http_auth, requests.auth.HTTPBasicAuth)
- assert gl.headers["PRIVATE-TOKEN"] == "private_token"
- assert "Authorization" not in gl.headers
diff --git a/tests/unit/test_gitlab_http_methods.py b/tests/unit/test_gitlab_http_methods.py
deleted file mode 100644
index ba57c31..0000000
--- a/tests/unit/test_gitlab_http_methods.py
+++ /dev/null
@@ -1,406 +0,0 @@
-import pytest
-import requests
-from httmock import HTTMock, response, urlmatch
-
-from gitlab import GitlabHttpError, GitlabList, GitlabParsingError, RedirectError
-
-
-def test_build_url(gl):
- r = gl._build_url("http://localhost/api/v4")
- assert r == "http://localhost/api/v4"
- r = gl._build_url("https://localhost/api/v4")
- assert r == "https://localhost/api/v4"
- r = gl._build_url("/projects")
- assert r == "http://localhost/api/v4/projects"
-
-
-def test_http_request(gl):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects", method="get")
- def resp_cont(url, request):
- headers = {"content-type": "application/json"}
- content = '[{"name": "project1"}]'
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- http_r = gl.http_request("get", "/projects")
- http_r.json()
- assert http_r.status_code == 200
-
-
-def test_http_request_404(gl):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/not_there", method="get")
- def resp_cont(url, request):
- content = {"Here is why it failed"}
- return response(404, content, {}, None, 5, request)
-
- with HTTMock(resp_cont):
- with pytest.raises(GitlabHttpError):
- gl.http_request("get", "/not_there")
-
-
-@pytest.mark.parametrize("status_code", [500, 502, 503, 504])
-def test_http_request_with_only_failures(gl, status_code):
- call_count = 0
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects", method="get")
- def resp_cont(url, request):
- nonlocal call_count
- call_count += 1
- return response(status_code, {"Here is why it failed"}, {}, None, 5, request)
-
- with HTTMock(resp_cont):
- with pytest.raises(GitlabHttpError):
- gl.http_request("get", "/projects")
-
- assert call_count == 1
-
-
-def test_http_request_with_retry_on_method_for_transient_failures(gl):
- call_count = 0
- calls_before_success = 3
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects", method="get")
- def resp_cont(url, request):
- nonlocal call_count
- call_count += 1
- status_code = 200 if call_count == calls_before_success else 500
- return response(
- status_code,
- {"Failure is the stepping stone to success"},
- {},
- None,
- 5,
- request,
- )
-
- with HTTMock(resp_cont):
- http_r = gl.http_request("get", "/projects", retry_transient_errors=True)
-
- assert http_r.status_code == 200
- assert call_count == calls_before_success
-
-
-def test_http_request_with_retry_on_class_for_transient_failures(gl_retry):
- call_count = 0
- calls_before_success = 3
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects", method="get")
- def resp_cont(url, request):
- nonlocal call_count
- call_count += 1
- status_code = 200 if call_count == calls_before_success else 500
- return response(
- status_code,
- {"Failure is the stepping stone to success"},
- {},
- None,
- 5,
- request,
- )
-
- with HTTMock(resp_cont):
- http_r = gl_retry.http_request("get", "/projects")
-
- assert http_r.status_code == 200
- assert call_count == calls_before_success
-
-
-def test_http_request_with_retry_on_class_and_method_for_transient_failures(gl_retry):
- call_count = 0
- calls_before_success = 3
-
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects", method="get")
- def resp_cont(url, request):
- nonlocal call_count
- call_count += 1
- status_code = 200 if call_count == calls_before_success else 500
- return response(status_code, {"Here is why it failed"}, {}, None, 5, request)
-
- with HTTMock(resp_cont):
- with pytest.raises(GitlabHttpError):
- gl_retry.http_request("get", "/projects", retry_transient_errors=False)
-
- assert call_count == 1
-
-
-def create_redirect_response(
- *, request: requests.models.PreparedRequest, http_method: str, api_path: str
-) -> requests.models.Response:
- """Create a Requests response object that has a redirect in it"""
-
- assert api_path.startswith("/")
- http_method = http_method.upper()
-
- # Create a history which contains our original request which is redirected
- history = [
- response(
- status_code=302,
- content="",
- headers={"Location": f"http://example.com/api/v4{api_path}"},
- reason="Moved Temporarily",
- request=request,
- )
- ]
-
- # Create a "prepped" Request object to be the final redirect. The redirect
- # will be a "GET" method as Requests changes the method to "GET" when there
- # is a 301/302 redirect code.
- req = requests.Request(
- method="GET",
- url=f"http://example.com/api/v4{api_path}",
- )
- prepped = req.prepare()
-
- resp_obj = response(
- status_code=200,
- content="",
- headers={},
- reason="OK",
- elapsed=5,
- request=prepped,
- )
- resp_obj.history = history
- return resp_obj
-
-
-def test_http_request_302_get_does_not_raise(gl):
- """Test to show that a redirect of a GET will not cause an error"""
-
- method = "get"
- api_path = "/user/status"
-
- @urlmatch(
- scheme="http", netloc="localhost", path=f"/api/v4{api_path}", method=method
- )
- def resp_cont(
- url: str, request: requests.models.PreparedRequest
- ) -> requests.models.Response:
- resp_obj = create_redirect_response(
- request=request, http_method=method, api_path=api_path
- )
- return resp_obj
-
- with HTTMock(resp_cont):
- gl.http_request(verb=method, path=api_path)
-
-
-def test_http_request_302_put_raises_redirect_error(gl):
- """Test to show that a redirect of a PUT will cause an error"""
-
- method = "put"
- api_path = "/user/status"
-
- @urlmatch(
- scheme="http", netloc="localhost", path=f"/api/v4{api_path}", method=method
- )
- def resp_cont(
- url: str, request: requests.models.PreparedRequest
- ) -> requests.models.Response:
- resp_obj = create_redirect_response(
- request=request, http_method=method, api_path=api_path
- )
- return resp_obj
-
- with HTTMock(resp_cont):
- with pytest.raises(RedirectError) as exc:
- gl.http_request(verb=method, path=api_path)
- error_message = exc.value.error_message
- assert "Moved Temporarily" in error_message
- assert "http://localhost/api/v4/user/status" in error_message
- assert "http://example.com/api/v4/user/status" in error_message
-
-
-def test_get_request(gl):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects", method="get")
- def resp_cont(url: str, request: requests.models.PreparedRequest):
- headers = {"content-type": "application/json"}
- content = '{"name": "project1"}'
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- result = gl.http_get("/projects")
- assert isinstance(result, dict)
- assert result["name"] == "project1"
-
-
-def test_get_request_raw(gl):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects", method="get")
- def resp_cont(url, request):
- headers = {"content-type": "application/octet-stream"}
- content = "content"
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- result = gl.http_get("/projects")
- assert result.content.decode("utf-8") == "content"
-
-
-def test_get_request_404(gl):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/not_there", method="get")
- def resp_cont(url, request):
- content = {"Here is why it failed"}
- return response(404, content, {}, None, 5, request)
-
- with HTTMock(resp_cont):
- with pytest.raises(GitlabHttpError):
- gl.http_get("/not_there")
-
-
-def test_get_request_invalid_data(gl):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects", method="get")
- def resp_cont(url, request):
- headers = {"content-type": "application/json"}
- content = '["name": "project1"]'
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- with pytest.raises(GitlabParsingError):
- gl.http_get("/projects")
-
-
-def test_list_request(gl):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects", method="get")
- def resp_cont(url, request):
- headers = {"content-type": "application/json", "X-Total": 1}
- content = '[{"name": "project1"}]'
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- result = gl.http_list("/projects", as_list=True)
- assert isinstance(result, list)
- assert len(result) == 1
-
- with HTTMock(resp_cont):
- result = gl.http_list("/projects", as_list=False)
- assert isinstance(result, GitlabList)
- assert len(result) == 1
-
- with HTTMock(resp_cont):
- result = gl.http_list("/projects", all=True)
- assert isinstance(result, list)
- assert len(result) == 1
-
-
-def test_list_request_404(gl):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/not_there", method="get")
- def resp_cont(url, request):
- content = {"Here is why it failed"}
- return response(404, content, {}, None, 5, request)
-
- with HTTMock(resp_cont):
- with pytest.raises(GitlabHttpError):
- gl.http_list("/not_there")
-
-
-def test_list_request_invalid_data(gl):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects", method="get")
- def resp_cont(url, request):
- headers = {"content-type": "application/json"}
- content = '["name": "project1"]'
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- with pytest.raises(GitlabParsingError):
- gl.http_list("/projects")
-
-
-def test_post_request(gl):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects", method="post")
- def resp_cont(url, request):
- headers = {"content-type": "application/json"}
- content = '{"name": "project1"}'
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- result = gl.http_post("/projects")
- assert isinstance(result, dict)
- assert result["name"] == "project1"
-
-
-def test_post_request_404(gl):
- @urlmatch(
- scheme="http", netloc="localhost", path="/api/v4/not_there", method="post"
- )
- def resp_cont(url, request):
- content = {"Here is why it failed"}
- return response(404, content, {}, None, 5, request)
-
- with HTTMock(resp_cont):
- with pytest.raises(GitlabHttpError):
- gl.http_post("/not_there")
-
-
-def test_post_request_invalid_data(gl):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects", method="post")
- def resp_cont(url, request):
- headers = {"content-type": "application/json"}
- content = '["name": "project1"]'
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- with pytest.raises(GitlabParsingError):
- gl.http_post("/projects")
-
-
-def test_put_request(gl):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects", method="put")
- def resp_cont(url, request):
- headers = {"content-type": "application/json"}
- content = '{"name": "project1"}'
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- result = gl.http_put("/projects")
- assert isinstance(result, dict)
- assert result["name"] == "project1"
-
-
-def test_put_request_404(gl):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/not_there", method="put")
- def resp_cont(url, request):
- content = {"Here is why it failed"}
- return response(404, content, {}, None, 5, request)
-
- with HTTMock(resp_cont):
- with pytest.raises(GitlabHttpError):
- gl.http_put("/not_there")
-
-
-def test_put_request_invalid_data(gl):
- @urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects", method="put")
- def resp_cont(url, request):
- headers = {"content-type": "application/json"}
- content = '["name": "project1"]'
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- with pytest.raises(GitlabParsingError):
- gl.http_put("/projects")
-
-
-def test_delete_request(gl):
- @urlmatch(
- scheme="http", netloc="localhost", path="/api/v4/projects", method="delete"
- )
- def resp_cont(url, request):
- headers = {"content-type": "application/json"}
- content = "true"
- return response(200, content, headers, None, 5, request)
-
- with HTTMock(resp_cont):
- result = gl.http_delete("/projects")
- assert isinstance(result, requests.Response)
- assert result.json() is True
-
-
-def test_delete_request_404(gl):
- @urlmatch(
- scheme="http", netloc="localhost", path="/api/v4/not_there", method="delete"
- )
- def resp_cont(url, request):
- content = {"Here is why it failed"}
- return response(404, content, {}, None, 5, request)
-
- with HTTMock(resp_cont):
- with pytest.raises(GitlabHttpError):
- gl.http_delete("/not_there")
diff --git a/tests/unit/test_types.py b/tests/unit/test_types.py
deleted file mode 100644
index a2e5ff5..0000000
--- a/tests/unit/test_types.py
+++ /dev/null
@@ -1,74 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2018 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-from gitlab import types
-
-
-def test_gitlab_attribute_get():
- o = types.GitlabAttribute("whatever")
- assert o.get() == "whatever"
-
- o.set_from_cli("whatever2")
- assert o.get() == "whatever2"
- assert o.get_for_api() == "whatever2"
-
- o = types.GitlabAttribute()
- assert o._value is None
-
-
-def test_list_attribute_input():
- o = types.ListAttribute()
- o.set_from_cli("foo,bar,baz")
- assert o.get() == ["foo", "bar", "baz"]
-
- o.set_from_cli("foo")
- assert o.get() == ["foo"]
-
-
-def test_list_attribute_empty_input():
- o = types.ListAttribute()
- o.set_from_cli("")
- assert o.get() == []
-
- o.set_from_cli(" ")
- assert o.get() == []
-
-
-def test_list_attribute_get_for_api_from_cli():
- o = types.ListAttribute()
- o.set_from_cli("foo,bar,baz")
- assert o.get_for_api() == "foo,bar,baz"
-
-
-def test_list_attribute_get_for_api_from_list():
- o = types.ListAttribute(["foo", "bar", "baz"])
- assert o.get_for_api() == "foo,bar,baz"
-
-
-def test_list_attribute_get_for_api_from_int_list():
- o = types.ListAttribute([1, 9, 7])
- assert o.get_for_api() == "1,9,7"
-
-
-def test_list_attribute_does_not_split_string():
- o = types.ListAttribute("foo")
- assert o.get_for_api() == "foo"
-
-
-def test_lowercase_string_attribute_get_for_api():
- o = types.LowercaseStringAttribute("FOO")
- assert o.get_for_api() == "foo"
diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py
deleted file mode 100644
index dbe0838..0000000
--- a/tests/unit/test_utils.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2019 Gauvain Pocentek <gauvain@pocentek.net>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-from gitlab import utils
-
-
-def test_clean_str_id():
- src = "nothing_special"
- dest = "nothing_special"
- assert dest == utils.clean_str_id(src)
-
- src = "foo#bar/baz/"
- dest = "foo%23bar%2Fbaz%2F"
- assert dest == utils.clean_str_id(src)
-
- src = "foo%bar/baz/"
- dest = "foo%25bar%2Fbaz%2F"
- assert dest == utils.clean_str_id(src)
-
-
-def test_sanitized_url():
- src = "http://localhost/foo/bar"
- dest = "http://localhost/foo/bar"
- assert dest == utils.sanitized_url(src)
-
- src = "http://localhost/foo.bar.baz"
- dest = "http://localhost/foo%2Ebar%2Ebaz"
- assert dest == utils.sanitized_url(src)
diff --git a/tox.ini b/tox.ini
deleted file mode 100644
index da1f1e8..0000000
--- a/tox.ini
+++ /dev/null
@@ -1,102 +0,0 @@
-[tox]
-minversion = 1.6
-skipsdist = True
-envlist = py310,py39,py38,py37,py36,pep8,black,twine-check,mypy,isort
-
-[testenv]
-passenv = GITLAB_IMAGE GITLAB_TAG
-setenv = VIRTUAL_ENV={envdir}
-whitelist_externals = true
-usedevelop = True
-install_command = pip install {opts} {packages}
-
-deps = -r{toxinidir}/requirements.txt
- -r{toxinidir}/requirements-test.txt
-commands =
- pytest tests/unit {posargs}
-
-[testenv:pep8]
-basepython = python3
-envdir={toxworkdir}/lint
-deps = -r{toxinidir}/requirements-lint.txt
-commands =
- flake8 {posargs} .
-
-[testenv:black]
-basepython = python3
-envdir={toxworkdir}/lint
-deps = -r{toxinidir}/requirements-lint.txt
-commands =
- black {posargs} .
-
-[testenv:isort]
-basepython = python3
-envdir={toxworkdir}/lint
-deps = -r{toxinidir}/requirements-lint.txt
-commands =
- isort {posargs} {toxinidir}
-
-[testenv:mypy]
-basepython = python3
-envdir={toxworkdir}/lint
-deps = -r{toxinidir}/requirements-lint.txt
-commands =
- mypy {posargs}
-
-[testenv:twine-check]
-basepython = python3
-deps = -r{toxinidir}/requirements.txt
- twine
-commands =
- python3 setup.py sdist bdist_wheel
- twine check dist/*
-
-[testenv:venv]
-commands = {posargs}
-
-[flake8]
-exclude = .git,.venv,.tox,dist,doc,*egg,build,
-max-line-length = 88
-# We ignore the following because we use black to handle code-formatting
-# E203: Whitespace before ':'
-# E501: Line too long
-# W503: Line break occurred before a binary operator
-ignore = E203,E501,W503
-per-file-ignores =
- gitlab/v4/objects/__init__.py:F401,F403
-
-[testenv:docs]
-deps = -r{toxinidir}/requirements-docs.txt
-commands = python setup.py build_sphinx
-
-[testenv:cover]
-commands =
- pytest --cov --cov-report term --cov-report html \
- --cov-report xml tests/unit {posargs}
-
-[coverage:run]
-omit = *tests*
-source = gitlab
-
-[coverage:report]
-exclude_lines =
- pragma: no cover
- if TYPE_CHECKING:
- if debug:
-
-[pytest]
-script_launch_mode = subprocess
-
-[testenv:cli_func_v4]
-deps = -r{toxinidir}/requirements-docker.txt
-commands =
- pytest --cov --cov-report xml tests/functional/cli {posargs}
-
-[testenv:py_func_v4]
-deps = -r{toxinidir}/requirements-docker.txt
-commands =
- pytest --cov --cov-report xml tests/functional/api {posargs}
-
-[testenv:smoke]
-deps = -r{toxinidir}/requirements-test.txt
-commands = pytest tests/smoke {posargs}