diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 90f7d3fd5..899f8fabf 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -22,9 +22,9 @@ jobs: sphinx: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v6.0.1 + - uses: actions/checkout@v6.0.2 - name: Set up Python - uses: actions/setup-python@v6.1.0 + uses: actions/setup-python@v6.2.0 with: python-version: "3.14" - name: Install dependencies @@ -37,9 +37,9 @@ jobs: twine-check: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v6.0.1 + - uses: actions/checkout@v6.0.2 - name: Set up Python - uses: actions/setup-python@v6.1.0 + uses: actions/setup-python@v6.2.0 with: python-version: "3.14" - name: Install dependencies diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 01bee8a33..7a0dd38dc 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -22,10 +22,10 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6.0.1 + - uses: actions/checkout@v6.0.2 with: fetch-depth: 0 - - uses: actions/setup-python@v6.1.0 + - uses: actions/setup-python@v6.2.0 with: python-version: "3.14" - run: pip install --upgrade tox diff --git a/.github/workflows/pre_commit.yml b/.github/workflows/pre_commit.yml index 4be228d66..993606750 100644 --- a/.github/workflows/pre_commit.yml +++ b/.github/workflows/pre_commit.yml @@ -29,8 +29,8 @@ jobs: pre_commit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6.0.1 - - uses: actions/setup-python@v6.1.0 + - uses: actions/checkout@v6.0.2 + - uses: actions/setup-python@v6.2.0 with: python-version: "3.14" - name: install tox diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ff04716dd..01829b244 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ jobs: id-token: write environment: pypi.org steps: - - uses: actions/checkout@v6.0.1 + - uses: actions/checkout@v6.0.2 with: fetch-depth: 0 token: ${{ secrets.RELEASE_GITHUB_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0f2af115e..397559d31 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -46,9 +46,9 @@ jobs: version: "3.14" toxenv: py314,smoke steps: - - uses: actions/checkout@v6.0.1 + - uses: actions/checkout@v6.0.2 - name: Set up Python ${{ matrix.python.version }} - uses: actions/setup-python@v6.1.0 + uses: actions/setup-python@v6.2.0 with: python-version: ${{ matrix.python.version }} - name: Install dependencies @@ -65,9 +65,9 @@ jobs: matrix: toxenv: [api_func_v4, cli_func_v4] steps: - - uses: actions/checkout@v6.0.1 + - uses: actions/checkout@v6.0.2 - name: Set up Python - uses: actions/setup-python@v6.1.0 + uses: actions/setup-python@v6.2.0 with: python-version: "3.14" - name: Install dependencies @@ -87,9 +87,9 @@ jobs: coverage: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v6.0.1 + - uses: actions/checkout@v6.0.2 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v6.1.0 + uses: actions/setup-python@v6.2.0 with: python-version: "3.14" - name: Install dependencies @@ -111,8 +111,8 @@ jobs: runs-on: ubuntu-latest name: Python wheel steps: - - uses: actions/checkout@v6.0.1 - - uses: actions/setup-python@v6.1.0 + - uses: actions/checkout@v6.0.2 + - uses: actions/setup-python@v6.2.0 with: python-version: "3.14" - name: Install dependencies @@ -129,9 +129,9 @@ jobs: runs-on: ubuntu-latest needs: [dist] steps: - - uses: actions/checkout@v6.0.1 + - uses: actions/checkout@v6.0.2 - name: Set up Python - uses: actions/setup-python@v6.1.0 + uses: actions/setup-python@v6.2.0 with: python-version: '3.14' - uses: actions/download-artifact@v7.0.0 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3b93e8655..1db179a02 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,11 +3,11 @@ default_language_version: repos: - repo: https://github.com/psf/black - rev: 25.12.0 + rev: 26.1.0 hooks: - id: black - repo: https://github.com/commitizen-tools/commitizen - rev: v4.10.1 + rev: v4.12.1 hooks: - id: commitizen stages: [commit-msg] @@ -25,7 +25,7 @@ repos: - id: pylint additional_dependencies: - argcomplete==2.0.0 - - gql==3.5.0 + - gql==4.0.0 - httpx==0.27.2 - pytest==7.4.2 - requests==2.28.1 @@ -37,7 +37,7 @@ repos: - id: mypy args: [] additional_dependencies: - - gql==3.5.0 + - gql==4.0.0 - httpx==0.27.2 - jinja2==3.1.2 - pytest==7.4.2 @@ -51,6 +51,6 @@ repos: - id: rst-directive-colons - id: rst-inline-touching-normal - repo: https://github.com/maxbrunet/pre-commit-renovate - rev: 42.64.1 + rev: 42.84.0 hooks: - id: renovate-config-validator diff --git a/CHANGELOG.md b/CHANGELOG.md index ef85c5019..dca7acc04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,34 @@ All versions below are listed in reverse chronological order. +## v8.0.0 (2026-01-28) + +### Bug Fixes + +- Actually define project repr_attr + ([`4187a69`](https://github.com/python-gitlab/python-gitlab/commit/4187a69420dd7b2e60c2d833ab246aec745d35cb)) + +- File save start_branch as a body attribute + ([`1001d93`](https://github.com/python-gitlab/python-gitlab/commit/1001d934e8c36cc3b14408b46b41030bf705a294)) + +### Chores + +- **black**: Run black v26 on code + ([`4a8d82b`](https://github.com/python-gitlab/python-gitlab/commit/4a8d82bec8f09fa142e8134589a0f40ef4f9c3be)) + +- **precommit**: Update dependency black to v26 + ([`ad43b76`](https://github.com/python-gitlab/python-gitlab/commit/ad43b763acdcd8d7db832972921fb071ea0a826f)) + +### Features + +- **graphql**: Update to gql 4.0.0 + ([`6f0da67`](https://github.com/python-gitlab/python-gitlab/commit/6f0da671b4586b23232ae89d57622254fa8a7945)) + +### Breaking Changes + +- **graphql**: GraphQL.execute() no longer accepts graphql.Source + + ## v7.1.0 (2025-12-28) ### Bug Fixes diff --git a/gitlab/_version.py b/gitlab/_version.py index 8305f3a6c..cf0655911 100644 --- a/gitlab/_version.py +++ b/gitlab/_version.py @@ -3,4 +3,4 @@ __email__ = "gauvainpocentek@gmail.com" __license__ = "LGPL3" __title__ = "python-gitlab" -__version__ = "7.1.0" +__version__ = "8.0.0" diff --git a/gitlab/cli.py b/gitlab/cli.py index ca4734190..c804911a1 100644 --- a/gitlab/cli.py +++ b/gitlab/cli.py @@ -334,7 +334,7 @@ def main() -> None: # 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) + options, _ = parser.parse_known_args(sys.argv) try: config = gitlab.config.GitlabConfigParser(options.gitlab, options.config_file) except gitlab.config.ConfigError as e: diff --git a/gitlab/client.py b/gitlab/client.py index 37dd4c2e6..a3cf1f31a 100644 --- a/gitlab/client.py +++ b/gitlab/client.py @@ -18,7 +18,6 @@ try: import gql import gql.transport.exceptions - import graphql import httpx from ._backends.graphql import GitlabAsyncTransport, GitlabTransport @@ -1350,7 +1349,7 @@ def __enter__(self) -> GraphQL: def __exit__(self, *args: Any) -> None: self._http_client.close() - def execute(self, request: str | graphql.Source, *args: Any, **kwargs: Any) -> Any: + def execute(self, request: str, *args: Any, **kwargs: Any) -> Any: parsed_document = self._gql(request) retry = utils.Retry( max_retries=self._max_retries, @@ -1420,9 +1419,7 @@ async def __aenter__(self) -> AsyncGraphQL: async def __aexit__(self, *args: Any) -> None: await self._http_client.aclose() - async def execute( - self, request: str | graphql.Source, *args: Any, **kwargs: Any - ) -> Any: + async def execute(self, request: str, *args: Any, **kwargs: Any) -> Any: parsed_document = self._gql(request) retry = utils.Retry( max_retries=self._max_retries, diff --git a/gitlab/v4/objects/files.py b/gitlab/v4/objects/files.py index 757d16eeb..3bcf931a2 100644 --- a/gitlab/v4/objects/files.py +++ b/gitlab/v4/objects/files.py @@ -29,6 +29,7 @@ class ProjectFile(SaveMixin, ObjectDeleteMixin, RESTObject): file_path: str manager: ProjectFileManager content: str # since the `decode()` method uses `self.content` + start_branch: str | None = None def decode(self) -> bytes: """Returns the decoded content of the file. @@ -41,7 +42,11 @@ def decode(self) -> bytes: # NOTE(jlvillal): Signature doesn't match SaveMixin.save() so ignore # type error def save( # type: ignore[override] - self, branch: str, commit_message: str, **kwargs: Any + self, + branch: str, + commit_message: str, + start_branch: str | None = None, + **kwargs: Any, ) -> None: """Save the changes made to the file to the server. @@ -50,6 +55,7 @@ def save( # type: ignore[override] Args: branch: Branch in which the file will be updated commit_message: Message to send with the commit + start_branch: Name of the branch to start the new branch from **kwargs: Extra options to send to the server (e.g. sudo) Raises: @@ -58,6 +64,7 @@ def save( # type: ignore[override] """ self.branch = branch self.commit_message = commit_message + self.start_branch = start_branch self.file_path = utils.EncodedId(self.file_path) super().save(**kwargs) diff --git a/gitlab/v4/objects/projects.py b/gitlab/v4/objects/projects.py index 035b9b861..751ac4c1f 100644 --- a/gitlab/v4/objects/projects.py +++ b/gitlab/v4/objects/projects.py @@ -178,6 +178,8 @@ class Project( _repr_attr = "path_with_namespace" _upload_path = "/projects/{id}/uploads" + path_with_namespace: str + access_tokens: ProjectAccessTokenManager accessrequests: ProjectAccessRequestManager additionalstatistics: ProjectAdditionalStatisticsManager diff --git a/pyproject.toml b/pyproject.toml index 7b8510b94..45e8c36f8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,7 +43,7 @@ dynamic = ["version"] [project.optional-dependencies] autocompletion = ["argcomplete>=1.10.0,<3"] yaml = ["PyYaml>=6.0.1"] -graphql = ["gql[httpx]>=3.5.0,<4"] +graphql = ["gql[httpx]>=3.5.0,<5"] [project.scripts] gitlab = "gitlab.cli:main" diff --git a/requirements-docs.txt b/requirements-docs.txt index 8d756d4f1..1c445092a 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -1,7 +1,7 @@ -r requirements.txt furo==2025.12.19 jinja2==3.1.6 -myst-parser==4.0.1 -sphinx==8.2.3 +myst-parser==5.0.0 +sphinx==9.1.0 sphinxcontrib-autoprogram==0.1.9 sphinx-autobuild==2025.8.25 diff --git a/requirements-lint.txt b/requirements-lint.txt index 5006460d1..dc670d57e 100644 --- a/requirements-lint.txt +++ b/requirements-lint.txt @@ -1,7 +1,7 @@ -r requirements.txt argcomplete==2.0.0 -black==25.12.0 -commitizen==4.10.1 +black==26.1.0 +commitizen==4.12.1 flake8==7.3.0 isort==7.0.0 mypy==1.19.1 @@ -10,5 +10,5 @@ pytest==9.0.2 responses==0.25.8 respx==0.22.0 types-PyYAML==6.0.12.20250915 -types-requests==2.32.4.20250913 -types-setuptools==80.9.0.20251221 +types-requests==2.32.4.20260107 +types-setuptools==80.10.0.20260124 diff --git a/requirements-test.txt b/requirements-test.txt index b06434452..875329563 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,7 +1,7 @@ -r requirements.txt -anyio==4.12.0 -build==1.3.0 -coverage==7.13.0 +anyio==4.12.1 +build==1.4.0 +coverage==7.13.2 pytest-console-scripts==1.4.1 pytest-cov==7.0.0 pytest-github-actions-annotate-failures==0.3.0 @@ -10,4 +10,4 @@ PyYaml==6.0.3 responses==0.25.8 respx==0.22.0 trio==0.32.0 -wheel==0.45.1 +wheel==0.46.3 diff --git a/requirements.txt b/requirements.txt index 6930e5d2c..d51e14533 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -gql==3.5.3 +gql==4.0.0 httpx==0.28.1 requests==2.32.5 requests-toolbelt==1.0.0 diff --git a/tests/functional/cli/test_cli_artifacts.py b/tests/functional/cli/test_cli_artifacts.py index 589486844..9fe4d01ef 100644 --- a/tests/functional/cli/test_cli_artifacts.py +++ b/tests/functional/cli/test_cli_artifacts.py @@ -7,14 +7,12 @@ import pytest -content = textwrap.dedent( - """\ +content = textwrap.dedent("""\ test-artifact: script: echo "test" > artifact.txt artifacts: untracked: true - """ -) + """) data = { "file_path": ".gitlab-ci.yml", "branch": "main", diff --git a/tests/functional/fixtures/.env b/tests/functional/fixtures/.env index f33c35752..c51423c9d 100644 --- a/tests/functional/fixtures/.env +++ b/tests/functional/fixtures/.env @@ -1,4 +1,4 @@ GITLAB_IMAGE=gitlab/gitlab-ee -GITLAB_TAG=18.7.0-ee.0 +GITLAB_TAG=18.8.2-ee.0 GITLAB_RUNNER_IMAGE=gitlab/gitlab-runner GITLAB_RUNNER_TAG=96856197 diff --git a/tests/unit/test_config.py b/tests/unit/test_config.py index 32b9c9ef9..b8470f22b 100644 --- a/tests/unit/test_config.py +++ b/tests/unit/test_config.py @@ -268,28 +268,22 @@ def test_ssl_verify_as_str(m_open, monkeypatch): def test_data_from_helper(m_open, monkeypatch, tmp_path): helper = tmp_path / "helper.sh" helper.write_text( - dedent( - """\ + dedent("""\ #!/bin/sh echo "secret" - """ - ), + """), encoding="utf-8", ) helper.chmod(0o755) - fd = io.StringIO( - dedent( - f"""\ + fd = io.StringIO(dedent(f"""\ [global] default = helper [helper] url = https://helper.url oauth_token = helper: {helper} - """ - ) - ) + """)) fd.close = mock.Mock(return_value=None) m_open.return_value = fd @@ -306,18 +300,14 @@ def test_data_from_helper(m_open, monkeypatch, tmp_path): @pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows") def test_from_helper_subprocess_error_raises_error(m_open, monkeypatch): # using false here to force a non-zero return code - fd = io.StringIO( - dedent( - """\ + fd = io.StringIO(dedent("""\ [global] default = helper [helper] url = https://helper.url oauth_token = helper: false - """ - ) - ) + """)) fd.close = mock.Mock(return_value=None) m_open.return_value = fd