gitlabform 6.2.0__tar.gz → 6.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {gitlabform-6.2.0 → gitlabform-6.2.1}/.github/workflows/_releases.yml +7 -19
- gitlabform-6.2.1/.python-version +1 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/PKG-INFO +1 -1
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/changelog.md +18 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/project/files_processor.py +3 -5
- gitlabform-6.2.1/gitlabform/processors/project/merge_requests_approval_rules.py +38 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/pyproject.toml +8 -8
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tbump.toml +2 -1
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/premium/test_merge_request_approval_rules.py +53 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_files.py +95 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_remote_mirrors.py +3 -3
- gitlabform-6.2.1/tests/unit/processors/test_merge_requests_approval_rules_processor.py +120 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/uv.lock +159 -159
- gitlabform-6.2.0/.python-version +0 -1
- gitlabform-6.2.0/gitlabform/processors/project/merge_requests_approval_rules.py +0 -17
- {gitlabform-6.2.0 → gitlabform-6.2.1}/.coveragerc +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/.dockerignore +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/.git-blame-ignore-revs +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/.github/CODEOWNERS +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/.github/ISSUE_TEMPLATE/question-or-other-issue.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/.github/actions/setup-uv-local/README.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/.github/actions/setup-uv-local/action.yml +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/.github/dependabot.yml +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/.github/workflows/_main.yml +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/.github/workflows/build.yml +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/.github/workflows/codecov-upload.yml +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/.github/workflows/pr-ci-workflow.yml +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/.github/workflows/prs-entrypoint-forks.yml +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/.github/workflows/prs-entrypoint-main.yml +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/.github/workflows/static-analysis.yml +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/.github/workflows/tests-acceptance.yml +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/.github/workflows/tests-unit.yml +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/.gitignore +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/.lgtm.yml +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/.mypy.ini +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/.overrides/gitlabform-logo-favicon.png +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/.overrides/gitlabform-logo-favicon.svg +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/CONTRIBUTING.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/Dockerfile +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/LICENSE +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/README.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/codecov.yml +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/dev/__init__.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/dev/common.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/dev/docker.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/dev/docs.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/dev/env.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/dev/gitlab/await-healthy.sh +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/dev/gitlab/gitlab.rb +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/dev/gitlab/healthcheck-and-setup.sh +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/dev/gitlab/run_gitlab_in_docker.sh +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/dev/gitlab/tests.Dockerfile +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/dev/infra.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/dev/main.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/dev/package.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/dev/qa.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/dev/release.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/automation.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/contrib/coding_guidelines.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/contrib/implementation_design.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/contrib/index.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/contrib/local_development.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/contrib/releases.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/contrib/workflows.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/features.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/images/Opencast-logo.png +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/images/egnyte-logo.svg +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/images/elastic-path-logo.svg +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/images/gitlabform-logo-square.png +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/images/gitlabform-logo.png +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/images/gitlabform-logo.svg +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/images/group-ldap-links-provider.png +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/images/liquidlight-logo.svg +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/images/roche-logo.png +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/images/stroeer-online-marketing-logo.svg +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/index.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/installation.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/reference/archive_unarchive.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/reference/avatar.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/reference/badges.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/reference/ci_cd_variables.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/reference/deploy_keys.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/reference/files.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/reference/group_ldap_links.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/reference/group_saml_links.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/reference/index.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/reference/integrations.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/reference/job_token_scope.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/reference/labels.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/reference/members.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/reference/merge_requests.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/reference/pipeline_schedules.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/reference/project_security_settings.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/reference/project_transfer.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/reference/protected_branches.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/reference/protected_environments.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/reference/push_mirrors.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/reference/push_rules.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/reference/resource_groups.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/reference/settings.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/reference/tags_protection.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/reference/webhooks.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/requirements.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/running.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/similar_apps.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/docs/upgrade.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlab-config/config.yml +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlab-config/gitlab-config.md +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/__init__.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/configuration/__init__.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/configuration/common.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/configuration/core.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/configuration/groups.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/configuration/projects.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/configuration/transform.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/constants.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/gitlab/__init__.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/gitlab/commits.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/gitlab/core.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/gitlab/group_badges.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/gitlab/group_ldap_links.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/gitlab/groups.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/gitlab/merge_requests.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/gitlab/pipelines.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/gitlab/project_badges.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/gitlab/project_deploy_keys.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/gitlab/project_merge_requests_approvals.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/gitlab/project_protected_environments.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/gitlab/projects.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/gitlab/python_gitlab.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/gitlab/variables.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/lists/__init__.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/lists/filter.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/lists/groups.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/lists/projects.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/output.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/__init__.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/abstract_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/application/__init__.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/application/application_settings_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/defining_keys.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/group/__init__.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/group/group_badges_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/group/group_hooks_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/group/group_labels_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/group/group_ldap_links_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/group/group_members_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/group/group_protected_branches_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/group/group_push_rules_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/group/group_saml_links_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/group/group_settings_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/group/group_variables_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/multiple_entities_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/project/__init__.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/project/badges_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/project/branches_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/project/deploy_keys_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/project/hooks_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/project/integrations_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/project/job_token_scope_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/project/members_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/project/merge_requests_approvals.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/project/project_labels_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/project/project_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/project/project_push_rules_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/project/project_security_settings.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/project/project_settings_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/project/project_variables_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/project/remote_mirrors_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/project/resource_groups_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/project/schedules_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/project/tags_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/shared/__init__.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/shared/protected_environments_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/util/__init__.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/util/branch_protection.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/util/decorators.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/util/difference_logger.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/util/labels_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/processors/util/variables_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/run.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/gitlabform/util.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/mkdocs.yml +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/prek.toml +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/__init__.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/__init__.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/conftest.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/premium/test_branches.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/premium/test_branches_users_case_insensitive.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/premium/test_group_ldap_links.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/premium/test_group_protected_branches.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/premium/test_group_push_rules.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/premium/test_group_saml_links.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/premium/test_group_settings.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/premium/test_group_variables.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/premium/test_merge_request_approvals_settings.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/premium/test_protected_environments.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/premium/test_push_rules.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/premium/test_tags.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/__init__.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_application_settings.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_archive_project.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_badges.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_branches.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_deploy_keys.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_deploy_keys_all_projects.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_files_templates.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_group_avatar.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_group_badges.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_group_hooks.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_group_labels.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_group_members_case_insensitive.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_group_members_groups.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_group_members_users.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_group_settings.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_group_variables.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_hooks.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_integrations.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_job_token_scope.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_members.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_members_add_group.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_members_enforce.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_project_avatar.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_project_group_members_case_insensitive.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_project_labels.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_project_members_case_insensitve.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_project_settings.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_project_variables.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_resource_groups.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_running.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_scheduled_for_deletion_project.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_schedules.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_tags.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_token_from_config.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_transfer_project.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/standard/test_wildcard_patterns.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/ultimate/test_group_members.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/ultimate/test_group_settings.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/ultimate/test_members.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/ultimate/test_project_security_settings.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/ultimate/test_project_settings.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/__init__.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/configuration/__init__.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/configuration/test_case_sensitivity.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/configuration/test_inheritance_break_projects_and_groups.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/configuration/test_inheritance_break_subgroups.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/configuration/test_inheritance_break_validation.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/configuration/test_projects_and_groups.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/configuration/test_skip_groups_skip_projects.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/configuration/test_subgroups.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/configuration/test_wildcard_patterns.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/configuration/test_yaml_version.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/configuration/transform/test_access_level_transformer.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/configuration/transform/test_group_transformer.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/configuration/transform/test_implicit_name_transformer.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/configuration/transform/test_user_and_group_transformers.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/configuration/transform/test_user_transformer.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/gitlab/test_core.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/gitlab/test_python_gitlab.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/processors/__init__.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/processors/test_abstract_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/processors/test_abstract_processors.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/processors/test_branches_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/processors/test_difference_logger.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/processors/test_files_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/processors/test_group_members_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/processors/test_group_protected_branches_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/processors/test_group_saml_links_processor.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/processors/test_schedules_processor_extended_cron_pattern.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/test_access_levels.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/test_non_empty_configs_provider.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/test_parse_args.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/test_projects_provider.py +0 -0
- {gitlabform-6.2.0 → gitlabform-6.2.1}/tests/unit/test_utils.py +0 -0
|
@@ -64,17 +64,6 @@ jobs:
|
|
|
64
64
|
MANUAL_UPSTREAM_RUN_ID: ${{ inputs.override_run_id }}
|
|
65
65
|
run: uv run release gh-workflow-check
|
|
66
66
|
|
|
67
|
-
# TODO: Remove this job when all the release jobs have been migrated
|
|
68
|
-
# to consume build artifacts from Main workflow instead of rebuilding.
|
|
69
|
-
build-release-artifacts:
|
|
70
|
-
name: Build release artifacts
|
|
71
|
-
if: ${{ needs.prep-release.outputs.is_valid_release == 'true' }}
|
|
72
|
-
needs:
|
|
73
|
-
- prep-release
|
|
74
|
-
uses: ./.github/workflows/build.yml
|
|
75
|
-
with:
|
|
76
|
-
BRANCH_REF: ${{ needs.prep-release.outputs.semver_tag }}
|
|
77
|
-
|
|
78
67
|
publish-docs:
|
|
79
68
|
name: Publish Docs
|
|
80
69
|
if: ${{ needs.prep-release.outputs.is_valid_release == 'true' }}
|
|
@@ -120,7 +109,6 @@ jobs:
|
|
|
120
109
|
if: ${{ needs.prep-release.outputs.is_valid_release == 'true' }}
|
|
121
110
|
needs:
|
|
122
111
|
- prep-release
|
|
123
|
-
- build-release-artifacts
|
|
124
112
|
runs-on: ubuntu-latest
|
|
125
113
|
environment:
|
|
126
114
|
name: pypi
|
|
@@ -140,12 +128,13 @@ jobs:
|
|
|
140
128
|
- name: Setup Python ${{ env.UV_PYTHON }} and install dependencies
|
|
141
129
|
run: |
|
|
142
130
|
uv sync --frozen --no-dev --group release
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
131
|
+
- name: Download built package
|
|
132
|
+
uses: actions/download-artifact@v8
|
|
133
|
+
with:
|
|
134
|
+
run-id: ${{ needs.prep-release.outputs.target_run_id }}
|
|
135
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
136
|
+
name: package-dist
|
|
137
|
+
path: dist
|
|
149
138
|
- name: Publish to PyPI
|
|
150
139
|
run: |
|
|
151
140
|
uv run --no-sync release pypi
|
|
@@ -155,7 +144,6 @@ jobs:
|
|
|
155
144
|
if: ${{ needs.prep-release.outputs.is_valid_release == 'true' }}
|
|
156
145
|
needs:
|
|
157
146
|
- prep-release
|
|
158
|
-
- build-release-artifacts
|
|
159
147
|
- publish-to-pypi
|
|
160
148
|
runs-on: ubuntu-latest
|
|
161
149
|
steps:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.14.6
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitlabform
|
|
3
|
-
Version: 6.2.
|
|
3
|
+
Version: 6.2.1
|
|
4
4
|
Summary: 🏗 Specialized configuration as a code tool for GitLab projects, groups and more using hierarchical configuration written in YAML
|
|
5
5
|
Project-URL: Homepage, https://gitlabform.github.io/gitlabform/
|
|
6
6
|
Project-URL: Repository, https://github.com/gitlabform/gitlabform.git
|
|
@@ -6,6 +6,24 @@ For details on how to migrate between major versions, please refer to the [upgra
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## 6.2.1
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* catch GitlabCreateError and GitlabDeleteError when modifying files on protected branches [#1361](https://github.com/gitlabform/gitlabform/pull/1361). ([haijeploeg](https://github.com/haijeploeg))
|
|
14
|
+
* fix merge_request_approval_rules to stop redundant edit calls on unchanged rules [#1350](https://github.com/gitlabform/gitlabform/pull/1350). ([rickbrouwer](https://github.com/rickbrouwer))
|
|
15
|
+
* use add instead of delete in commit message for new files to reflect what actually happened in commit history [#1363](https://github.com/gitlabform/gitlabform/pull/1363). ([rickbrouwer](https://github.com/rickbrouwer))
|
|
16
|
+
|
|
17
|
+
### Build
|
|
18
|
+
|
|
19
|
+
* consume package artifact from main workflow in pypi release [#1327](https://github.com/gitlabform/gitlabform/pull/1327). ([amimas](https://github.com/amimas))
|
|
20
|
+
|
|
21
|
+
### Dependencies
|
|
22
|
+
|
|
23
|
+
* Update various dependencies to newer versions.
|
|
24
|
+
|
|
25
|
+
Thanks to all the contributors of this release!
|
|
26
|
+
|
|
9
27
|
## 6.2.0
|
|
10
28
|
|
|
11
29
|
### Features
|
|
@@ -6,7 +6,7 @@ from pathlib import Path
|
|
|
6
6
|
from logging import debug, info, warning, critical
|
|
7
7
|
|
|
8
8
|
from jinja2 import Environment, FileSystemLoader
|
|
9
|
-
from gitlab import GitlabGetError,
|
|
9
|
+
from gitlab import GitlabCreateError, GitlabDeleteError, GitlabGetError, GitlabListError, GitlabUpdateError
|
|
10
10
|
from gitlab.v4.objects import Project, ProjectFile, ProjectBranch
|
|
11
11
|
from gitlab.base import RESTObject
|
|
12
12
|
|
|
@@ -235,7 +235,7 @@ class FilesProcessor(AbstractProcessor):
|
|
|
235
235
|
new_content,
|
|
236
236
|
)
|
|
237
237
|
|
|
238
|
-
except GitlabUpdateError as e:
|
|
238
|
+
except (GitlabUpdateError, GitlabCreateError, GitlabDeleteError) as e:
|
|
239
239
|
if (
|
|
240
240
|
e.response_code == 400 or e.response_code == 403
|
|
241
241
|
) and "You are not allowed to push into this branch" in e.error_message:
|
|
@@ -302,9 +302,7 @@ class FilesProcessor(AbstractProcessor):
|
|
|
302
302
|
"file_path": file_to_operate_on,
|
|
303
303
|
"branch": branch.name,
|
|
304
304
|
"content": new_content,
|
|
305
|
-
"commit_message": self.get_commit_message_for_file_change(
|
|
306
|
-
"delete", file_to_operate_on, configuration
|
|
307
|
-
),
|
|
305
|
+
"commit_message": self.get_commit_message_for_file_change("add", file_to_operate_on, configuration),
|
|
308
306
|
}
|
|
309
307
|
)
|
|
310
308
|
elif operation == "delete" and isinstance(file_to_operate_on, ProjectFile):
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from gitlabform.gitlab import GitLab
|
|
2
|
+
from gitlabform.processors.defining_keys import Key, And
|
|
3
|
+
from gitlabform.processors.multiple_entities_processor import MultipleEntitiesProcessor
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MergeRequestsApprovalRules(MultipleEntitiesProcessor):
|
|
7
|
+
def __init__(self, gitlab: GitLab):
|
|
8
|
+
super().__init__(
|
|
9
|
+
"merge_requests_approval_rules",
|
|
10
|
+
gitlab,
|
|
11
|
+
list_method_name="get_approval_rules",
|
|
12
|
+
add_method_name="add_approval_rule",
|
|
13
|
+
edit_method_name="edit_approval_rule",
|
|
14
|
+
delete_method_name="delete_approval_rule",
|
|
15
|
+
defining=Key("name"),
|
|
16
|
+
required_to_create_or_update=And(Key("name"), Key("approvals_required")),
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
def _needs_update(self, entity_in_gitlab: dict, entity_in_configuration: dict) -> bool:
|
|
20
|
+
# GitLab returns users/groups as lists of objects and protected_branches as list
|
|
21
|
+
# of objects with a "name" field, while the config (post-transform) has user_ids /
|
|
22
|
+
# group_ids as int lists and protected_branches as list of names. Without
|
|
23
|
+
# normalization the base _needs_update always triggers an update, even when nothing
|
|
24
|
+
# changed. We normalize both sides unconditionally so keys line up regardless of
|
|
25
|
+
# whether GitLab omitted an empty list or the user omitted the field in config.
|
|
26
|
+
gitlab_norm = dict(entity_in_gitlab)
|
|
27
|
+
gitlab_norm["user_ids"] = sorted(u["id"] for u in gitlab_norm.pop("users", []))
|
|
28
|
+
gitlab_norm["group_ids"] = sorted(g["id"] for g in gitlab_norm.pop("groups", []))
|
|
29
|
+
gitlab_norm["protected_branches"] = sorted(b["name"] for b in gitlab_norm.get("protected_branches", []))
|
|
30
|
+
|
|
31
|
+
# edit_approval_rule treats missing user_ids/group_ids/protected_branches
|
|
32
|
+
# in config as "clear them", so mirror that here to keep the comparison honest.
|
|
33
|
+
config_norm = dict(entity_in_configuration)
|
|
34
|
+
config_norm["user_ids"] = sorted(config_norm.get("user_ids", []))
|
|
35
|
+
config_norm["group_ids"] = sorted(config_norm.get("group_ids", []))
|
|
36
|
+
config_norm["protected_branches"] = sorted(config_norm.get("protected_branches", []))
|
|
37
|
+
|
|
38
|
+
return super()._needs_update(gitlab_norm, config_norm)
|
|
@@ -10,7 +10,7 @@ packages = ["gitlabform"]
|
|
|
10
10
|
|
|
11
11
|
[project]
|
|
12
12
|
name = "gitlabform"
|
|
13
|
-
version = "6.2.
|
|
13
|
+
version = "6.2.1"
|
|
14
14
|
requires-python = ">=3.12.0"
|
|
15
15
|
authors = [{ name = "Greg Dubicki and Contributors" }]
|
|
16
16
|
description = "🏗 Specialized configuration as a code tool for GitLab projects, groups and more using hierarchical configuration written in YAML"
|
|
@@ -53,12 +53,12 @@ dev = [
|
|
|
53
53
|
{ include-group = "lint" },
|
|
54
54
|
{ include-group = "docs" },
|
|
55
55
|
{ include-group = "release" },
|
|
56
|
-
"commitizen==4.16.
|
|
57
|
-
"prek==0.4.
|
|
56
|
+
"commitizen==4.16.5",
|
|
57
|
+
"prek==0.4.10",
|
|
58
58
|
]
|
|
59
59
|
# Minimum set of dependencies required to execute the test suite.
|
|
60
60
|
test = [
|
|
61
|
-
"coverage==7.15.
|
|
61
|
+
"coverage==7.15.2",
|
|
62
62
|
"cryptography==49.0.0",
|
|
63
63
|
"pytest==9.1.1",
|
|
64
64
|
"pytest-cov==7.1.0",
|
|
@@ -69,17 +69,17 @@ test = [
|
|
|
69
69
|
lint = [
|
|
70
70
|
"bandit==1.9.4",
|
|
71
71
|
"black==26.5.1",
|
|
72
|
-
"codespell==2.4.
|
|
73
|
-
"mypy==2.
|
|
72
|
+
"codespell==2.4.3",
|
|
73
|
+
"mypy==2.3.0",
|
|
74
74
|
"mypy-extensions==1.1.0",
|
|
75
|
-
"ruff==0.15.
|
|
75
|
+
"ruff==0.15.22",
|
|
76
76
|
"types-requests==2.33.0.20260712",
|
|
77
77
|
]
|
|
78
78
|
# Documentation generation stack.
|
|
79
79
|
docs = [
|
|
80
80
|
"markdown==3.10.2",
|
|
81
81
|
"mkdocs==1.6.1",
|
|
82
|
-
"mkdocs-material==9.7.
|
|
82
|
+
"mkdocs-material==9.7.7",
|
|
83
83
|
"mkdocs-material-extensions==1.3.1",
|
|
84
84
|
"pygments==2.20.0",
|
|
85
85
|
"pymdown-extensions==11.0.1",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
github_url = "https://github.com/gitlabform/gitlabform/"
|
|
3
3
|
|
|
4
4
|
[version]
|
|
5
|
-
current = "6.2.
|
|
5
|
+
current = "6.2.1"
|
|
6
6
|
|
|
7
7
|
# Example of a semver regexp.
|
|
8
8
|
# Make sure this matches current_version before
|
|
@@ -25,6 +25,7 @@ tag_template = "v{new_version}"
|
|
|
25
25
|
# tbump.toml location.
|
|
26
26
|
[[file]]
|
|
27
27
|
src = "pyproject.toml"
|
|
28
|
+
search = 'version = "{current_version}"'
|
|
28
29
|
|
|
29
30
|
# You can specify a list of commands to
|
|
30
31
|
# run after the files have been patched
|
{gitlabform-6.2.0 → gitlabform-6.2.1}/tests/acceptance/premium/test_merge_request_approval_rules.py
RENAMED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
1
3
|
import pytest
|
|
2
4
|
|
|
3
5
|
from gitlabform.gitlab import AccessLevel
|
|
@@ -546,3 +548,54 @@ class TestMergeRequestApprovers:
|
|
|
546
548
|
coverage_check_approval_rule_details = mr_approval_rules_under_this_project[1]
|
|
547
549
|
assert coverage_check_approval_rule_details.name == "Coverage-Check"
|
|
548
550
|
assert coverage_check_approval_rule_details.approvals_required == 0
|
|
551
|
+
|
|
552
|
+
def test__no_churn_when_config_unchanged(
|
|
553
|
+
self,
|
|
554
|
+
project_for_function,
|
|
555
|
+
group_with_one_owner_and_two_developers,
|
|
556
|
+
make_user,
|
|
557
|
+
branch_for_function,
|
|
558
|
+
caplog,
|
|
559
|
+
):
|
|
560
|
+
user1 = make_user(AccessLevel.DEVELOPER, add_to_project=False)
|
|
561
|
+
project_for_function.members.create({"user_id": user1.id, "access_level": AccessLevel.DEVELOPER.value})
|
|
562
|
+
|
|
563
|
+
config = f"""
|
|
564
|
+
projects_and_groups:
|
|
565
|
+
{project_for_function.path_with_namespace}:
|
|
566
|
+
branches:
|
|
567
|
+
{branch_for_function}:
|
|
568
|
+
protected: true
|
|
569
|
+
push_access_level: no access
|
|
570
|
+
merge_access_level: developer
|
|
571
|
+
unprotect_access_level: maintainer
|
|
572
|
+
merge_requests_approval_rules:
|
|
573
|
+
standard:
|
|
574
|
+
approvals_required: 1
|
|
575
|
+
name: "Regular approvers"
|
|
576
|
+
users:
|
|
577
|
+
- {user1.username}
|
|
578
|
+
security:
|
|
579
|
+
approvals_required: 2
|
|
580
|
+
name: "Extra Security Team approval for selected branches"
|
|
581
|
+
users:
|
|
582
|
+
- {user1.username}
|
|
583
|
+
groups:
|
|
584
|
+
- {group_with_one_owner_and_two_developers.full_path}
|
|
585
|
+
protected_branches:
|
|
586
|
+
- {branch_for_function}
|
|
587
|
+
"""
|
|
588
|
+
|
|
589
|
+
run_gitlabform(config, project_for_function)
|
|
590
|
+
assert len(project_for_function.approvalrules.list()) >= 2
|
|
591
|
+
|
|
592
|
+
caplog.clear()
|
|
593
|
+
with caplog.at_level(logging.INFO):
|
|
594
|
+
run_gitlabform(config, project_for_function)
|
|
595
|
+
|
|
596
|
+
assert (
|
|
597
|
+
"Editing standard of merge_requests_approval_rules" not in caplog.text
|
|
598
|
+
), f"Expected no edit for unchanged 'standard' rule, got: {caplog.text}"
|
|
599
|
+
assert (
|
|
600
|
+
"Editing security of merge_requests_approval_rules" not in caplog.text
|
|
601
|
+
), f"Expected no edit for unchanged 'security' rule, got: {caplog.text}"
|
|
@@ -177,6 +177,101 @@ class TestFiles:
|
|
|
177
177
|
break
|
|
178
178
|
assert is_protected == True
|
|
179
179
|
|
|
180
|
+
def test__add_new_file_strongly_protected_branch(self, project, no_access_branch, is_enterprise_edition):
|
|
181
|
+
"""
|
|
182
|
+
Creating a NEW file (as opposed to modifying an existing one) goes through
|
|
183
|
+
python-gitlab's files.create(), which raises GitlabCreateError instead of
|
|
184
|
+
GitlabUpdateError. This test validates that the temporary unprotect/re-protect
|
|
185
|
+
fallback also works in that case.
|
|
186
|
+
"""
|
|
187
|
+
protected_branch = project.protectedbranches.get(no_access_branch.name)
|
|
188
|
+
push_access_level = protected_branch.push_access_levels[0]["access_level"]
|
|
189
|
+
merge_access_level = protected_branch.merge_access_levels[0]["access_level"]
|
|
190
|
+
|
|
191
|
+
# Unprotect access level is not available in GitLab CE.
|
|
192
|
+
unprotect_access_level_config = ""
|
|
193
|
+
if is_enterprise_edition:
|
|
194
|
+
unprotect_access_level = protected_branch.unprotect_access_levels[0]["access_level"]
|
|
195
|
+
unprotect_access_level_config = f"unprotect_access_level: {unprotect_access_level}"
|
|
196
|
+
|
|
197
|
+
add_file_to_protected_branch = f"""
|
|
198
|
+
projects_and_groups:
|
|
199
|
+
{project.path_with_namespace}:
|
|
200
|
+
branches:
|
|
201
|
+
{no_access_branch.name}:
|
|
202
|
+
protected: true
|
|
203
|
+
push_access_level: {push_access_level}
|
|
204
|
+
merge_access_level: {merge_access_level}
|
|
205
|
+
{unprotect_access_level_config}
|
|
206
|
+
files:
|
|
207
|
+
"newly-created-file.txt":
|
|
208
|
+
overwrite: true
|
|
209
|
+
branches:
|
|
210
|
+
- {no_access_branch.name}
|
|
211
|
+
content: "Content of a new file on a strongly protected branch"
|
|
212
|
+
"""
|
|
213
|
+
|
|
214
|
+
run_gitlabform(add_file_to_protected_branch, project)
|
|
215
|
+
|
|
216
|
+
project_file = project.files.get(ref=no_access_branch.name, file_path="newly-created-file.txt")
|
|
217
|
+
assert project_file.decode().decode("utf-8") == "Content of a new file on a strongly protected branch"
|
|
218
|
+
|
|
219
|
+
branch = project.branches.get(no_access_branch.name)
|
|
220
|
+
assert branch.commit["message"] == "Automated add made by gitlabform"
|
|
221
|
+
|
|
222
|
+
# the file must not exist on main
|
|
223
|
+
with pytest.raises(GitlabGetError):
|
|
224
|
+
project.files.get(ref="main", file_path="newly-created-file.txt")
|
|
225
|
+
|
|
226
|
+
# check that the branch protection was restored
|
|
227
|
+
protected_branches = project.protectedbranches.list()
|
|
228
|
+
assert any(pb.name == no_access_branch.name for pb in protected_branches)
|
|
229
|
+
|
|
230
|
+
def test__delete_file_strongly_protected_branch(self, project, no_access_branch, is_enterprise_edition):
|
|
231
|
+
"""
|
|
232
|
+
Deleting a file goes through python-gitlab's file.delete(), which raises
|
|
233
|
+
GitlabDeleteError instead of GitlabUpdateError. This test validates that
|
|
234
|
+
the temporary unprotect/re-protect fallback also works in that case.
|
|
235
|
+
"""
|
|
236
|
+
protected_branch = project.protectedbranches.get(no_access_branch.name)
|
|
237
|
+
push_access_level = protected_branch.push_access_levels[0]["access_level"]
|
|
238
|
+
merge_access_level = protected_branch.merge_access_levels[0]["access_level"]
|
|
239
|
+
|
|
240
|
+
# Unprotect access level is not available in GitLab CE.
|
|
241
|
+
unprotect_access_level_config = ""
|
|
242
|
+
if is_enterprise_edition:
|
|
243
|
+
unprotect_access_level = protected_branch.unprotect_access_levels[0]["access_level"]
|
|
244
|
+
unprotect_access_level_config = f"unprotect_access_level: {unprotect_access_level}"
|
|
245
|
+
|
|
246
|
+
delete_file_on_protected_branch = f"""
|
|
247
|
+
projects_and_groups:
|
|
248
|
+
{project.path_with_namespace}:
|
|
249
|
+
branches:
|
|
250
|
+
{no_access_branch.name}:
|
|
251
|
+
protected: true
|
|
252
|
+
push_access_level: {push_access_level}
|
|
253
|
+
merge_access_level: {merge_access_level}
|
|
254
|
+
{unprotect_access_level_config}
|
|
255
|
+
files:
|
|
256
|
+
"README.md":
|
|
257
|
+
branches:
|
|
258
|
+
- {no_access_branch.name}
|
|
259
|
+
delete: true
|
|
260
|
+
"""
|
|
261
|
+
|
|
262
|
+
run_gitlabform(delete_file_on_protected_branch, project)
|
|
263
|
+
|
|
264
|
+
with pytest.raises(GitlabGetError):
|
|
265
|
+
project.files.get(ref=no_access_branch.name, file_path="README.md")
|
|
266
|
+
|
|
267
|
+
# the file must still exist on main
|
|
268
|
+
project_file = project.files.get(ref="main", file_path="README.md")
|
|
269
|
+
assert project_file.decode().decode("utf-8") == DEFAULT_README
|
|
270
|
+
|
|
271
|
+
# check that the branch protection was restored
|
|
272
|
+
protected_branches = project.protectedbranches.list()
|
|
273
|
+
assert any(pb.name == no_access_branch.name for pb in protected_branches)
|
|
274
|
+
|
|
180
275
|
def test__delete_file_protected_branch(self, project, branch):
|
|
181
276
|
set_file_specific_branch = f"""
|
|
182
277
|
projects_and_groups:
|
|
@@ -754,7 +754,7 @@ class TestRemoteMirrorsProcessor:
|
|
|
754
754
|
|
|
755
755
|
def test_remote_mirrors_print_details(
|
|
756
756
|
self,
|
|
757
|
-
|
|
757
|
+
project_for_function: Project,
|
|
758
758
|
mirror_urls: Tuple[str, str, str],
|
|
759
759
|
root_username,
|
|
760
760
|
root_access_token,
|
|
@@ -764,7 +764,7 @@ class TestRemoteMirrorsProcessor:
|
|
|
764
764
|
Validates that when print_details is true, the full mirror object
|
|
765
765
|
details are retrieved and printed to the info logs.
|
|
766
766
|
"""
|
|
767
|
-
target_path =
|
|
767
|
+
target_path = project_for_function.path_with_namespace
|
|
768
768
|
first_url, second_url, _ = mirror_urls
|
|
769
769
|
|
|
770
770
|
# Normalize URLs as they will appear in the masked logs
|
|
@@ -773,7 +773,7 @@ class TestRemoteMirrorsProcessor:
|
|
|
773
773
|
|
|
774
774
|
# 1. Setup: Ensure at least one mirror exists manually that is NOT in the config
|
|
775
775
|
# This tests that the report shows the "Final State" of GitLab, not just the config.
|
|
776
|
-
|
|
776
|
+
project_for_function.remote_mirrors.create({"url": second_url, "enabled": True, "auth_method": "password"})
|
|
777
777
|
|
|
778
778
|
# 2. Run GitLabForm with global print_details: true
|
|
779
779
|
# Config only contains the first mirror
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
from unittest.mock import MagicMock, patch
|
|
2
|
+
|
|
3
|
+
from gitlabform.processors.project.merge_requests_approval_rules import MergeRequestsApprovalRules
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TestMergeRequestsApprovalRulesProcessor:
|
|
7
|
+
def setup_method(self):
|
|
8
|
+
self.gitlab = MagicMock()
|
|
9
|
+
with patch("gitlabform.processors.abstract_processor.GitlabWrapper"):
|
|
10
|
+
self.processor = MergeRequestsApprovalRules(self.gitlab)
|
|
11
|
+
|
|
12
|
+
@staticmethod
|
|
13
|
+
def _gitlab_rule(
|
|
14
|
+
name="standard",
|
|
15
|
+
approvals_required=1,
|
|
16
|
+
users=None,
|
|
17
|
+
groups=None,
|
|
18
|
+
protected_branches=None,
|
|
19
|
+
applies_to_all_protected_branches=False,
|
|
20
|
+
rule_type="regular",
|
|
21
|
+
):
|
|
22
|
+
return {
|
|
23
|
+
"id": 1,
|
|
24
|
+
"name": name,
|
|
25
|
+
"rule_type": rule_type,
|
|
26
|
+
"report_type": None,
|
|
27
|
+
"eligible_approvers": [],
|
|
28
|
+
"approvals_required": approvals_required,
|
|
29
|
+
"users": users or [],
|
|
30
|
+
"groups": groups or [],
|
|
31
|
+
"contains_hidden_groups": False,
|
|
32
|
+
"protected_branches": protected_branches or [],
|
|
33
|
+
"applies_to_all_protected_branches": applies_to_all_protected_branches,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
def test_no_update_when_users_match(self):
|
|
37
|
+
gitlab_rule = self._gitlab_rule(
|
|
38
|
+
users=[{"id": 5, "username": "alice"}, {"id": 7, "username": "bob"}],
|
|
39
|
+
)
|
|
40
|
+
config = {"name": "standard", "approvals_required": 1, "user_ids": [7, 5]}
|
|
41
|
+
|
|
42
|
+
assert self.processor._needs_update(gitlab_rule, config) is False
|
|
43
|
+
|
|
44
|
+
def test_no_update_when_groups_match(self):
|
|
45
|
+
gitlab_rule = self._gitlab_rule(groups=[{"id": 3, "name": "sec"}])
|
|
46
|
+
config = {"name": "standard", "approvals_required": 1, "group_ids": [3]}
|
|
47
|
+
|
|
48
|
+
assert self.processor._needs_update(gitlab_rule, config) is False
|
|
49
|
+
|
|
50
|
+
def test_no_update_when_protected_branches_match(self):
|
|
51
|
+
gitlab_rule = self._gitlab_rule(
|
|
52
|
+
protected_branches=[
|
|
53
|
+
{"id": 1, "name": "main"},
|
|
54
|
+
{"id": 2, "name": "release"},
|
|
55
|
+
],
|
|
56
|
+
)
|
|
57
|
+
config = {
|
|
58
|
+
"name": "standard",
|
|
59
|
+
"approvals_required": 1,
|
|
60
|
+
"protected_branches": ["release", "main"],
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
assert self.processor._needs_update(gitlab_rule, config) is False
|
|
64
|
+
|
|
65
|
+
def test_no_update_when_config_omits_user_ids_and_gitlab_has_no_users(self):
|
|
66
|
+
gitlab_rule = self._gitlab_rule()
|
|
67
|
+
config = {"name": "standard", "approvals_required": 1}
|
|
68
|
+
|
|
69
|
+
assert self.processor._needs_update(gitlab_rule, config) is False
|
|
70
|
+
|
|
71
|
+
def test_no_update_when_gitlab_omits_users_groups_protected_branches_keys(self):
|
|
72
|
+
gitlab_rule = {
|
|
73
|
+
"id": 1,
|
|
74
|
+
"name": "standard",
|
|
75
|
+
"rule_type": "regular",
|
|
76
|
+
"approvals_required": 1,
|
|
77
|
+
}
|
|
78
|
+
config = {"name": "standard", "approvals_required": 1}
|
|
79
|
+
|
|
80
|
+
assert self.processor._needs_update(gitlab_rule, config) is False
|
|
81
|
+
|
|
82
|
+
def test_update_when_user_ids_differ(self):
|
|
83
|
+
gitlab_rule = self._gitlab_rule(users=[{"id": 5, "username": "alice"}])
|
|
84
|
+
config = {"name": "standard", "approvals_required": 1, "user_ids": [9]}
|
|
85
|
+
|
|
86
|
+
assert self.processor._needs_update(gitlab_rule, config) is True
|
|
87
|
+
|
|
88
|
+
def test_update_when_config_clears_users_but_gitlab_has_users(self):
|
|
89
|
+
gitlab_rule = self._gitlab_rule(users=[{"id": 5, "username": "alice"}])
|
|
90
|
+
config = {"name": "standard", "approvals_required": 1}
|
|
91
|
+
|
|
92
|
+
assert self.processor._needs_update(gitlab_rule, config) is True
|
|
93
|
+
|
|
94
|
+
def test_update_when_protected_branches_differ(self):
|
|
95
|
+
gitlab_rule = self._gitlab_rule(
|
|
96
|
+
protected_branches=[{"id": 1, "name": "main"}],
|
|
97
|
+
)
|
|
98
|
+
config = {
|
|
99
|
+
"name": "standard",
|
|
100
|
+
"approvals_required": 1,
|
|
101
|
+
"protected_branches": ["release"],
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
assert self.processor._needs_update(gitlab_rule, config) is True
|
|
105
|
+
|
|
106
|
+
def test_update_when_approvals_required_changes(self):
|
|
107
|
+
gitlab_rule = self._gitlab_rule(approvals_required=1)
|
|
108
|
+
config = {"name": "standard", "approvals_required": 2}
|
|
109
|
+
|
|
110
|
+
assert self.processor._needs_update(gitlab_rule, config) is True
|
|
111
|
+
|
|
112
|
+
def test_no_update_for_any_approver_rule(self):
|
|
113
|
+
gitlab_rule = self._gitlab_rule(rule_type="any_approver", approvals_required=0)
|
|
114
|
+
config = {
|
|
115
|
+
"name": "standard",
|
|
116
|
+
"approvals_required": 0,
|
|
117
|
+
"rule_type": "any_approver",
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
assert self.processor._needs_update(gitlab_rule, config) is False
|