gitlabform 5.5.7__tar.gz → 6.1.0__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-5.5.7 → gitlabform-6.1.0}/.github/ISSUE_TEMPLATE/bug_report.md +11 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/PKG-INFO +4 -5
- {gitlabform-5.5.7 → gitlabform-6.1.0}/dev/gitlab/gitlab.rb +4 -1
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/changelog.md +28 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/upgrade.md +19 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/__init__.py +21 -14
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/configuration/core.py +26 -4
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/configuration/projects.py +25 -4
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/configuration/transform.py +12 -11
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/gitlab/core.py +3 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/lists/projects.py +43 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/output.py +2 -3
- gitlabform-6.1.0/gitlabform/processors/group/group_saml_links_processor.py +61 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/project/__init__.py +2 -2
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/project/files_processor.py +13 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/project/project_variables_processor.py +19 -13
- gitlabform-6.1.0/gitlabform/util.py +47 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/pyproject.toml +10 -16
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tbump.toml +1 -1
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/premium/test_group_saml_links.py +32 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_files_templates.py +1 -1
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_remote_mirrors.py +85 -70
- gitlabform-6.1.0/tests/acceptance/standard/test_wildcard_patterns.py +68 -0
- gitlabform-6.1.0/tests/unit/configuration/test_wildcard_patterns.py +202 -0
- gitlabform-6.1.0/tests/unit/configuration/test_yaml_version.py +63 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/unit/configuration/transform/test_access_level_transformer.py +3 -11
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/unit/configuration/transform/test_group_transformer.py +1 -7
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/unit/configuration/transform/test_implicit_name_transformer.py +2 -3
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/unit/configuration/transform/test_user_and_group_transformers.py +1 -19
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/unit/configuration/transform/test_user_transformer.py +6 -17
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/unit/gitlab/test_core.py +71 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/unit/processors/test_files_processor.py +15 -0
- gitlabform-6.1.0/tests/unit/processors/test_group_saml_links_processor.py +93 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/uv.lock +136 -269
- gitlabform-5.5.7/gitlabform/processors/group/group_saml_links_processor.py +0 -65
- gitlabform-5.5.7/gitlabform/util.py +0 -7
- gitlabform-5.5.7/tests/unit/configuration/test_yaml_version.py +0 -36
- {gitlabform-5.5.7 → gitlabform-6.1.0}/.coveragerc +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/.dockerignore +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/.git-blame-ignore-revs +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/.github/CODEOWNERS +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/.github/ISSUE_TEMPLATE/question-or-other-issue.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/.github/actions/setup-uv-local/README.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/.github/actions/setup-uv-local/action.yml +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/.github/dependabot.yml +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/.github/workflows/_main.yml +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/.github/workflows/_releases.yml +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/.github/workflows/build.yml +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/.github/workflows/pr-ci-workflow.yml +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/.github/workflows/prs-entrypoint-forks.yml +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/.github/workflows/prs-entrypoint-main.yml +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/.github/workflows/static-analysis.yml +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/.github/workflows/tests-acceptance.yml +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/.github/workflows/tests-unit.yml +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/.gitignore +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/.lgtm.yml +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/.mypy.ini +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/.overrides/gitlabform-logo-favicon.png +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/.overrides/gitlabform-logo-favicon.svg +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/.python-version +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/CONTRIBUTING.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/Dockerfile +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/LICENSE +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/README.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/codecov.yml +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/dev/__init__.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/dev/common.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/dev/docker.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/dev/docs.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/dev/env.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/dev/gitlab/await-healthy.sh +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/dev/gitlab/healthcheck-and-setup.sh +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/dev/gitlab/run_gitlab_in_docker.sh +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/dev/gitlab/tests.Dockerfile +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/dev/infra.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/dev/main.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/dev/package.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/dev/qa.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/dev/release.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/automation.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/contrib/coding_guidelines.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/contrib/implementation_design.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/contrib/index.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/contrib/local_development.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/contrib/releases.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/contrib/workflows.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/features.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/images/Opencast-logo.png +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/images/egnyte-logo.svg +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/images/elastic-path-logo.svg +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/images/gitlabform-logo-square.png +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/images/gitlabform-logo.png +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/images/gitlabform-logo.svg +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/images/group-ldap-links-provider.png +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/images/liquidlight-logo.svg +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/images/roche-logo.png +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/images/stroeer-online-marketing-logo.svg +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/index.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/installation.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/reference/archive_unarchive.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/reference/avatar.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/reference/badges.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/reference/ci_cd_variables.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/reference/deploy_keys.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/reference/files.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/reference/group_ldap_links.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/reference/group_saml_links.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/reference/index.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/reference/integrations.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/reference/job_token_scope.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/reference/labels.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/reference/members.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/reference/merge_requests.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/reference/pipeline_schedules.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/reference/project_security_settings.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/reference/project_transfer.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/reference/protected_branches.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/reference/protected_environments.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/reference/push_mirrors.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/reference/push_rules.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/reference/resource_groups.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/reference/settings.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/reference/tags_protection.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/reference/webhooks.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/requirements.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/running.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/docs/similar_apps.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlab-config/config.yml +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlab-config/gitlab-config.md +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/configuration/__init__.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/configuration/common.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/configuration/groups.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/constants.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/gitlab/__init__.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/gitlab/commits.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/gitlab/group_badges.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/gitlab/group_ldap_links.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/gitlab/groups.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/gitlab/merge_requests.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/gitlab/pipelines.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/gitlab/project_badges.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/gitlab/project_deploy_keys.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/gitlab/project_merge_requests_approvals.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/gitlab/project_protected_environments.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/gitlab/projects.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/gitlab/python_gitlab.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/gitlab/variables.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/lists/__init__.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/lists/filter.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/lists/groups.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/__init__.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/abstract_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/application/__init__.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/application/application_settings_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/defining_keys.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/group/__init__.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/group/group_badges_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/group/group_hooks_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/group/group_labels_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/group/group_ldap_links_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/group/group_members_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/group/group_protected_branches_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/group/group_push_rules_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/group/group_settings_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/group/group_variables_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/multiple_entities_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/project/badges_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/project/branches_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/project/deploy_keys_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/project/hooks_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/project/integrations_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/project/job_token_scope_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/project/members_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/project/merge_requests_approval_rules.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/project/merge_requests_approvals.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/project/project_labels_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/project/project_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/project/project_push_rules_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/project/project_security_settings.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/project/project_settings_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/project/remote_mirrors_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/project/resource_groups_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/project/schedules_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/project/tags_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/shared/__init__.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/shared/protected_environments_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/util/__init__.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/util/branch_protection.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/util/decorators.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/util/difference_logger.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/util/labels_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/util/variables_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/run.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/mkdocs.yml +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/prek.toml +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/__init__.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/__init__.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/conftest.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/premium/test_branches.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/premium/test_branches_users_case_insensitive.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/premium/test_group_ldap_links.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/premium/test_group_protected_branches.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/premium/test_group_push_rules.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/premium/test_group_settings.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/premium/test_group_variables.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/premium/test_merge_request_approval_rules.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/premium/test_merge_request_approvals_settings.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/premium/test_protected_environments.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/premium/test_push_rules.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/premium/test_tags.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/__init__.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_application_settings.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_archive_project.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_badges.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_branches.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_deploy_keys.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_deploy_keys_all_projects.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_files.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_group_avatar.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_group_badges.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_group_hooks.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_group_labels.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_group_members_case_insensitive.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_group_members_groups.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_group_members_users.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_group_settings.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_group_variables.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_hooks.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_integrations.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_job_token_scope.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_members.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_members_add_group.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_members_enforce.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_project_avatar.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_project_group_members_case_insensitive.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_project_labels.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_project_members_case_insensitve.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_project_settings.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_project_variables.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_resource_groups.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_running.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_scheduled_for_deletion_project.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_schedules.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_tags.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_token_from_config.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/standard/test_transfer_project.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/ultimate/test_group_members.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/ultimate/test_group_settings.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/ultimate/test_members.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/ultimate/test_project_security_settings.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/acceptance/ultimate/test_project_settings.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/unit/__init__.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/unit/configuration/__init__.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/unit/configuration/test_case_sensitivity.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/unit/configuration/test_inheritance_break_projects_and_groups.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/unit/configuration/test_inheritance_break_subgroups.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/unit/configuration/test_inheritance_break_validation.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/unit/configuration/test_projects_and_groups.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/unit/configuration/test_skip_groups_skip_projects.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/unit/configuration/test_subgroups.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/unit/gitlab/test_python_gitlab.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/unit/processors/__init__.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/unit/processors/test_abstract_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/unit/processors/test_abstract_processors.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/unit/processors/test_branches_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/unit/processors/test_difference_logger.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/unit/processors/test_group_protected_branches_processor.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/unit/processors/test_schedules_processor_extended_cron_pattern.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/unit/test_access_levels.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/unit/test_non_empty_configs_provider.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/unit/test_parse_args.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/unit/test_projects_provider.py +0 -0
- {gitlabform-5.5.7 → gitlabform-6.1.0}/tests/unit/test_utils.py +0 -0
|
@@ -18,3 +18,14 @@ Output of `gitlabform -V`
|
|
|
18
18
|
**GitLab version**
|
|
19
19
|
|
|
20
20
|
F.e. `13.12.4-ee`
|
|
21
|
+
|
|
22
|
+
**GitLabForm configuration**
|
|
23
|
+
|
|
24
|
+
The relevant part of your gitlabform config (optionally anonymized).
|
|
25
|
+
If applicable, share both the before and after config.
|
|
26
|
+
|
|
27
|
+
**Would you be open to contributing a fix?**
|
|
28
|
+
|
|
29
|
+
- [ ] Yes
|
|
30
|
+
- [ ] No
|
|
31
|
+
- [ ] Maybe
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitlabform
|
|
3
|
-
Version:
|
|
3
|
+
Version: 6.1.0
|
|
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
|
|
@@ -19,9 +19,8 @@ Classifier: Operating System :: POSIX :: Linux
|
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.14
|
|
20
20
|
Classifier: Topic :: Software Development :: Version Control :: Git
|
|
21
21
|
Requires-Python: >=3.12.0
|
|
22
|
-
Requires-Dist: certifi==2026.
|
|
22
|
+
Requires-Dist: certifi==2026.6.17
|
|
23
23
|
Requires-Dist: cli-ui==0.19.0
|
|
24
|
-
Requires-Dist: ez-yaml==1.2.0
|
|
25
24
|
Requires-Dist: jinja2==3.1.6
|
|
26
25
|
Requires-Dist: luddite==1.0.4
|
|
27
26
|
Requires-Dist: markupsafe==3.0.3
|
|
@@ -30,8 +29,8 @@ Requires-Dist: packaging==26.2
|
|
|
30
29
|
Requires-Dist: python-gitlab[graphql]==8.4.0
|
|
31
30
|
Requires-Dist: requests==2.34.2
|
|
32
31
|
Requires-Dist: rich==15.0.0
|
|
33
|
-
Requires-Dist: ruamel-yaml==0.
|
|
34
|
-
Requires-Dist: yamlpath==3.
|
|
32
|
+
Requires-Dist: ruamel-yaml==0.19.1
|
|
33
|
+
Requires-Dist: yamlpath==3.9.1
|
|
35
34
|
Description-Content-Type: text/markdown
|
|
36
35
|
|
|
37
36
|
[](https://badge.fury.io/gh/gitlabform%2Fgitlabform)
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
gitlab_rails['initial_root_password']='mK9JnG7jwYdFcBNoQ3W3'
|
|
2
|
+
# Bind nginx on IPv4 and IPv6 loopback. Without IPv6, Sidekiq's mirror sync
|
|
3
|
+
# fails intermittently with ECONNREFUSED when libcurl resolves localhost to ::1.
|
|
4
|
+
nginx['listen_addresses'] = ['0.0.0.0', '[::]']
|
|
2
5
|
registry['enable']=false
|
|
3
6
|
prometheus_monitoring['enable']=false
|
|
4
7
|
gitlab_rails['initial_license_file']='/etc/gitlab/Gitlab.gitlab-license'
|
|
@@ -38,4 +41,4 @@ gitlab_rails['omniauth_providers'] = [
|
|
|
38
41
|
},
|
|
39
42
|
label: 'SAML Login'
|
|
40
43
|
}
|
|
41
|
-
]
|
|
44
|
+
]
|
|
@@ -6,6 +6,34 @@ For details on how to migrate between major versions, please refer to the [upgra
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## 6.1.0
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* Add wildcard pattern support for project configuration [#1311](https://github.com/gitlabform/gitlabform/pull/1311). ([nikaro](https://github.com/nikaro))
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* group_saml_links: stop delete-and-recreate churn on unchanged links [#1331](https://github.com/gitlabform/gitlabform/pull/1331). ([rickbrouwer](https://github.com/rickbrouwer))
|
|
18
|
+
* validate key in files config to avoid cryptic KeyError [#1317](https://github.com/gitlabform/gitlabform/pull/1317), resolves [#1312](https://github.com/gitlabform/gitlabform/issues/1312). ([rickbrouwer](https://github.com/rickbrouwer))
|
|
19
|
+
* Normalize GitLab URL while parsing configuration [#1338](https://github.com/gitlabform/gitlabform/pull/1338). ([schurzi](https://github.com/schurzi))
|
|
20
|
+
|
|
21
|
+
### Dependencies
|
|
22
|
+
|
|
23
|
+
* Update various dependencies to newer versions.
|
|
24
|
+
|
|
25
|
+
Thanks to all the contributors of this release!
|
|
26
|
+
|
|
27
|
+
## 6.0.0
|
|
28
|
+
|
|
29
|
+
### Breaking Changes
|
|
30
|
+
|
|
31
|
+
**Drops default support for YAML 1.1 by upgrading dependency major versions:** [#1309](https://github.com/gitlabform/gitlabform/pull/1309). ([timknight01](https://github.com/TimKnight01))
|
|
32
|
+
* Drops DeepDiff and ez_yaml dependencies
|
|
33
|
+
* Upgrades ruamel.yaml and yamlpath to their latest versions
|
|
34
|
+
|
|
35
|
+
See [upgrade guide](upgrade.md) for important upgrade instructions and migration details.
|
|
36
|
+
|
|
9
37
|
## 5.5.7
|
|
10
38
|
|
|
11
39
|
### Build
|
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
Some of these changes between major application versions may affect the effective configuration that would be applied if you run the application. Therefore to do the upgrade safely please follow this procedure.
|
|
4
4
|
|
|
5
|
+
## From 5.\* to 6.\*
|
|
6
|
+
|
|
7
|
+
We have upgraded to new major versions of underlying YAML libraries `ezyaml`, `yamlpath` and `ruamel.yaml`.
|
|
8
|
+
|
|
9
|
+
This results in GitLabForm no longer defaulting to `yaml 1.1` support when parsing YAML configuration files.
|
|
10
|
+
|
|
11
|
+
In order to retain `yaml 1.1` configuration you **must** add the `%YAML` directive to the top of your configuration file, for example:
|
|
12
|
+
|
|
13
|
+
```yaml
|
|
14
|
+
%YAML 1.1
|
|
15
|
+
---
|
|
16
|
+
projects_and_groups:
|
|
17
|
+
some_group/*:
|
|
18
|
+
project_settings:
|
|
19
|
+
foo: yes
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Alternatively update your YAML to be compatible with `yaml 1.2` specification, for example by replacing `yes` with `true` in the above example, and any instances of `no` with `false`.
|
|
23
|
+
|
|
5
24
|
## From 4.\* to 5.\*
|
|
6
25
|
|
|
7
26
|
### Update `config_version`
|
|
@@ -68,8 +68,9 @@ class GitLabForm:
|
|
|
68
68
|
self.only_sections = "all"
|
|
69
69
|
self.exclude_sections = []
|
|
70
70
|
self.recurse_subgroups = recurse_subgroups
|
|
71
|
+
self.log_level = logging.DEBUG
|
|
71
72
|
|
|
72
|
-
self.
|
|
73
|
+
self._configure_logging()
|
|
73
74
|
else:
|
|
74
75
|
# normal mode
|
|
75
76
|
|
|
@@ -94,7 +95,16 @@ class GitLabForm:
|
|
|
94
95
|
self.recurse_subgroups,
|
|
95
96
|
) = self._parse_args()
|
|
96
97
|
|
|
97
|
-
self.
|
|
98
|
+
if self.debug:
|
|
99
|
+
level = logging.DEBUG
|
|
100
|
+
elif self.verbose:
|
|
101
|
+
level = logging.INFO
|
|
102
|
+
else:
|
|
103
|
+
level = logging.WARNING
|
|
104
|
+
|
|
105
|
+
self.log_level = level
|
|
106
|
+
|
|
107
|
+
self._configure_logging()
|
|
98
108
|
|
|
99
109
|
self._show_version(self.skip_version_check)
|
|
100
110
|
if self.just_show_version:
|
|
@@ -108,7 +118,7 @@ class GitLabForm:
|
|
|
108
118
|
|
|
109
119
|
self.application_processors = ApplicationProcessors(self.gitlab, self.configuration, self.strict)
|
|
110
120
|
self.group_processors = GroupProcessors(self.gitlab, self.configuration, self.strict)
|
|
111
|
-
self.project_processors = ProjectProcessors(self.gitlab, self.configuration, self.strict)
|
|
121
|
+
self.project_processors = ProjectProcessors(self.gitlab, self.configuration, self.strict, self.log_level)
|
|
112
122
|
self.groups_provider = GroupsProvider(
|
|
113
123
|
self.gitlab,
|
|
114
124
|
self.configuration,
|
|
@@ -323,27 +333,24 @@ class GitLabForm:
|
|
|
323
333
|
args.recurse_subsgroups,
|
|
324
334
|
)
|
|
325
335
|
|
|
326
|
-
def
|
|
336
|
+
def _configure_logging(self) -> None:
|
|
327
337
|
"""
|
|
328
338
|
Configures the application output using logging, based on debug and verbose flags:
|
|
329
339
|
|
|
330
340
|
* normal mode - logging only WARNING logs
|
|
331
341
|
* verbose mode - logging INFO level and above
|
|
332
342
|
* debug / tests mode - logging DEBUG level and above, along with rich exception tracebacks (may expose secrets)
|
|
333
|
-
|
|
334
|
-
:param tests: True if we are running in tests mode
|
|
335
343
|
"""
|
|
336
344
|
rich_tracebacks = False
|
|
337
|
-
|
|
338
|
-
|
|
345
|
+
|
|
346
|
+
if self.log_level == logging.DEBUG:
|
|
339
347
|
rich_tracebacks = True
|
|
340
|
-
elif self.verbose:
|
|
341
|
-
level = logging.INFO
|
|
342
|
-
else:
|
|
343
|
-
level = logging.WARNING
|
|
344
348
|
|
|
345
349
|
logging.basicConfig(
|
|
346
|
-
level=
|
|
350
|
+
level=self.log_level,
|
|
351
|
+
format="%(message)s",
|
|
352
|
+
datefmt="[%X]",
|
|
353
|
+
handlers=[RichHandler(rich_tracebacks=rich_tracebacks)],
|
|
347
354
|
)
|
|
348
355
|
|
|
349
356
|
def _initialize_configuration_and_gitlab(self) -> Tuple[GitLab, Configuration]:
|
|
@@ -362,7 +369,7 @@ class GitLabForm:
|
|
|
362
369
|
gitlab = GitLab(config_path=self.config)
|
|
363
370
|
configuration = gitlab.get_configuration()
|
|
364
371
|
|
|
365
|
-
configuration_transformers = ConfigurationTransformers(gitlab)
|
|
372
|
+
configuration_transformers = ConfigurationTransformers(gitlab, self.log_level)
|
|
366
373
|
configuration_transformers.transform(configuration)
|
|
367
374
|
|
|
368
375
|
except ConfigFileNotFoundException as e:
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import fnmatch
|
|
1
2
|
import sys
|
|
2
3
|
from typing import Any
|
|
3
4
|
|
|
@@ -93,10 +94,6 @@ class ConfigurationCore(ABC):
|
|
|
93
94
|
|
|
94
95
|
yaml = Parsers.get_yaml_editor()
|
|
95
96
|
|
|
96
|
-
# for better backward compatibility with PyYAML (that supports only YAML 1.1) used in the previous
|
|
97
|
-
# GitLabForm versions, let's force ruamel.yaml to use YAML version 1.1 by default too
|
|
98
|
-
yaml.version = (1, 1)
|
|
99
|
-
|
|
100
97
|
if config_string:
|
|
101
98
|
config = textwrap.dedent(source)
|
|
102
99
|
info("Reading config from the provided string.")
|
|
@@ -215,6 +212,26 @@ class ConfigurationCore(ABC):
|
|
|
215
212
|
|
|
216
213
|
return dict(merged_dict)
|
|
217
214
|
|
|
215
|
+
@staticmethod
|
|
216
|
+
def _get_key_type(a_key: str) -> str:
|
|
217
|
+
"""
|
|
218
|
+
Classify a config key used under 'projects_and_groups'.
|
|
219
|
+
"""
|
|
220
|
+
if a_key == "*":
|
|
221
|
+
return "common"
|
|
222
|
+
if a_key.endswith("/*"):
|
|
223
|
+
return "group"
|
|
224
|
+
if "*" in a_key:
|
|
225
|
+
return "project_pattern"
|
|
226
|
+
return "project"
|
|
227
|
+
|
|
228
|
+
@staticmethod
|
|
229
|
+
def _match_pattern(pattern: str, item: str) -> bool:
|
|
230
|
+
"""
|
|
231
|
+
Case-insensitive fnmatch between pattern and item.
|
|
232
|
+
"""
|
|
233
|
+
return fnmatch.fnmatchcase(item.lower(), pattern.lower())
|
|
234
|
+
|
|
218
235
|
@staticmethod
|
|
219
236
|
def _get_case_insensitively(a_dict: dict, a_key: str):
|
|
220
237
|
for dict_key in a_dict.keys():
|
|
@@ -235,6 +252,7 @@ class ConfigurationCore(ABC):
|
|
|
235
252
|
if list_element == item:
|
|
236
253
|
return True
|
|
237
254
|
|
|
255
|
+
# Support for traditional group/* skip pattern
|
|
238
256
|
if (
|
|
239
257
|
list_element.endswith("/*")
|
|
240
258
|
and item.startswith(list_element[:-2])
|
|
@@ -242,6 +260,10 @@ class ConfigurationCore(ABC):
|
|
|
242
260
|
):
|
|
243
261
|
return True
|
|
244
262
|
|
|
263
|
+
# Support for partial wildcard patterns like group/foo-*
|
|
264
|
+
if "*" in list_element and fnmatch.fnmatchcase(item, list_element):
|
|
265
|
+
return True
|
|
266
|
+
|
|
245
267
|
return False
|
|
246
268
|
|
|
247
269
|
def _find_almost_duplicates(self):
|
|
@@ -15,14 +15,14 @@ class ConfigurationProjects(ConfigurationGroups, ABC):
|
|
|
15
15
|
depends on the groups (and common) configuration.
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
|
-
def get_projects(self) -> list:
|
|
18
|
+
def get_projects(self, key_type: str = "project") -> list:
|
|
19
19
|
"""
|
|
20
20
|
:return: sorted list of projects names, that are EXPLICITLY defined in the config
|
|
21
21
|
"""
|
|
22
22
|
projects = []
|
|
23
23
|
projects_and_groups = self.get("projects_and_groups")
|
|
24
24
|
for element in projects_and_groups.keys():
|
|
25
|
-
if element
|
|
25
|
+
if self._get_key_type(element) == key_type:
|
|
26
26
|
projects.append(element)
|
|
27
27
|
return sorted(projects)
|
|
28
28
|
|
|
@@ -68,6 +68,27 @@ class ConfigurationProjects(ConfigurationGroups, ABC):
|
|
|
68
68
|
"""
|
|
69
69
|
:param group_and_project: 'group/project'
|
|
70
70
|
:return: configuration for this project or empty dict if not defined,
|
|
71
|
-
ignoring the case
|
|
71
|
+
ignoring the case. Also checks for matching project patterns.
|
|
72
72
|
"""
|
|
73
|
-
|
|
73
|
+
projects_and_groups = self.get("projects_and_groups")
|
|
74
|
+
|
|
75
|
+
# 1. Exact match
|
|
76
|
+
exact = self._get_case_insensitively(projects_and_groups, group_and_project)
|
|
77
|
+
if exact:
|
|
78
|
+
return exact
|
|
79
|
+
|
|
80
|
+
# 2. Best matching pattern
|
|
81
|
+
matches = []
|
|
82
|
+
for key in projects_and_groups.keys():
|
|
83
|
+
if self._get_key_type(key) == "project_pattern":
|
|
84
|
+
if self._match_pattern(key, group_and_project):
|
|
85
|
+
# store a 3-tuple for later sorting
|
|
86
|
+
prefix = key.split("*", 1)[0]
|
|
87
|
+
matches.append((len(prefix), -key.count("*"), key))
|
|
88
|
+
if matches:
|
|
89
|
+
# longest literal prefix wins; tie -> fewer wildcards; still tie -> first appearance
|
|
90
|
+
matches.sort(reverse=True)
|
|
91
|
+
best_key = matches[0][2]
|
|
92
|
+
return self._get_case_insensitively(projects_and_groups, best_key)
|
|
93
|
+
|
|
94
|
+
return {}
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import sys
|
|
2
|
-
from logging import debug, info, critical
|
|
2
|
+
from logging import debug, info, critical, DEBUG
|
|
3
3
|
from abc import ABC, abstractmethod
|
|
4
|
-
from ez_yaml import ez_yaml
|
|
5
|
-
from ruamel.yaml import YAML
|
|
6
4
|
from types import SimpleNamespace
|
|
7
|
-
|
|
8
5
|
from ruamel.yaml.comments import CommentedMap
|
|
9
6
|
from yamlpath import Processor
|
|
10
7
|
from yamlpath.exceptions import YAMLPathException
|
|
11
8
|
from yamlpath.wrappers import ConsolePrinter
|
|
12
9
|
|
|
10
|
+
from gitlabform import util
|
|
13
11
|
from gitlabform.constants import EXIT_INVALID_INPUT, APPROVAL_RULE_NAME
|
|
14
12
|
from gitlabform.configuration import Configuration
|
|
15
13
|
from gitlabform.gitlab import AccessLevel
|
|
@@ -24,23 +22,26 @@ from gitlabform.gitlab import GitLab
|
|
|
24
22
|
|
|
25
23
|
|
|
26
24
|
class ConfigurationTransformers:
|
|
27
|
-
def __init__(self, gitlab: GitLab):
|
|
25
|
+
def __init__(self, gitlab: GitLab, log_level: int):
|
|
28
26
|
self.user_transformer = UserTransformer(gitlab)
|
|
29
27
|
self.group_transformer = GroupTransformer(gitlab)
|
|
30
28
|
self.implicit_name_transformer = ImplicitNameTransformer(gitlab)
|
|
31
29
|
self.access_level_transformer = AccessLevelsTransformer(gitlab)
|
|
30
|
+
self.log_level = log_level
|
|
32
31
|
|
|
33
32
|
def transform(self, configuration: Configuration) -> None:
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
if self.log_level == DEBUG:
|
|
34
|
+
config_before = util.yaml_config_to_string(configuration.config)
|
|
35
|
+
debug(f"Config BEFORE transformations:\n{config_before}")
|
|
36
36
|
|
|
37
37
|
self.user_transformer.transform(configuration)
|
|
38
38
|
self.group_transformer.transform(configuration)
|
|
39
39
|
self.implicit_name_transformer.transform(configuration)
|
|
40
40
|
self.access_level_transformer.transform(configuration, last=True)
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
if self.log_level == DEBUG:
|
|
43
|
+
config_after = util.yaml_config_to_string(configuration.config)
|
|
44
|
+
debug(f"Config AFTER transformations:\n{config_after}")
|
|
44
45
|
|
|
45
46
|
|
|
46
47
|
class ConfigurationTransformer(ABC):
|
|
@@ -58,9 +59,9 @@ class ConfigurationTransformer(ABC):
|
|
|
58
59
|
# we needed complex ruamel.yaml's types like ordereddict and CommentedSeq
|
|
59
60
|
# for transformations, but at the end convert them to simple dict and lists
|
|
60
61
|
# for easier to understand debug output and tests
|
|
62
|
+
config_yaml_string = util.yaml_config_to_string(configuration.config)
|
|
61
63
|
|
|
62
|
-
|
|
63
|
-
simple_yaml_loader = YAML(typ="safe", pure=True)
|
|
64
|
+
simple_yaml_loader = util.configure_ruamel_yaml_loader(typ="safe", pure=True)
|
|
64
65
|
configuration.config = simple_yaml_loader.load(config_yaml_string)
|
|
65
66
|
|
|
66
67
|
|
|
@@ -37,6 +37,9 @@ class GitLabCore:
|
|
|
37
37
|
gitlab_config_from_file = self.configuration.get("gitlab", {})
|
|
38
38
|
self.gitlab_config = {**default_gitlab_config, **gitlab_config_from_file}
|
|
39
39
|
|
|
40
|
+
if self.gitlab_config["url"]:
|
|
41
|
+
self.gitlab_config["url"] = self.gitlab_config["url"].rstrip("/")
|
|
42
|
+
|
|
40
43
|
self.session = requests.Session()
|
|
41
44
|
|
|
42
45
|
retries_status_forcelist = []
|
|
@@ -96,6 +96,13 @@ class ProjectsProvider(GroupsProvider):
|
|
|
96
96
|
# defined in the configuration, but we don't need to re-check for
|
|
97
97
|
# being archived or scheduled for deletion projects that we already got from groups
|
|
98
98
|
|
|
99
|
+
for resolved_project in self._resolve_project_patterns(groups.get_effective()):
|
|
100
|
+
if (
|
|
101
|
+
resolved_project not in projects.requested
|
|
102
|
+
and resolved_project not in projects_from_configuration_not_from_groups
|
|
103
|
+
):
|
|
104
|
+
projects_from_configuration_not_from_groups.append(resolved_project)
|
|
105
|
+
|
|
99
106
|
(
|
|
100
107
|
archived_projects_from_configuration_not_from_groups,
|
|
101
108
|
scheduled_for_deletion_projects_from_configuration_not_from_groups,
|
|
@@ -198,6 +205,42 @@ class ProjectsProvider(GroupsProvider):
|
|
|
198
205
|
|
|
199
206
|
return skipped
|
|
200
207
|
|
|
208
|
+
def _resolve_project_patterns(self, effective_groups: list) -> list:
|
|
209
|
+
"""
|
|
210
|
+
Resolve project patterns from config to concrete project paths.
|
|
211
|
+
Only resolves patterns whose parent group is not already in the effective groups list
|
|
212
|
+
to avoid redundant API calls.
|
|
213
|
+
"""
|
|
214
|
+
pattern_projects = []
|
|
215
|
+
for pattern in self.configuration.get_projects("project_pattern"):
|
|
216
|
+
pattern_group = pattern.rsplit("/", 1)[0]
|
|
217
|
+
if pattern_group in effective_groups:
|
|
218
|
+
# projects from this group already fetched through normal means
|
|
219
|
+
continue
|
|
220
|
+
|
|
221
|
+
try:
|
|
222
|
+
maybe_group = self.gitlab.get_group_case_insensitive(pattern_group)
|
|
223
|
+
group_path = maybe_group["full_path"]
|
|
224
|
+
except NotFoundException:
|
|
225
|
+
debug(
|
|
226
|
+
"Group '%s' for pattern '%s' not found in GitLab, skipping",
|
|
227
|
+
pattern_group,
|
|
228
|
+
pattern,
|
|
229
|
+
)
|
|
230
|
+
continue
|
|
231
|
+
|
|
232
|
+
try:
|
|
233
|
+
all_project_objects = self.gitlab.get_projects(group_path, include_archived=True, only_names=False)
|
|
234
|
+
except NotFoundException:
|
|
235
|
+
all_project_objects = []
|
|
236
|
+
|
|
237
|
+
for project_object in all_project_objects:
|
|
238
|
+
path = project_object["path_with_namespace"]
|
|
239
|
+
if self.configuration._match_pattern(pattern, path) and path not in pattern_projects:
|
|
240
|
+
pattern_projects.append(path)
|
|
241
|
+
|
|
242
|
+
return pattern_projects
|
|
243
|
+
|
|
201
244
|
def _get_project_transfer_source_from_config(self, project: str) -> Optional[str]:
|
|
202
245
|
try:
|
|
203
246
|
debug(
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import sys
|
|
2
2
|
from logging import debug, critical, info
|
|
3
3
|
|
|
4
|
-
import
|
|
5
|
-
|
|
4
|
+
from gitlabform import util
|
|
6
5
|
from gitlabform.constants import EXIT_INVALID_INPUT, EXIT_PROCESSING_ERROR
|
|
7
6
|
|
|
8
7
|
|
|
@@ -38,7 +37,7 @@ class EffectiveConfigurationFile:
|
|
|
38
37
|
def write_to_file(self):
|
|
39
38
|
if self.output_file:
|
|
40
39
|
try:
|
|
41
|
-
yaml_configuration =
|
|
40
|
+
yaml_configuration = util.yaml_config_to_string(self.config)
|
|
42
41
|
self.output_file.write(yaml_configuration)
|
|
43
42
|
self.output_file.close()
|
|
44
43
|
except Exception as e:
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from logging import debug
|
|
2
|
+
from typing import List
|
|
3
|
+
|
|
4
|
+
from gitlabform.gitlab import GitLab
|
|
5
|
+
from gitlab.v4.objects import Group, GroupSAMLGroupLink
|
|
6
|
+
from gitlabform.processors.abstract_processor import AbstractProcessor
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class GroupSAMLLinksProcessor(AbstractProcessor):
|
|
10
|
+
|
|
11
|
+
def __init__(self, gitlab: GitLab):
|
|
12
|
+
super().__init__("group_saml_links", gitlab)
|
|
13
|
+
|
|
14
|
+
def _process_configuration(self, group_path_and_name: str, configuration: dict) -> None:
|
|
15
|
+
"""Process the SAML links configuration for a group."""
|
|
16
|
+
|
|
17
|
+
configured_section = configuration.get("group_saml_links", {}) or {}
|
|
18
|
+
enforce_links = configuration.get("group_saml_links|enforce", False)
|
|
19
|
+
|
|
20
|
+
configured_links = {k: v for k, v in configured_section.items() if k != "enforce"}
|
|
21
|
+
|
|
22
|
+
group: Group = self.gl.get_group_by_path_cached(group_path_and_name)
|
|
23
|
+
existing_links: List[GroupSAMLGroupLink] = group.saml_group_links.list(get_all=True)
|
|
24
|
+
existing_by_name = {link.name: link for link in existing_links}
|
|
25
|
+
|
|
26
|
+
for _, link_configuration in configured_links.items():
|
|
27
|
+
saml_group_name = link_configuration.get("saml_group_name")
|
|
28
|
+
existing_link = existing_by_name.get(saml_group_name)
|
|
29
|
+
|
|
30
|
+
if existing_link is None:
|
|
31
|
+
group.saml_group_links.create(link_configuration)
|
|
32
|
+
continue
|
|
33
|
+
|
|
34
|
+
# GitLab API's request and response attributes differs when SAML link is created vs retrieved.
|
|
35
|
+
# On creation or retrieving a link, attribute is called `saml_group_name` but and returned response is 'name' instead.
|
|
36
|
+
# To compare existing link (retrieved from GitLab) with configured link, link configuration need to replace `saml_group_name` with `name`
|
|
37
|
+
expected = {**link_configuration, "name": saml_group_name}
|
|
38
|
+
expected.pop("saml_group_name", None)
|
|
39
|
+
|
|
40
|
+
if self._needs_update(existing_link.asdict(), expected):
|
|
41
|
+
# GitLab API has no update endpoint for SAML links; delete and recreate instead.
|
|
42
|
+
debug(f"Updating SAML link: {saml_group_name} with {link_configuration}")
|
|
43
|
+
existing_link.delete()
|
|
44
|
+
group.saml_group_links.create(link_configuration)
|
|
45
|
+
|
|
46
|
+
if enforce_links:
|
|
47
|
+
self._delete_extra_links(group, existing_links, configured_links)
|
|
48
|
+
|
|
49
|
+
def _delete_extra_links(
|
|
50
|
+
self,
|
|
51
|
+
group: Group,
|
|
52
|
+
existing: List[GroupSAMLGroupLink],
|
|
53
|
+
configured: dict,
|
|
54
|
+
) -> None:
|
|
55
|
+
"""Delete any SAML links that are not in the configuration."""
|
|
56
|
+
known_names = {link["saml_group_name"] for link in configured.values()}
|
|
57
|
+
|
|
58
|
+
for link in existing:
|
|
59
|
+
if link.name not in known_names:
|
|
60
|
+
debug(f"Deleting extra SAML link: {link.name}")
|
|
61
|
+
group.saml_group_links.delete(link.name)
|
|
@@ -47,7 +47,7 @@ from gitlabform.processors.project.remote_mirrors_processor import RemoteMirrors
|
|
|
47
47
|
|
|
48
48
|
|
|
49
49
|
class ProjectProcessors(AbstractProcessors):
|
|
50
|
-
def __init__(self, gitlab: GitLab, config: Configuration, strict: bool):
|
|
50
|
+
def __init__(self, gitlab: GitLab, config: Configuration, strict: bool, log_level: int):
|
|
51
51
|
super().__init__(gitlab, config, strict)
|
|
52
52
|
self.processors: List[AbstractProcessor] = [
|
|
53
53
|
# Order of processors matter. GitLabForm will process config sections
|
|
@@ -64,7 +64,7 @@ class ProjectProcessors(AbstractProcessors):
|
|
|
64
64
|
ProjectLabelsProcessor(gitlab),
|
|
65
65
|
JobTokenScopeProcessor(gitlab),
|
|
66
66
|
DeployKeysProcessor(gitlab),
|
|
67
|
-
ProjectVariablesProcessor(gitlab),
|
|
67
|
+
ProjectVariablesProcessor(gitlab, log_level),
|
|
68
68
|
BranchesProcessor(gitlab, strict),
|
|
69
69
|
TagsProcessor(gitlab, strict),
|
|
70
70
|
IntegrationsProcessor(gitlab),
|
|
@@ -24,6 +24,19 @@ class FilesProcessor(AbstractProcessor):
|
|
|
24
24
|
self.strict = strict
|
|
25
25
|
self.branch_processor = BranchesProcessor(gitlab, strict)
|
|
26
26
|
|
|
27
|
+
def _can_proceed(self, project_or_group: str, configuration: dict):
|
|
28
|
+
for file in sorted(configuration["files"]):
|
|
29
|
+
file_config = configuration["files"][file]
|
|
30
|
+
if "branches" not in file_config:
|
|
31
|
+
critical(
|
|
32
|
+
f"File '{file}' is missing the required 'branches' key."
|
|
33
|
+
f" Specify which branches to apply the file to using one of:"
|
|
34
|
+
f" 'all', 'protected', or a list of branch names (e.g. ['main'])."
|
|
35
|
+
)
|
|
36
|
+
sys.exit(EXIT_INVALID_INPUT)
|
|
37
|
+
|
|
38
|
+
return True
|
|
39
|
+
|
|
27
40
|
def _process_configuration(self, project_and_group: str, configuration: dict):
|
|
28
41
|
for file in sorted(configuration["files"]):
|
|
29
42
|
project: Project = self.gl.get_project_by_path_cached(project_and_group)
|
{gitlabform-5.5.7 → gitlabform-6.1.0}/gitlabform/processors/project/project_variables_processor.py
RENAMED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
from typing import Dict, Any, List, cast
|
|
2
|
-
from logging import warning, info
|
|
2
|
+
from logging import warning, info, INFO
|
|
3
3
|
|
|
4
4
|
import copy
|
|
5
5
|
import textwrap
|
|
6
|
-
import ez_yaml
|
|
7
6
|
|
|
8
7
|
from gitlab.exceptions import GitlabGetError
|
|
9
8
|
from gitlab.v4.objects import Project, ProjectVariable
|
|
9
|
+
|
|
10
|
+
from gitlabform import util
|
|
10
11
|
from gitlabform.gitlab import GitLab
|
|
11
12
|
from gitlabform.processors.util.difference_logger import hide
|
|
12
13
|
from gitlabform.processors.abstract_processor import AbstractProcessor
|
|
@@ -14,8 +15,9 @@ from gitlabform.processors.util.variables_processor import VariablesProcessor
|
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
class ProjectVariablesProcessor(AbstractProcessor):
|
|
17
|
-
def __init__(self, gitlab: GitLab):
|
|
18
|
+
def __init__(self, gitlab: GitLab, log_level: int):
|
|
18
19
|
super().__init__("variables", gitlab)
|
|
20
|
+
self.log_level = log_level
|
|
19
21
|
self._variables_processor = VariablesProcessor(self._needs_update)
|
|
20
22
|
|
|
21
23
|
def _process_configuration(self, project_and_group: str, configuration: Dict[str, Any]) -> None:
|
|
@@ -65,12 +67,14 @@ class ProjectVariablesProcessor(AbstractProcessor):
|
|
|
65
67
|
variables_list.append(var_dict)
|
|
66
68
|
|
|
67
69
|
info(f"Variables for {project_and_group} in GitLab:")
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
if self.log_level == INFO:
|
|
71
|
+
yaml = util.yaml_config_to_string(variables_list)
|
|
72
|
+
info(
|
|
73
|
+
textwrap.indent(
|
|
74
|
+
yaml,
|
|
75
|
+
" ",
|
|
76
|
+
)
|
|
72
77
|
)
|
|
73
|
-
)
|
|
74
78
|
except GitlabGetError:
|
|
75
79
|
info(f"Variables for {project_and_group} in GitLab cannot be checked.")
|
|
76
80
|
|
|
@@ -86,9 +90,11 @@ class ProjectVariablesProcessor(AbstractProcessor):
|
|
|
86
90
|
for key in configured_variables.keys():
|
|
87
91
|
configured_variables[key]["value"] = hide(configured_variables[key]["value"])
|
|
88
92
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
+
if self.log_level == INFO:
|
|
94
|
+
yaml = util.yaml_config_to_string(configured_variables)
|
|
95
|
+
info(
|
|
96
|
+
textwrap.indent(
|
|
97
|
+
"",
|
|
98
|
+
" ",
|
|
99
|
+
)
|
|
93
100
|
)
|
|
94
|
-
)
|