diff --git a/gitlab/manage_projects.py b/gitlab/manage_projects.py new file mode 100644 --- /dev/null +++ b/gitlab/manage_projects.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 + +import logging +from typing import Dict + +import click +import yaml + +import gitlab + +logger = logging.getLogger(__name__) + + +def get_gitlab(gitlab_instance): + gl = gitlab.Gitlab.from_config(gitlab_instance) + gl.auth() + + return gl + + +def load_projects_configuration(projects_file: str) -> Dict: + with open(projects_file, "r") as f: + data = yaml.safe_load(f) + + return data + + +@click.command() +@click.option( + "--gitlab", + "-g", + "gitlab_instance", + help="Which GitLab instance to use, as configured in the python-gitlab config", +) +@click.option( + "--do-it", "do_it", is_flag=True, help="Actually perform the operations", +) +@click.argument("projects_file") +def cli(gitlab_instance: str, do_it: bool, projects_file: str,) -> None: + gl = get_gitlab(gitlab_instance) + + configuration = load_projects_configuration(projects_file) + + # Global configuration for all projects + global_settings = configuration["global_settings"] + + # Managed Projects (which can override global configuration) + project_path_with_namespaces = configuration["projects"] + + # Load the list of all projects + projects = {} + for project in gl.projects.list(iterator=True): + projects[project.path_with_namespace] = project + + # Now configure the project according to global settings + # (and the potential specific project override) + for path_with_namespace, project_settings in project_path_with_namespaces.items(): + # Retrieve the gitlab project reference + project = projects.get(path_with_namespace) + + # Skip non configured project + if not project: + continue + + print("Project: ", project.path_with_namespace, project.id) + updated = False + + # override with specific settings + config = {**global_settings, **project_settings} + logger.debug("Merged configuration", config) + + # Iterate over the new settings to apply + for attribute, value in config.items(): + existing_value = getattr(project, attribute) + # If any changes is detected + if existing_value != value: + # New settings is applied + setattr(project, attribute, value) + new_value = getattr(project, attribute) + print(f"Update '{attribute}' '{existing_value}' -> '{new_value}'") + updated = True + + if updated and do_it: + project.save() + + prefix_msg = "(**DRY RUN**) " if not do_it else "" + msg = f"{prefix_msg}Number of projects updated: {len(projects)}" + print(msg) + + +if __name__ == "__main__": + logging.basicConfig( + level=logging.INFO, format="%(asctime)s %(name)s:%(levelname)s %(message)s" + ) + cli(auto_envvar_prefix="SWH") diff --git a/gitlab/projects.yml b/gitlab/projects.yml new file mode 100644 --- /dev/null +++ b/gitlab/projects.yml @@ -0,0 +1,191 @@ +global_settings: + # string (optional): Set the merge method used. + merge_method: ff + # string (optional): One of disabled, private, or enabled. + # releases_access_level: enabled + # boolean (optional): Enable Delete source branch option by default for all new merge + # requests. + remove_source_branch_after_merge: true + # string (optional): See project visibility level. + visibility: public + # boolean: Set whether or not merge requests can be merged with skipped jobs. + # allow_merge_on_skipped_pipeline: false + # string: One of disabled, private or enabled + # analytics_access_level: enabled + # boolean: Enable Auto DevOps for this project. + # auto_devops_enabled: false + # boolean: Set whether auto-closing referenced issues on default branch. + # autoclose_referenced_issues: true + # string (optional): The Git strategy. Defaults to fetch. + # build_git_strategy: fetch + # integer (optional): The maximum amount of time, in seconds, that a job can run. + # build_timeout: 3600 + # string (optional): One of disabled, private, or enabled. + # builds_access_level: enabled + # string (optional): The path to CI configuration file. + # ci_config_path: "" + # integer (optional): Default number of revisions for shallow cloning. + # ci_default_git_depth: 20 + # boolean (optional): Enable or disable prevent outdated deployment jobs. + # ci_forward_deployment_enabled: true + # boolean (optional): Enable or disable running pipelines in the parent project for + # merge requests from forks. (Introduced in GitLab 15.3.) + # ci_allow_fork_pipelines_to_run_in_parent_project: true + # boolean (optional): Set whether or not caches should be separated by branch + # protection status. + # ci_separated_caches: true + # string (optional): Set visibility of container registry, for this project, to one of + # disabled, private or enabled. + # container_registry_access_level: disabled + # string (optional): The default branch name. + # default_branch: master + # string (optional): Short project description. + # description: "" + # boolean (optional): Disable email notifications. + # emails_disabled: true + # boolean (optional): Enforce auth checks on uploads. + # enforce_auth_checks_on_uploads: true + # string (optional): One of disabled, private, or enabled. + # forking_access_level: enabled + # string (optional): One of disabled, private, or enabled. + # issues_access_level: enabled + # boolean (optional): Disable or enable the ability to keep the latest artifact for + # this project. + # keep_latest_artifact: True + # boolean (optional): Enable LFS. + # lfs_enabled: True + # string (optional): Template used to create merge commit message in merge requests. + # (Introduced in GitLab 14.5.) + # merge_commit_template: "" + # string (optional): One of disabled, private, or enabled. + # merge_requests_access_level: enabled + # boolean (optional): Set whether merge requests can only be merged when all the + # discussions are resolved. + # only_allow_merge_if_all_discussions_are_resolved: False + # boolean (optional): Set whether merge requests can only be merged with successful + # jobs. + # only_allow_merge_if_pipeline_succeeds: False + # string (optional): One of disabled, private, or enabled. + # operations_access_level: disabled + # boolean (optional): Enable or disable packages repository feature. + # packages_enabled: False + # string (optional): One of disabled, private, enabled, or public. + # pages_access_level: disabled + # boolean (optional): Show link to create/view merge request when pushing from the + # command line. + # printing_merge_request_link_enabled: true + # string (optional): One of disabled, private, or enabled. + # repository_access_level: enabled + # string (optional): Which storage shard the repository is on. (administrators only) + # repository_storage: default + # boolean (optional): Allow users to request member access. + # request_access_enabled: true + # boolean (optional): Automatically resolve merge request diffs discussions on lines + # changed with a push. + # resolve_outdated_diff_discussions: false + # boolean (optional): Allow only users with the Maintainer role to pass user-defined + # variables when triggering a pipeline. For example when the pipeline is triggered in + # the UI, with the API, or by a trigger token. + # restrict_user_defined_variables: false + # string (optional): (GitLab 14.9 and later) Security and compliance access level. One + # of disabled, private, or enabled. + # security_and_compliance_access_level: private + # boolean (optional): Enable or disable Service Desk feature. + # service_desk_enabled: false + # boolean (optional): Enable shared runners for this project. + # shared_runners_enabled: true + # string (optional): One of disabled, private, or enabled. + # snippets_access_level: enabled + # string (optional): Template used to create squash commit message in merge requests. + # (Introduced in GitLab 14.6.) + # squash_commit_template: "" + # string (optional): One of never, always, default_on, or default_off. + # squash_option: default_on + # string (optional): The commit message used to apply merge request suggestions. + # suggestion_commit_message: "" + # string (optional): One of disabled, private, or enabled. + # wiki_access_level: enabled + +projects: + infra/getsentry-onpremise: {} + infra/swh-ansible: {} + infra/swh-apps: {} + # swh-grafana-dashboards: {} + # swh-jenkins-jobs: {} + # swh-jenkins-dockerfiles: {} + # swh-jenkins-library: {} + infra/swh-sysadmin-provisioning: {} + infra/ci-cd/k8s-swh-private-data: + visibility: private + infra/ci-cd/swh-charts: {} + infra/ci-cd/k8s-clusters-conf: {} + infra/puppet/puppet-environment: {} + infra/puppet/puppet-swh-dar: {} + infra/puppet/puppet-swh-gunicorn: {} + infra/puppet/puppet-swh-mediawiki: {} + infra/puppet/puppet-swh-postfix: {} + infra/puppet/puppet-swh-private-data-censored: {} + infra/puppet/puppet-swh-profile: {} + infra/puppet/puppet-swh-role: {} + infra/puppet/puppet-swh-site: {} + infra/puppet/puppet-swh-systemd: {} + infra/puppet/puppet-swh-uwsgi: {} + infra/puppet/puppet-swh-private-data: + visibility: private + infra/puppet/3rdparty/puppet-arioch-redis: {} + infra/puppet/3rdparty/puppet-camptocamp-systemd: {} + infra/puppet/3rdparty/puppet-claranet-varnish: {} + infra/puppet/3rdparty/puppet-covermymeds-pgbouncer: {} + infra/puppet/3rdparty/puppet-deric-zookeeper: {} + infra/puppet/3rdparty/puppet-elastic-elastic_stack: {} + infra/puppet/3rdparty/puppet-elastic-elasticsearch: {} + infra/puppet/3rdparty/puppet-icinga-icinga2: {} + infra/puppet/3rdparty/puppet-icinga-icingaweb2: {} + infra/puppet/3rdparty/puppet-inkblot-bind: {} + infra/puppet/3rdparty/puppet-mosen-cups: {} + infra/puppet/3rdparty/puppet-openstack-ceph: {} + infra/puppet/3rdparty/puppet-puppet-archive: {} + infra/puppet/3rdparty/puppet-puppet-cassandra: {} + infra/puppet/3rdparty/puppet-puppet-extlib: {} + infra/puppet/3rdparty/puppet-puppet-grafana: {} + infra/puppet/3rdparty/puppet-puppet-kafka: {} + infra/puppet/3rdparty/puppet-puppet-letsencrypt: {} + infra/puppet/3rdparty/puppet-puppet-nginx: {} + infra/puppet/3rdparty/puppet-puppet-php: {} + infra/puppet/3rdparty/puppet-puppet-prometheus: {} + infra/puppet/3rdparty/puppet-puppet-rabbitmq: {} + infra/puppet/3rdparty/puppet-puppet-redis: {} + infra/puppet/3rdparty/puppet-puppet-unattended_upgrades: {} + infra/puppet/3rdparty/puppet-puppetlabs-apache: {} + infra/puppet/3rdparty/puppet-puppetlabs-apt: {} + infra/puppet/3rdparty/puppet-puppetlabs-concat: {} + infra/puppet/3rdparty/puppet-puppetlabs-docker: {} + infra/puppet/3rdparty/puppet-puppetlabs-inifile: {} + infra/puppet/3rdparty/puppet-puppetlabs-java: {} + infra/puppet/3rdparty/puppet-puppetlabs-java_ks: {} + infra/puppet/3rdparty/puppet-puppetlabs-mysql: {} + infra/puppet/3rdparty/puppet-puppetlabs-ntp: {} + infra/puppet/3rdparty/puppet-puppetlabs-postgresql: {} + infra/puppet/3rdparty/puppet-puppetlabs-puppetdb: {} + infra/puppet/3rdparty/puppet-puppetlabs-stdlib: {} + infra/puppet/3rdparty/puppet-puppetlabs-translate: {} + infra/puppet/3rdparty/puppet-puppetlabs-vcsrepo: {} + infra/puppet/3rdparty/puppet-puppetlabs-zfs_core: {} + infra/puppet/3rdparty/puppet-richardc-datacat: {} + infra/puppet/3rdparty/puppet-ripienaar-module_data: {} + infra/puppet/3rdparty/puppet-rlenglet-debconf_package: {} + infra/puppet/3rdparty/puppet-saz-locales: {} + infra/puppet/3rdparty/puppet-saz-memcached: {} + infra/puppet/3rdparty/puppet-saz-resolv_conf: {} + infra/puppet/3rdparty/puppet-saz-ssh: {} + infra/puppet/3rdparty/puppet-saz-sudo: {} + infra/puppet/3rdparty/puppet-saz-timezone: {} + infra/puppet/3rdparty/puppet-ssm-hitch: {} + infra/puppet/3rdparty/puppet-ssm-munin: {} + infra/puppet/3rdparty/puppet-stm-debconf: {} + infra/puppet/3rdparty/puppet-theforeman-puppet: {} + infra/puppet/3rdparty/puppet-thias-php: {} + infra/puppet/3rdparty/puppet-trepasi-debnet: {} + infra/puppet/3rdparty/puppet-treydock-keycloak: {} + infra/puppet/3rdparty/puppet-wikimedia-kafka: {} + infra/puppet/3rdparty/puppet-wikimedia-zookeeper: {}