#!/usr/bin/python3
# -*- coding: utf-8 -*-
# pylint: disable=line-too-long
# kate: space-indent on; indent-width 4; replace-tabs on; indent-mode python; remove-trailing-space modified;
# vim: expandtab ts=4
# pylint: enable=line-too-long
############################################################################
# Copyright © 2017-2024 José Manuel Santamaría Lema <panfaust@gmail.com> #
# #
# This program is free software; you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation; either version 2 of the License, or #
# (at your option) any later version. #
############################################################################
"""
This module just provides the KAIronHand class.
"""
from launchpadlib.launchpad import Launchpad
from libka.kagraph.ka_graph import KAGraph
from libka.kagraph.ka_graph_archive_build_status import KAGraphArchiveBuildStatus
from libka.kagraph.ka_graph_ppa_build_status import KAGraphPPABuildStatus
from libka.kagraph.ka_graph_proposed_migration_status import KAGraphProposedMigrationStatus
from libka.ka_data_utils import read_json_data_file
from libka.ka_data_utils import read_bd_relations_file
from libka.kubuntu_ppa import KubuntuPPA
from libka.ka_print import ka_print_good_stuff
from libka.ka_print import ka_print_info
[docs]
class KAIronHand(): # pylint: disable=too-many-instance-attributes
"""
This class is meant to provide one or several graph types for the given release type.
:param release_type: must be `"qt"`, `"frameworks"`, `"plasma"` or `"applications"`
:param version: the version in `ka-metadata/versions.json`
:param archive_name: it can be either `None`, `"staging"` or `"ubuntu"`
- `None` will not retrieve any info from Launchpad (only use it if you just need a `build_depends` graph),
- `"staging"` will retrieve information from the default staging PPA for `release_type`,
- `"archive"` will retrieve information from the Ubuntu archive.
:param dist: is the distribution name: focal, bionic, groovy ...
"""
#FIXME: ↑ and retry builds, but that's not implemented yet
def __init__(self, release_type, version, archive_name, dist):
#Init members
self._archive_name = archive_name
self._release_type = release_type
self._release_type_version = version
self._dist = dist
self._graphs_map = {}
self._graph_types_list = []
self._retry_builds = False
self._retry_builds_force = False
self._arch_list_str = 'any'
self._pm_graph_check_excuses = False
#Init these LP related members to avoid pylint warnings
self._lp = None
self._lpubuntu = None
self._lpseries = None
self._excuses_url = None
self._bd_relation_map = None
self._archive = None
self._all_sources = None
self._all_builds = None
[docs]
def login(self):
"""
Mehod to log in into Launchpad anonymously. This is probably the first method
you should call before doing anything else.
"""
#Print info message
ka_print_info("Log in to Launchpad anonymously...")
#Login into launchpad to check the status of packages
self._lp = Launchpad.login_anonymously('ka-iron-hand (Kubuntu Automation)',
'production', version='devel')
self._lpubuntu = self._lp.distributions["ubuntu"]
self._lpseries = self._lpubuntu.getSeries(name_or_version=self._dist)
[docs]
def login_auth(self):
"""
Method to log into Launchpad, authenticated.
"""
self.login() #FIXME: do the actual thing
[docs]
def set_arch_list(self, arch_list_str):
"""
Sets the architecture list to consider.
"""
self._arch_list_str = arch_list_str
[docs]
def set_pm_graph_check_excuses(self, enabled):
"""
Wether to parse the update excuses to set some package states or not.
"""
self._pm_graph_check_excuses = enabled
[docs]
def set_excuses_url(self, url):
"""
URL for th update excuses web page (if it's parsing is enabled).
"""
self._excuses_url = url
[docs]
def set_graph_types(self, graph_types_list):
"""
Sets the graph types to render, takes as an argument a string list with the
kind of graphs to render, current supported types:
* "build_depends"
* "build_status"
* "proposed_migration"
* "retry_builds"
"""
self._graph_types_list = graph_types_list
[docs]
def set_retry_builds(self, retry_builds, force=False):
"""
Determines if we should retry dep-wait/failed builds or not.
"""
self._retry_builds = retry_builds
self._retry_builds_force = force
def _get_package_info(self):
"""
Gets package information to be used by execute()
"""
#Read the build dependencies relations map
package_bd_relations_file = ("package-bd-relations/%s-%s.json"
% (self._release_type, self._dist))
self._bd_relation_map = read_bd_relations_file(package_bd_relations_file, nodoc=False)
#Find out the archive to work with
if self._archive_name is None:
return
if self._archive_name == "staging":
#Find out the proper ppa from release_type
kppa = KubuntuPPA(self._release_type)
self._archive = self._lp.people[kppa.get_user_name()].getPPAByName(
name=kppa.get_ppa_name())
elif self._archive_name == "ubuntu":
self._archive = self._lpubuntu.main_archive
else:
pass #FIXME: support for custom ppas not implemented yet
#Get the source and builds info
ka_print_info("Getting package information...")
self._all_sources = {}
self._all_builds = {}
number_of_sources = len(self._bd_relation_map)
i = 0
padding = len(str(number_of_sources))
for package in self._bd_relation_map:
i += 1
ka_print_info("(%s of %s) Getting information for package %s"
% (str(i).rjust(padding), number_of_sources, package))
status_to_check = ['Pending', 'Published']
for status in status_to_check:
sources = self._archive.getPublishedSources(distro_series=self._lpseries,
exact_match=True,
status=status,
source_name=package)
self._all_sources[package] = sources
if len(sources) > 0:
break
try:
builds = sources[0].getBuilds()
except IndexError:
continue
self._all_builds[package] = builds
[docs]
def execute(self):
"""
Retrieves all the needed package information from Launchpad and performs
all the operations to get the graphs and/or retry builds.
You can access the resulting graphs with the `get_graphs` method from
this class.
"""
#Get the package information
self._get_package_info()
#Retry builds
if self._retry_builds:
pass #TODO
#Reset the graphs map
self._graphs_map = {}
#Render and store the graphs requested
for graph_type in self._graph_types_list:
ka_print_good_stuff("Rendering %s graph..." % graph_type)
#Create the graph
if graph_type == "build_depends":
graph_title = ("%s %s - %s (build depends graph)"
% (self._release_type, self._release_type_version, self._dist))
kag = KAGraph(graph_title)
elif graph_type == "proposed_migration":
graph_title = ("%s %s - %s (proposed migration status)"
% (self._release_type, self._release_type_version, self._dist))
kag = KAGraphProposedMigrationStatus(title=graph_title, dist=self._dist)
kag.set_check_excuses(self._pm_graph_check_excuses)
kag.set_excuses_url(self._excuses_url)
elif (graph_type == "build_status") or (graph_type == "retry_builds"): #FIXME: retry-builds
if self._archive_name == "ubuntu":
graph_title = "%s %s - %s (%s build status)" % (
self._release_type, self._release_type_version,
self._dist, self._archive_name)
kag = KAGraphArchiveBuildStatus(title=graph_title, dist=self._dist)
else:
graph_title = ("%s %s - %s (%s build status)"
% (self._release_type, self._release_type_version,
self._dist, self._archive_name))
kag = KAGraphPPABuildStatus(title=graph_title, dist=self._dist)
else:
raise Exception("Graph type %s not supported" % graph_type)
#Set expected version and list for different versions
kag.set_expected_version(self._release_type_version)
diff_versions_map = read_json_data_file('different-versions.json')
try:
diff_versions_list = diff_versions_map[self._release_type]
except KeyError:
diff_versions_list = []
kag.set_different_versions(diff_versions_list)
#Populate graph with nodes and edges
kag.populate_from_bd_relations_map(self._bd_relation_map)
#Set the arch list
kag.set_arch_list(self._arch_list_str)
#Set the status of the nodes with the info from LP
if graph_type != "build_depends":
if kag.__class__.__name__ == "KAGraphPPABuildStatus":
kag.set_package_status_from_lp_sources_and_builds(
self._all_sources, self._all_builds)
else:
kag.set_package_status_from_lp_sources_and_builds(
self._all_sources, self._all_builds, self._lpseries)
#Add legend
kag.add_legend()
#Final graph settings...
kag.update_timestamp()
kag.transitive_reduction()
#Store the graph for retrieving it later
self._graphs_map[graph_type] = kag
[docs]
def get_graphs(self):
"""
Returns a hashmap of KAGraph's indexed by type of graph.
"""
return self._graphs_map