Changeset View
Standalone View
gitlab/manage_projects.py
- This file was added.
#!/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 | |||||
ardumont: dict comprehension! | |||||
# 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 | |||||
olasdUnsubmitted Done Inline ActionsAm I missing something, or would this skip over repos configured with an empty dict? Overall I think we should probably just have a list of namespaces in which all projects are configured unconditionally, rather than having to inline the list of all projects and keeping it up to date? olasd: Am I missing something, or would this skip over repos configured with an empty dict?
Overall I… | |||||
ardumontAuthorUnsubmitted Done Inline Actions
I changed that locally to check against None instead indeed.
If I apply what you mentioned earlier with a 3rd level of configuration, that should work. ardumont: > Am I missing something, or would this skip over repos configured with an empty dict?
I… | |||||
ardumontAuthorUnsubmitted Done Inline Actions
Actually, I did not and somehow it was not an issue ¯\_(ツ)_/¯. ardumont: >> Am I missing something, or would this skip over repos configured with an empty dict?
> I… | |||||
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") |
dict comprehension!