Loading gitlab_butler/butler.py +23 −7 Original line number Diff line number Diff line Loading @@ -9,7 +9,7 @@ import yaml from gitlab import ( Gitlab, GitlabDeleteError, GitlabGetError, GitlabGetError, GitlabHttpError, ) from gitlab.v4.objects import Group, Project, ProjectFile, ProjectPipeline Loading Loading @@ -101,7 +101,7 @@ class Butler: file_path=file_name, ref=project.default_branch ) break except GitlabGetError as ge: except (GitlabHttpError, GitlabGetError) as ge: if ge.response_code != 404: self.handle_error(ge) Loading Loading @@ -208,6 +208,26 @@ class Butler: raise ValueError(f'Unexpected pipeline source "{source.source_type}"') def cleaning_enabled_project(self, project: Project) -> bool: """ Check if project should be cleaned. Projects can be excluded or be in read-only mode. :param project: :return: True if project should be cleaned, False otherwise """ if self.is_excluded(project.path_with_namespace): print( f" - 🏠 Project {AnsiColors.BLUE}{project.path_with_namespace}{AnsiColors.RESET} matches excludes: {AnsiColors.HGRAY}skip{AnsiColors.RESET}" ) return False if project.archived: print( f" - 🏠 Project {AnsiColors.BLUE}{project.path_with_namespace}{AnsiColors.RESET} is archived: {AnsiColors.HGRAY}skip{AnsiColors.RESET}" ) return False return True def find_pipeline_source_status(self, project: Project, pipeline: ProjectPipeline, caches: dict) -> PipelineSource | None: """ Check if the source of the pipeline still exists Loading Loading @@ -397,11 +417,7 @@ class Butler: print(f"- clean {len(subprojects)} sub projects...") for project in subprojects: manageable_project = self.client.projects.get(project.id) if self.is_excluded(manageable_project.path_with_namespace): print( f" - 🏠 Project {AnsiColors.BLUE}{manageable_project.path_with_namespace}{AnsiColors.RESET} matches excludes: {AnsiColors.HGRAY}skip{AnsiColors.RESET}" ) else: if self.cleaning_enabled_project(manageable_project): # store current count of cleaned pipeline current_pipeline_count = self.pipelines_count start_time = time.monotonic() Loading poetry.lock +9 −6 Original line number Diff line number Diff line Loading @@ -218,15 +218,18 @@ toml = ["tomli"] [[package]] name = "idna" version = "3.8" version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" files = [ {file = "idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac"}, {file = "idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603"}, {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, ] [package.extras] all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] [[package]] name = "iniconfig" version = "2.0.0" Loading Loading @@ -519,13 +522,13 @@ dev = ["pre-commit", "pytest-asyncio", "tox"] [[package]] name = "python-gitlab" version = "4.11.1" version = "4.12.0" description = "A python wrapper for the GitLab API" optional = false python-versions = ">=3.8.0" files = [ {file = "python_gitlab-4.11.1-py3-none-any.whl", hash = "sha256:f15fe4f4cbaa58c04e422ad30a2d4fc7976294dfdf1f90f81ab927e5b2238f33"}, {file = "python_gitlab-4.11.1.tar.gz", hash = "sha256:7afa2f9c30618bc3daee95d2186a06e1cf18ca2fa97890eb55fd8ddc4e339812"}, {file = "python_gitlab-4.12.0-py3-none-any.whl", hash = "sha256:bb82f877d4af48dc94f1fdc3f83abdc2fcb811bf3b523c03b90a618519dff80c"}, {file = "python_gitlab-4.12.0.tar.gz", hash = "sha256:d65c08ba787156e01f7359429b201a83395bde3f2ea01f51115996be0e346d8e"}, ] [package.dependencies] Loading tests/test_groups.py +4 −2 Original line number Diff line number Diff line Loading @@ -7,9 +7,9 @@ from gitlab_butler.main import to_url # project details : https://docs.gitlab.com/ee/api/projects.html#get-single-project def mock_empty_project(project_id): def mock_empty_project(project_id, archived: bool = False): responses.add(responses.GET, f'http://gitlab.test/api/v4/projects/{project_id}', status=200, json= {'id': project_id, 'path': f'project_{project_id}', 'path_with_namespace': f'path/to/group/project_{project_id}', 'default_branch': 'main'} {'id': project_id, 'path': f'project_{project_id}', 'path_with_namespace': f'path/to/group/project_{project_id}', 'default_branch': 'main', 'archived': archived} ) responses.add(responses.GET, f'http://gitlab.test/api/v4/projects/{project_id}/repository/files/.butlercfg.yaml?ref=main', status=404) responses.add(responses.GET, f'http://gitlab.test/api/v4/projects/{project_id}/repository/files/.butlercfg.yml?ref=main', status=404) Loading Loading @@ -208,6 +208,7 @@ class TestGroups: {'id': 1000, 'path': 'path/to/group/project1'}, {'id': 1001, 'path': 'path/to/group/project2'}, {'id': 1002, 'path': 'path/to/group/project3'}, {'id': 1003, 'path': 'path/to/group/project4'}, ]) responses.add(responses.GET, 'http://gitlab.test/api/v4/groups/42/descendant_groups?per_page=100', status=200, json=[]) Loading @@ -215,6 +216,7 @@ class TestGroups: mock_empty_project('1000') mock_empty_project('1001') mock_empty_project('1002') mock_empty_project('1003', archived=True) # get initial group group = butler.client.groups.get(butler.group_path) Loading Loading
gitlab_butler/butler.py +23 −7 Original line number Diff line number Diff line Loading @@ -9,7 +9,7 @@ import yaml from gitlab import ( Gitlab, GitlabDeleteError, GitlabGetError, GitlabGetError, GitlabHttpError, ) from gitlab.v4.objects import Group, Project, ProjectFile, ProjectPipeline Loading Loading @@ -101,7 +101,7 @@ class Butler: file_path=file_name, ref=project.default_branch ) break except GitlabGetError as ge: except (GitlabHttpError, GitlabGetError) as ge: if ge.response_code != 404: self.handle_error(ge) Loading Loading @@ -208,6 +208,26 @@ class Butler: raise ValueError(f'Unexpected pipeline source "{source.source_type}"') def cleaning_enabled_project(self, project: Project) -> bool: """ Check if project should be cleaned. Projects can be excluded or be in read-only mode. :param project: :return: True if project should be cleaned, False otherwise """ if self.is_excluded(project.path_with_namespace): print( f" - 🏠 Project {AnsiColors.BLUE}{project.path_with_namespace}{AnsiColors.RESET} matches excludes: {AnsiColors.HGRAY}skip{AnsiColors.RESET}" ) return False if project.archived: print( f" - 🏠 Project {AnsiColors.BLUE}{project.path_with_namespace}{AnsiColors.RESET} is archived: {AnsiColors.HGRAY}skip{AnsiColors.RESET}" ) return False return True def find_pipeline_source_status(self, project: Project, pipeline: ProjectPipeline, caches: dict) -> PipelineSource | None: """ Check if the source of the pipeline still exists Loading Loading @@ -397,11 +417,7 @@ class Butler: print(f"- clean {len(subprojects)} sub projects...") for project in subprojects: manageable_project = self.client.projects.get(project.id) if self.is_excluded(manageable_project.path_with_namespace): print( f" - 🏠 Project {AnsiColors.BLUE}{manageable_project.path_with_namespace}{AnsiColors.RESET} matches excludes: {AnsiColors.HGRAY}skip{AnsiColors.RESET}" ) else: if self.cleaning_enabled_project(manageable_project): # store current count of cleaned pipeline current_pipeline_count = self.pipelines_count start_time = time.monotonic() Loading
poetry.lock +9 −6 Original line number Diff line number Diff line Loading @@ -218,15 +218,18 @@ toml = ["tomli"] [[package]] name = "idna" version = "3.8" version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" files = [ {file = "idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac"}, {file = "idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603"}, {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, ] [package.extras] all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] [[package]] name = "iniconfig" version = "2.0.0" Loading Loading @@ -519,13 +522,13 @@ dev = ["pre-commit", "pytest-asyncio", "tox"] [[package]] name = "python-gitlab" version = "4.11.1" version = "4.12.0" description = "A python wrapper for the GitLab API" optional = false python-versions = ">=3.8.0" files = [ {file = "python_gitlab-4.11.1-py3-none-any.whl", hash = "sha256:f15fe4f4cbaa58c04e422ad30a2d4fc7976294dfdf1f90f81ab927e5b2238f33"}, {file = "python_gitlab-4.11.1.tar.gz", hash = "sha256:7afa2f9c30618bc3daee95d2186a06e1cf18ca2fa97890eb55fd8ddc4e339812"}, {file = "python_gitlab-4.12.0-py3-none-any.whl", hash = "sha256:bb82f877d4af48dc94f1fdc3f83abdc2fcb811bf3b523c03b90a618519dff80c"}, {file = "python_gitlab-4.12.0.tar.gz", hash = "sha256:d65c08ba787156e01f7359429b201a83395bde3f2ea01f51115996be0e346d8e"}, ] [package.dependencies] Loading
tests/test_groups.py +4 −2 Original line number Diff line number Diff line Loading @@ -7,9 +7,9 @@ from gitlab_butler.main import to_url # project details : https://docs.gitlab.com/ee/api/projects.html#get-single-project def mock_empty_project(project_id): def mock_empty_project(project_id, archived: bool = False): responses.add(responses.GET, f'http://gitlab.test/api/v4/projects/{project_id}', status=200, json= {'id': project_id, 'path': f'project_{project_id}', 'path_with_namespace': f'path/to/group/project_{project_id}', 'default_branch': 'main'} {'id': project_id, 'path': f'project_{project_id}', 'path_with_namespace': f'path/to/group/project_{project_id}', 'default_branch': 'main', 'archived': archived} ) responses.add(responses.GET, f'http://gitlab.test/api/v4/projects/{project_id}/repository/files/.butlercfg.yaml?ref=main', status=404) responses.add(responses.GET, f'http://gitlab.test/api/v4/projects/{project_id}/repository/files/.butlercfg.yml?ref=main', status=404) Loading Loading @@ -208,6 +208,7 @@ class TestGroups: {'id': 1000, 'path': 'path/to/group/project1'}, {'id': 1001, 'path': 'path/to/group/project2'}, {'id': 1002, 'path': 'path/to/group/project3'}, {'id': 1003, 'path': 'path/to/group/project4'}, ]) responses.add(responses.GET, 'http://gitlab.test/api/v4/groups/42/descendant_groups?per_page=100', status=200, json=[]) Loading @@ -215,6 +216,7 @@ class TestGroups: mock_empty_project('1000') mock_empty_project('1001') mock_empty_project('1002') mock_empty_project('1003', archived=True) # get initial group group = butler.client.groups.get(butler.group_path) Loading