parent
64dd1fc405
commit
363622addf
@ -1 +1 @@
|
|||||||
1209 cjwatson@canonical.com-20190220074107-dvkdscxl2y2ww9j6
|
1324 steve.langasek@canonical.com-20200516195933-ymljxy32gq2m13p9
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,657 @@
|
|||||||
|
#!/usr/bin/env python2.7
|
||||||
|
#
|
||||||
|
|
||||||
|
try:
|
||||||
|
from urllib.request import urlopen
|
||||||
|
except ImportError:
|
||||||
|
from urllib2 import urlopen
|
||||||
|
|
||||||
|
import os
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
class KernelRoutingEntry:
|
||||||
|
def __init__(self, ks, source, data):
|
||||||
|
name = "{}:{}".format(source.series.codename, source.name)
|
||||||
|
if isinstance(data, str):
|
||||||
|
name = data
|
||||||
|
table = source.series.routing_table
|
||||||
|
if table is None:
|
||||||
|
raise ValueError("unable to map routing alias {}, "
|
||||||
|
"no series routing table".format(data))
|
||||||
|
if data not in table:
|
||||||
|
raise ValueError("unable to map routing alias {}, "
|
||||||
|
"not listed in series routing table".format(data))
|
||||||
|
data = table[data]
|
||||||
|
|
||||||
|
# Clear out any entries that have been overriden to None.
|
||||||
|
for entry in dict(data):
|
||||||
|
if data[entry] is None:
|
||||||
|
del data[entry]
|
||||||
|
|
||||||
|
self._ks = ks
|
||||||
|
self._source = source
|
||||||
|
self._name = name
|
||||||
|
self._data = data if data else {}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def source(self):
|
||||||
|
return self._source
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if isinstance(self, other.__class__):
|
||||||
|
return list(self) == list(other)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return not self.__eq__(other)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self._data.items())
|
||||||
|
|
||||||
|
def __getitem__(self, which):
|
||||||
|
return self._data[which]
|
||||||
|
|
||||||
|
def lookup_destination(self, dest, primary=False):
|
||||||
|
data = self._data.get(dest, None)
|
||||||
|
if primary is False or data is None:
|
||||||
|
return data
|
||||||
|
return data[0]
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(self._data)
|
||||||
|
|
||||||
|
|
||||||
|
class KernelRepoEntry:
|
||||||
|
def __init__(self, ks, owner, data):
|
||||||
|
if isinstance(data, list):
|
||||||
|
new_data = {'url': data[0]}
|
||||||
|
if len(data) == 1:
|
||||||
|
new_data['branch'] = 'master'
|
||||||
|
elif len(data) == 2:
|
||||||
|
new_data['branch'] = data[1]
|
||||||
|
data = new_data
|
||||||
|
|
||||||
|
self._ks = ks
|
||||||
|
self._owner = owner
|
||||||
|
self._data = data if data else {}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def owner(self):
|
||||||
|
return self._owner
|
||||||
|
|
||||||
|
# XXX: should this object have a name ?
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if isinstance(self, other.__class__):
|
||||||
|
return self.url == other.url and self.branch == other.branch
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return not self.__eq__(other)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def url(self):
|
||||||
|
return self._data['url']
|
||||||
|
|
||||||
|
@property
|
||||||
|
def branch(self):
|
||||||
|
return self._data.get('branch', None)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "{} {}".format(self.url, self.branch)
|
||||||
|
|
||||||
|
|
||||||
|
class KernelSnapEntry:
|
||||||
|
def __init__(self, ks, source, name, data):
|
||||||
|
self._ks = ks
|
||||||
|
self._source = source
|
||||||
|
self._name = name
|
||||||
|
self._data = data if data else {}
|
||||||
|
|
||||||
|
# Convert arches/track to publish-to form.
|
||||||
|
if 'publish-to' not in self._data:
|
||||||
|
if 'arches' in self._data:
|
||||||
|
publish_to = {}
|
||||||
|
for arch in self._data['arches']:
|
||||||
|
publish_to[arch] = [self._data.get('track', 'latest')]
|
||||||
|
self._data['publish-to'] = publish_to
|
||||||
|
|
||||||
|
# Convert stable to promote-to form.
|
||||||
|
if 'promote-to' not in self._data and 'stable' in self._data:
|
||||||
|
if self._data['stable'] is True:
|
||||||
|
self._data['promote-to'] = 'stable'
|
||||||
|
else:
|
||||||
|
self._data['promote-to'] = 'candidate'
|
||||||
|
# Assume no promote-to data to mean just to edge.
|
||||||
|
promote_to = self._data.get('promote-to', 'edge')
|
||||||
|
if isinstance(promote_to, str):
|
||||||
|
expand_promote_to = []
|
||||||
|
for risk in ('edge', 'beta', 'candidate', 'stable'):
|
||||||
|
expand_promote_to.append(risk)
|
||||||
|
if risk == promote_to:
|
||||||
|
break
|
||||||
|
self._data['promote-to'] = expand_promote_to
|
||||||
|
# Ensure we have stable when promote-to is present.
|
||||||
|
if 'promote-to' in self._data and 'stable' not in self._data:
|
||||||
|
if 'stable' in self._data['promote-to']:
|
||||||
|
self._data['stable'] = True
|
||||||
|
else:
|
||||||
|
self._data['stable'] = False
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if isinstance(self, other.__class__):
|
||||||
|
return self.name == other.name and self.source == other.source
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return not self.__eq__(other)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def series(self):
|
||||||
|
return self._source.series
|
||||||
|
|
||||||
|
@property
|
||||||
|
def source(self):
|
||||||
|
return self._source
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def repo(self):
|
||||||
|
data = self._data.get('repo', None)
|
||||||
|
if not data:
|
||||||
|
return None
|
||||||
|
return KernelRepoEntry(self._ks, self, data)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def primary(self):
|
||||||
|
return self._data.get('primary', False)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def gated(self):
|
||||||
|
return self._data.get('gated', False)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def stable(self):
|
||||||
|
return self._data.get('stable', False)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def qa(self):
|
||||||
|
return self._data.get('qa', False)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def hw_cert(self):
|
||||||
|
return self._data.get('hw-cert', False)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def arches(self):
|
||||||
|
# XXX: should this be []
|
||||||
|
return self._data.get('arches', None)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def track(self):
|
||||||
|
return self._data.get('track', None)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def publish_to(self):
|
||||||
|
return self._data.get('publish-to', None)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def promote_to(self):
|
||||||
|
return self._data.get('promote-to', None)
|
||||||
|
|
||||||
|
def promote_to_risk(self, risk):
|
||||||
|
return risk in self._data.get('promote-to', [])
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "{} {}".format(str(self.source), self.name)
|
||||||
|
|
||||||
|
|
||||||
|
class KernelPackageEntry:
|
||||||
|
def __init__(self, ks, source, name, data):
|
||||||
|
self._ks = ks
|
||||||
|
self._source = source
|
||||||
|
self._name = name
|
||||||
|
self._data = data if data else {}
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if isinstance(self, other.__class__):
|
||||||
|
return self.name == other.name and self.source == other.source
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return not self.__eq__(other)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def series(self):
|
||||||
|
return self._source.series
|
||||||
|
|
||||||
|
@property
|
||||||
|
def source(self):
|
||||||
|
return self._source
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self):
|
||||||
|
return self._data.get('type', None)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def repo(self):
|
||||||
|
data = self._data.get('repo', None)
|
||||||
|
if not data:
|
||||||
|
return None
|
||||||
|
return KernelRepoEntry(self._ks, self, data)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "{} {} {}".format(str(self.source), self.name, self.type)
|
||||||
|
|
||||||
|
|
||||||
|
class KernelSourceEntry:
|
||||||
|
def __init__(self, ks, series, name, data):
|
||||||
|
self._ks = ks
|
||||||
|
self._series = series
|
||||||
|
self._name = name
|
||||||
|
self._data = data if data else {}
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if isinstance(self, other.__class__):
|
||||||
|
return self.name == other.name and self.series == other.series
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return not self.__eq__(other)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def series(self):
|
||||||
|
return self._series
|
||||||
|
|
||||||
|
@property
|
||||||
|
def versions(self):
|
||||||
|
if 'versions' in self._data:
|
||||||
|
return self._data['versions']
|
||||||
|
|
||||||
|
derived_from = self.derived_from
|
||||||
|
if derived_from is not None:
|
||||||
|
return derived_from.versions
|
||||||
|
|
||||||
|
copy_forward = self.copy_forward
|
||||||
|
if copy_forward is not None:
|
||||||
|
return copy_forward.versions
|
||||||
|
|
||||||
|
# XXX: should this be []
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def version(self):
|
||||||
|
versions = self.versions
|
||||||
|
if not versions:
|
||||||
|
return None
|
||||||
|
return versions[-1]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def development(self):
|
||||||
|
return self._data.get('development', self.series.development)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def supported(self):
|
||||||
|
return self._data.get('supported', self.series.supported)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def severe_only(self):
|
||||||
|
return self._data.get('severe-only', False)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def stakeholder(self):
|
||||||
|
return self._data.get('stakeholder', None)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def packages(self):
|
||||||
|
# XXX: should this return None when empty
|
||||||
|
result = []
|
||||||
|
packages = self._data.get('packages')
|
||||||
|
if packages:
|
||||||
|
for package_key, package in packages.items():
|
||||||
|
result.append(KernelPackageEntry(self._ks, self, package_key, package))
|
||||||
|
return result
|
||||||
|
|
||||||
|
def lookup_package(self, package_key):
|
||||||
|
packages = self._data.get('packages')
|
||||||
|
if not packages or package_key not in packages:
|
||||||
|
return None
|
||||||
|
return KernelPackageEntry(self._ks, self, package_key, packages[package_key])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def snaps(self):
|
||||||
|
# XXX: should this return None when empty
|
||||||
|
result = []
|
||||||
|
snaps = self._data.get('snaps')
|
||||||
|
if snaps:
|
||||||
|
for snap_key, snap in snaps.items():
|
||||||
|
result.append(KernelSnapEntry(self._ks, self, snap_key, snap))
|
||||||
|
return result
|
||||||
|
|
||||||
|
def lookup_snap(self, snap_key):
|
||||||
|
snaps = self._data.get('snaps')
|
||||||
|
if not snaps or snap_key not in snaps:
|
||||||
|
return None
|
||||||
|
return KernelSnapEntry(self._ks, self, snap_key, snaps[snap_key])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def derived_from(self):
|
||||||
|
if 'derived-from' not in self._data:
|
||||||
|
return None
|
||||||
|
|
||||||
|
(series_key, source_key) = self._data['derived-from']
|
||||||
|
|
||||||
|
series = self._ks.lookup_series(series_key)
|
||||||
|
source = series.lookup_source(source_key)
|
||||||
|
|
||||||
|
return source
|
||||||
|
|
||||||
|
@property
|
||||||
|
def testable_flavours(self):
|
||||||
|
retval = []
|
||||||
|
if (self._data.get('testing') is not None and
|
||||||
|
self._data['testing'].get('flavours') is not None
|
||||||
|
):
|
||||||
|
for flavour in self._data['testing']['flavours'].keys():
|
||||||
|
fdata = self._data['testing']['flavours'][flavour]
|
||||||
|
# If we have neither arches nor clouds we represent a noop
|
||||||
|
if not fdata:
|
||||||
|
continue
|
||||||
|
arches = fdata.get('arches', None)
|
||||||
|
arches = arches if arches is not None else []
|
||||||
|
clouds = fdata.get('clouds', None)
|
||||||
|
clouds = clouds if clouds is not None else []
|
||||||
|
retval.append(KernelSourceTestingFlavourEntry(flavour, arches, clouds))
|
||||||
|
return retval
|
||||||
|
|
||||||
|
@property
|
||||||
|
def invalid_tasks(self):
|
||||||
|
retval = self._data.get('invalid-tasks', [])
|
||||||
|
if retval is None:
|
||||||
|
retval = []
|
||||||
|
return retval
|
||||||
|
|
||||||
|
@property
|
||||||
|
def copy_forward(self):
|
||||||
|
if 'copy-forward' not in self._data:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# XXX: backwards compatibility.
|
||||||
|
if self._data['copy-forward'] is False:
|
||||||
|
return None
|
||||||
|
if self._data['copy-forward'] is True:
|
||||||
|
derived_from = self.derived_from
|
||||||
|
if derived_from is None:
|
||||||
|
return True
|
||||||
|
return self.derived_from
|
||||||
|
|
||||||
|
(series_key, source_key) = self._data['copy-forward']
|
||||||
|
|
||||||
|
series = self._ks.lookup_series(series_key)
|
||||||
|
source = series.lookup_source(source_key)
|
||||||
|
|
||||||
|
return source
|
||||||
|
|
||||||
|
@property
|
||||||
|
def backport(self):
|
||||||
|
return self._data.get('backport', False)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def routing(self):
|
||||||
|
default = 'default'
|
||||||
|
if self.series.development:
|
||||||
|
default = 'devel'
|
||||||
|
if self.series.esm:
|
||||||
|
default = 'esm'
|
||||||
|
data = self._data.get('routing', default)
|
||||||
|
if data is None:
|
||||||
|
return data
|
||||||
|
return KernelRoutingEntry(self._ks, self, data)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def swm_data(self):
|
||||||
|
return self._data.get('swm')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def private(self):
|
||||||
|
return self._data.get('private', False)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "{} {}".format(self.series.name, self.name)
|
||||||
|
|
||||||
|
class KernelSourceTestingFlavourEntry:
|
||||||
|
def __init__(self, name, arches, clouds):
|
||||||
|
self._name = name
|
||||||
|
self._arches = arches
|
||||||
|
self._clouds = clouds
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def arches(self):
|
||||||
|
return self._arches
|
||||||
|
|
||||||
|
@property
|
||||||
|
def clouds(self):
|
||||||
|
return self._clouds
|
||||||
|
|
||||||
|
class KernelSeriesEntry:
|
||||||
|
def __init__(self, ks, name, data, defaults=None):
|
||||||
|
self._ks = ks
|
||||||
|
self._name = name
|
||||||
|
self._data = {}
|
||||||
|
if defaults is not None:
|
||||||
|
self._data.update(defaults)
|
||||||
|
if data is not None:
|
||||||
|
self._data.update(data)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if isinstance(self, other.__class__):
|
||||||
|
return self.name == other.name
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return not self.__eq__(other)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def codename(self):
|
||||||
|
return self._data.get('codename', None)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def opening(self):
|
||||||
|
if 'opening' in self._data:
|
||||||
|
if self._data['opening'] is not False:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def opening_ready(self, *flags):
|
||||||
|
if 'opening' not in self._data:
|
||||||
|
return True
|
||||||
|
allow = self._data['opening']
|
||||||
|
if allow is None:
|
||||||
|
return False
|
||||||
|
if allow in (True, False):
|
||||||
|
return not allow
|
||||||
|
for flag in flags:
|
||||||
|
flag_allow = allow.get(flag, False)
|
||||||
|
if flag_allow is None or flag_allow is False:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
opening_allow = opening_ready
|
||||||
|
|
||||||
|
@property
|
||||||
|
def development(self):
|
||||||
|
return self._data.get('development', False)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def supported(self):
|
||||||
|
return self._data.get('supported', False)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def lts(self):
|
||||||
|
return self._data.get('lts', False)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def esm(self):
|
||||||
|
return self._data.get('esm', False)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "{} ({})".format(self.name, self.codename)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def sources(self):
|
||||||
|
result = []
|
||||||
|
sources = self._data.get('sources')
|
||||||
|
if sources:
|
||||||
|
for source_key, source in sources.items():
|
||||||
|
result.append(KernelSourceEntry(
|
||||||
|
self._ks, self, source_key, source))
|
||||||
|
return result
|
||||||
|
|
||||||
|
@property
|
||||||
|
def routing_table(self):
|
||||||
|
return self._data.get('routing-table', None)
|
||||||
|
|
||||||
|
def lookup_source(self, source_key):
|
||||||
|
sources = self._data.get('sources')
|
||||||
|
if not sources or source_key not in sources:
|
||||||
|
return None
|
||||||
|
return KernelSourceEntry(self._ks, self, source_key, sources[source_key])
|
||||||
|
|
||||||
|
|
||||||
|
# KernelSeries
|
||||||
|
#
|
||||||
|
class KernelSeries:
|
||||||
|
_url = 'https://git.launchpad.net/~canonical-kernel/' \
|
||||||
|
'+git/kteam-tools/plain/info/kernel-series.yaml'
|
||||||
|
_url_local = 'file://' + os.path.realpath(os.path.join(os.path.dirname(__file__),
|
||||||
|
'..', 'info', 'kernel-series.yaml'))
|
||||||
|
#_url = 'file:///home/apw/git2/kteam-tools/info/kernel-series.yaml'
|
||||||
|
#_url = 'file:///home/work/kteam-tools/info/kernel-series.yaml'
|
||||||
|
_data_txt = {}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def __load_once(cls, url):
|
||||||
|
if url not in cls._data_txt:
|
||||||
|
response = urlopen(url)
|
||||||
|
data = response.read()
|
||||||
|
if not isinstance(data, str):
|
||||||
|
data = data.decode('utf-8')
|
||||||
|
cls._data_txt[url] = data
|
||||||
|
return cls._data_txt[url]
|
||||||
|
|
||||||
|
def __init__(self, url=None, data=None, use_local=os.getenv("USE_LOCAL_KERNEL_SERIES_YAML", False)):
|
||||||
|
if data or url:
|
||||||
|
if url:
|
||||||
|
response = urlopen(url)
|
||||||
|
data = response.read()
|
||||||
|
if not isinstance(data, str):
|
||||||
|
data = data.decode('utf-8')
|
||||||
|
else:
|
||||||
|
data = self.__load_once(self._url_local if use_local else self._url)
|
||||||
|
self._data = yaml.safe_load(data)
|
||||||
|
|
||||||
|
self._development_series = None
|
||||||
|
self._codename_to_series = {}
|
||||||
|
for series_key, series in self._data.items():
|
||||||
|
if not series:
|
||||||
|
continue
|
||||||
|
if series.get('development', False):
|
||||||
|
self._development_series = series_key
|
||||||
|
if 'codename' in series:
|
||||||
|
self._codename_to_series[series['codename']] = series_key
|
||||||
|
|
||||||
|
# Pull out the defaults.
|
||||||
|
self._defaults_series = {}
|
||||||
|
if 'defaults' in self._data:
|
||||||
|
self._defaults_series = self._data['defaults']
|
||||||
|
del self._data['defaults']
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def key_series_name(series):
|
||||||
|
return [int(x) for x in series.name.split('.')]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def series(self):
|
||||||
|
return [KernelSeriesEntry(self, series_key, series,
|
||||||
|
defaults=self._defaults_series)
|
||||||
|
for series_key, series in self._data.items()]
|
||||||
|
|
||||||
|
def lookup_series(self, series=None, codename=None, development=False):
|
||||||
|
if not series and not codename and not development:
|
||||||
|
raise ValueError("series/codename/development required")
|
||||||
|
if not series and codename:
|
||||||
|
if codename not in self._codename_to_series:
|
||||||
|
return None
|
||||||
|
series = self._codename_to_series[codename]
|
||||||
|
if not series and development:
|
||||||
|
if not self._development_series:
|
||||||
|
return None
|
||||||
|
series = self._development_series
|
||||||
|
if series and series not in self._data:
|
||||||
|
return None
|
||||||
|
return KernelSeriesEntry(self, series, self._data[series],
|
||||||
|
defaults=self._defaults_series)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
db = KernelSeries()
|
||||||
|
|
||||||
|
series = db.lookup_series('16.04')
|
||||||
|
if series.name != '16.04':
|
||||||
|
print('series.name != 16.04')
|
||||||
|
if series.codename != 'xenial':
|
||||||
|
print('series.codename != xenial')
|
||||||
|
|
||||||
|
series2 = db.lookup_series(codename='xenial')
|
||||||
|
if series2.name != '16.04':
|
||||||
|
print('series2.name != 16.04')
|
||||||
|
if series2.codename != 'xenial':
|
||||||
|
print('series2.codename != xenial')
|
||||||
|
|
||||||
|
series3 = db.lookup_series(development=True)
|
||||||
|
if series3.name != '18.04':
|
||||||
|
print('series3.name != 18.04')
|
||||||
|
if series3.codename != 'bionic':
|
||||||
|
print('series3.codename != bionic')
|
||||||
|
|
||||||
|
print(str(series), str(series2), str(series3))
|
||||||
|
|
||||||
|
for series2 in sorted(db.series, key=db.key_series_name):
|
||||||
|
print(series2)
|
||||||
|
|
||||||
|
for source in series.sources:
|
||||||
|
print(str(source), source.series.name, source.name)
|
||||||
|
|
||||||
|
print(source.derived_from)
|
||||||
|
print(source.versions)
|
||||||
|
|
||||||
|
for package in source.packages:
|
||||||
|
print("PACKAGE", str(package))
|
||||||
|
|
||||||
|
for snap in source.snaps:
|
||||||
|
print("SNAP", str(snap), snap.arches)
|
||||||
|
|
||||||
|
|
||||||
|
# vi:set ts=4 sw=4 expandtab:
|
Binary file not shown.
@ -0,0 +1,78 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Copyright (C) 2020 Canonical Ltd.
|
||||||
|
# Author: Iain Lane <iain.lane@canonical.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 program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
# Compare a given source package with the oem-qemu-meta reference package, to
|
||||||
|
# see if it complies with the MIR exception granted in
|
||||||
|
# https://wiki.ubuntu.com/MIRTeam/Exceptions/OEM
|
||||||
|
|
||||||
|
set -e
|
||||||
|
set -u
|
||||||
|
|
||||||
|
shopt -s nullglob
|
||||||
|
|
||||||
|
THIS="$(basename "${0}")"
|
||||||
|
|
||||||
|
ensure_programs() {
|
||||||
|
if [ ${#} -gt 0 ] && ! type "${1}" >/dev/null 2>/dev/null; then
|
||||||
|
echo "Required program $1 not found." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
shift
|
||||||
|
|
||||||
|
if [ ${#} -gt 0 ]; then
|
||||||
|
ensure_programs "${@}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ ${#} -ne 1 ]; then
|
||||||
|
echo -e "Usage: ${THIS} <dsc>\\n" >&2
|
||||||
|
cat <<EOM >&2
|
||||||
|
Compare the given package against the oem-qemu-meta reference package. Check
|
||||||
|
that all the differences are inconsequential or expected (different modalias,
|
||||||
|
different package name), and then promote or NEW the package directly to main.
|
||||||
|
|
||||||
|
https://wiki.ubuntu.com/MIRTeam/Exceptions/OEM
|
||||||
|
EOM
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ensure_programs pull-lp-source debdiff
|
||||||
|
|
||||||
|
if ! [ -e "${1}" ]; then
|
||||||
|
echo "${THIS}: ${1} not found" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
DSC="$(realpath -e "${1}")"
|
||||||
|
|
||||||
|
WORKINGDIR=$(mktemp -d)
|
||||||
|
|
||||||
|
trap 'rm -rf ${WORKINGDIR}' EXIT HUP INT QUIT TERM
|
||||||
|
|
||||||
|
pushd "${WORKINGDIR}" >/dev/null
|
||||||
|
|
||||||
|
# Download the reference package
|
||||||
|
pull-lp-source oem-qemu-meta -d 2>/dev/null
|
||||||
|
|
||||||
|
if [ -t 1 ] && type colordiff >/dev/null 2>/dev/null; then
|
||||||
|
debdiff oem-qemu-meta_*.dsc "${DSC}" 2>/dev/null | colordiff
|
||||||
|
else
|
||||||
|
debdiff oem-qemu-meta_*.dsc "${DSC}" 2>/dev/null
|
||||||
|
fi
|
@ -0,0 +1,139 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
# Copyright (C) 2020 Canonical Ltd.
|
||||||
|
# Author: Steve Langasek <steve.langasek@canonical.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; version 3 of the License.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
'''Synchronize the i386 source package whitelist in Launchpad with the output
|
||||||
|
of germinate.
|
||||||
|
|
||||||
|
USAGE:
|
||||||
|
update-i386-whitelist [--dry-run] https://people.canonical.com/~ubuntu-archive/germinate-output/i386.focal/i386+build-depends.sources
|
||||||
|
'''
|
||||||
|
|
||||||
|
from launchpadlib.launchpad import Launchpad
|
||||||
|
import optparse
|
||||||
|
from urllib.request import urlopen
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def get_sources_from_url(url):
|
||||||
|
'''Download the germinate output and parse out the list of sources.
|
||||||
|
|
||||||
|
Returns list of source package names.
|
||||||
|
'''
|
||||||
|
sources = []
|
||||||
|
|
||||||
|
file = urlopen(url)
|
||||||
|
for i in file:
|
||||||
|
if i.startswith(b'Source') or i.startswith(b'---'):
|
||||||
|
continue
|
||||||
|
sources.append(i.decode('utf-8').split(' ',maxsplit=1)[0])
|
||||||
|
return sources
|
||||||
|
|
||||||
|
def parse_options():
|
||||||
|
'''Parse command line arguments.
|
||||||
|
|
||||||
|
Return (options, source_package) tuple.
|
||||||
|
'''
|
||||||
|
parser = optparse.OptionParser(
|
||||||
|
usage='Usage: %prog [--dry-run] https://people.canonical.com/~ubuntu-archive/germinate-output/i386.focal/i386+build-depends.sources')
|
||||||
|
parser.add_option(
|
||||||
|
"--dry-run", help="don't change launchpad, just report the delta",
|
||||||
|
action="store_true")
|
||||||
|
parser.add_option(
|
||||||
|
"-s", dest="release", default=default_release, metavar="RELEASE",
|
||||||
|
help="release (default: %s)" % default_release)
|
||||||
|
|
||||||
|
(opts, args) = parser.parse_args()
|
||||||
|
|
||||||
|
if len(args) != 1:
|
||||||
|
parser.error('Need to specify a URL to sync from')
|
||||||
|
|
||||||
|
return (opts, args[0])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
default_release = 'focal'
|
||||||
|
|
||||||
|
(opts, url) = parse_options()
|
||||||
|
|
||||||
|
launchpad = Launchpad.login_with('update-i386-whitelist',
|
||||||
|
'production',
|
||||||
|
version="devel")
|
||||||
|
ubuntu = launchpad.distributions['ubuntu']
|
||||||
|
series = ubuntu.getSeries(name_or_version=opts.release)
|
||||||
|
archive = ubuntu.main_archive
|
||||||
|
|
||||||
|
sources = get_sources_from_url(url)
|
||||||
|
|
||||||
|
packageset = launchpad.packagesets.getByName(name='i386-whitelist',
|
||||||
|
distroseries=series)
|
||||||
|
currentSet = set(packageset.getSourcesIncluded())
|
||||||
|
newSet = set(sources)
|
||||||
|
# hard-coded list of ppa-only additions; can maybe go away when
|
||||||
|
# https://bugs.launchpad.net/launchpad/+bug/1855069 is fixed, but this is
|
||||||
|
# also potentially useful for bootstrapping any additional packages into
|
||||||
|
# the archive if needed.
|
||||||
|
|
||||||
|
# bootstrap new spdlog dep
|
||||||
|
newSet.update(['fmtlib'])
|
||||||
|
# for new lintian
|
||||||
|
newSet.update(['libdevel-size-perl', 'libcpanel-json-xs-perl',
|
||||||
|
'libsereal-decoder-perl', 'libsereal-encoder-perl',
|
||||||
|
'libjson-xs-perl'])
|
||||||
|
|
||||||
|
# needed to bootstrap openjdk-N
|
||||||
|
newSet.update(['openjdk-12'])
|
||||||
|
newSet.update(['openjdk-13'])
|
||||||
|
newSet.update(['openjdk-14'])
|
||||||
|
newSet.update(['openjdk-15'])
|
||||||
|
newSet.update(['openjdk-8'])
|
||||||
|
|
||||||
|
# we get the wrong answer from germinate about a source package's
|
||||||
|
# whitelisting when the package provides both Arch: any and Arch: all
|
||||||
|
# binaries but we actually only want the Arch: all ones. Rather than
|
||||||
|
# fix this in germinate, for now just manually exclude the packages
|
||||||
|
# we've found that have this problem.
|
||||||
|
for pkg in ('frei0r', 'xorg', 'ubuntu-drivers-common'):
|
||||||
|
try:
|
||||||
|
newSet.remove(pkg)
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
print("Additions:" )
|
||||||
|
additions = list(newSet-currentSet)
|
||||||
|
additions.sort()
|
||||||
|
for i in additions:
|
||||||
|
print(" * %s" % i)
|
||||||
|
print("Removals:" )
|
||||||
|
removals = list(currentSet-newSet)
|
||||||
|
removals.sort()
|
||||||
|
for i in removals:
|
||||||
|
print(" * %s" % i)
|
||||||
|
if opts.dry_run:
|
||||||
|
print("--dry-run is set, doing nothing.")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
if additions or removals:
|
||||||
|
print("Commit changes to the packageset? [yN] ", end="")
|
||||||
|
sys.stdout.flush()
|
||||||
|
response = sys.stdin.readline()
|
||||||
|
if not response.strip().lower().startswith('y'):
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if additions:
|
||||||
|
packageset.addSources(names=additions)
|
||||||
|
if removals:
|
||||||
|
packageset.removeSources(names=removals)
|
||||||
|
|
@ -0,0 +1,55 @@
|
|||||||
|
#!/usr/bin/python2.7
|
||||||
|
|
||||||
|
# Copyright (C) 2019 Canonical Ltd.
|
||||||
|
# Author: Brian Murray <brian.murray@canonical.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; version 3 of the License.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
"""Portions of archive related code that is re-used by various tools."""
|
||||||
|
|
||||||
|
import gzip
|
||||||
|
import os
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
import apt_pkg
|
||||||
|
|
||||||
|
|
||||||
|
def read_tag_file(path, pkg=None):
|
||||||
|
tmp = tempfile.NamedTemporaryFile(prefix='checkrdepends.', delete=False)
|
||||||
|
try:
|
||||||
|
compressed = gzip.open(path)
|
||||||
|
try:
|
||||||
|
tmp.write(compressed.read())
|
||||||
|
finally:
|
||||||
|
compressed.close()
|
||||||
|
tmp.close()
|
||||||
|
with open(tmp.name) as uncompressed:
|
||||||
|
tag_file = apt_pkg.TagFile(uncompressed)
|
||||||
|
prev_name = None
|
||||||
|
prev_stanza = None
|
||||||
|
for stanza in tag_file:
|
||||||
|
try:
|
||||||
|
name = stanza['package']
|
||||||
|
except KeyError:
|
||||||
|
continue
|
||||||
|
if pkg:
|
||||||
|
if name != pkg:
|
||||||
|
continue
|
||||||
|
if name != prev_name and prev_stanza is not None:
|
||||||
|
yield prev_stanza
|
||||||
|
prev_name = name
|
||||||
|
prev_stanza = stanza
|
||||||
|
if prev_stanza is not None:
|
||||||
|
yield prev_stanza
|
||||||
|
finally:
|
||||||
|
os.unlink(tmp.name)
|
Loading…
Reference in new issue