2017-09-25 16:21:40 +02:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
import re
|
|
|
|
import requests
|
|
|
|
|
|
|
|
|
|
|
|
# python 3 has gzip decompress function
|
|
|
|
try:
|
|
|
|
from gzip import decompress
|
|
|
|
except ImportError:
|
|
|
|
from gzip import GzipFile
|
|
|
|
import io
|
|
|
|
|
|
|
|
def decompress(data):
|
|
|
|
with GzipFile(fileobj=io.BytesIO(data)) as f:
|
|
|
|
return f.read()
|
|
|
|
|
|
|
|
# use cElementTree by default, fallback to pure python
|
|
|
|
try:
|
|
|
|
from xml.etree import cElementTree as ET
|
|
|
|
except ImportError:
|
|
|
|
from xml.etree import ElementTree as ET
|
|
|
|
|
|
|
|
import osc.core
|
|
|
|
|
|
|
|
from osclib.memoize import memoize
|
|
|
|
|
|
|
|
|
|
|
|
class Update(object):
|
|
|
|
incident_name_cache = {}
|
|
|
|
|
|
|
|
def __init__(self, settings):
|
|
|
|
self._settings = settings
|
|
|
|
self._settings['_NOOBSOLETEBUILD'] = '1'
|
|
|
|
self.opensuse = True
|
|
|
|
|
|
|
|
def get_max_revision(self, job):
|
|
|
|
repo = self.repo_prefix + '/'
|
|
|
|
repo += self.maintenance_project.replace(':', ':/')
|
|
|
|
repo += ':/{!s}'.format(job['id'])
|
|
|
|
max_revision = 0
|
|
|
|
for channel in job['channels']:
|
|
|
|
crepo = repo + '/' + channel.replace(':', '_')
|
|
|
|
xml = requests.get(crepo + '/repodata/repomd.xml')
|
|
|
|
if not xml.ok:
|
2017-09-27 01:04:46 +02:00
|
|
|
self.logger.info("{} skipped .. need wait".format(crepo))
|
2017-09-25 16:21:40 +02:00
|
|
|
# if one fails, we skip it and wait
|
|
|
|
return None
|
|
|
|
root = ET.fromstring(xml.text)
|
|
|
|
rev = root.find('.//{http://linux.duke.edu/metadata/repo}revision')
|
|
|
|
rev = int(rev.text)
|
|
|
|
if rev > max_revision:
|
|
|
|
max_revision = rev
|
|
|
|
return max_revision
|
|
|
|
|
2018-04-18 14:53:31 +02:00
|
|
|
def settings(self, src_prj, dst_prj):
|
2017-09-25 16:21:40 +02:00
|
|
|
s = self._settings.copy()
|
|
|
|
|
|
|
|
# start with a colon so it looks cool behind 'Build' :/
|
|
|
|
s['BUILD'] = ':' + src_prj.split(':')[-1]
|
|
|
|
name = self.incident_name(src_prj)
|
|
|
|
repo = dst_prj.replace(':', '_')
|
|
|
|
repo = '{!s}/{!s}/{!s}/'.format(self.repo_prefix, src_prj.replace(':', ':/'), repo)
|
|
|
|
patch_id = self.patch_id(repo)
|
|
|
|
if patch_id:
|
|
|
|
s['INCIDENT_REPO'] = repo
|
|
|
|
s['INCIDENT_PATCH'] = patch_id
|
|
|
|
s['BUILD'] += ':' + name
|
|
|
|
return [s]
|
|
|
|
|
|
|
|
@memoize()
|
|
|
|
def incident_packages(self, prj):
|
|
|
|
packages = []
|
|
|
|
for package in osc.core.meta_get_packagelist(self.apiurl, prj):
|
|
|
|
if package.endswith('SUSE_Channels') or package.startswith('patchinfo'):
|
|
|
|
continue
|
|
|
|
parts = package.split('.')
|
|
|
|
# remove target name
|
|
|
|
parts.pop()
|
|
|
|
packages.append('.'.join(parts))
|
|
|
|
return packages
|
|
|
|
|
|
|
|
# grab the updateinfo from the given repo and return its patch's id
|
|
|
|
@staticmethod
|
|
|
|
def patch_id(repo):
|
|
|
|
url = repo + 'repodata/repomd.xml'
|
|
|
|
repomd = requests.get(url)
|
|
|
|
if not repomd.ok:
|
|
|
|
return None
|
|
|
|
root = ET.fromstring(repomd.text)
|
|
|
|
|
|
|
|
cs = root.find(
|
|
|
|
'.//{http://linux.duke.edu/metadata/repo}data[@type="updateinfo"]/{http://linux.duke.edu/metadata/repo}location')
|
2017-12-14 16:04:00 +01:00
|
|
|
try:
|
|
|
|
url = repo + cs.attrib['href']
|
|
|
|
except AttributeError:
|
|
|
|
return None
|
2017-09-25 16:21:40 +02:00
|
|
|
|
|
|
|
repomd = requests.get(url).content
|
|
|
|
root = ET.fromstring(decompress(repomd))
|
|
|
|
return root.find('.//id').text
|
|
|
|
|
|
|
|
# take the first package name we find - often enough correct
|
|
|
|
def incident_name(self, prj):
|
|
|
|
if prj not in self.incident_name_cache:
|
|
|
|
self.incident_name_cache[prj] = self._incident_name(prj)
|
|
|
|
return self.incident_name_cache[prj]
|
|
|
|
|
|
|
|
def _incident_name(self, prj):
|
|
|
|
shortest_pkg = None
|
|
|
|
for package in osc.core.meta_get_packagelist(self.apiurl, prj):
|
|
|
|
if package.startswith('patchinfo'):
|
|
|
|
continue
|
|
|
|
if package.endswith('SUSE_Channels'):
|
|
|
|
continue
|
2018-04-18 14:53:31 +02:00
|
|
|
# other tools on SLE have data from SMELT without access to this attrib
|
2017-09-25 16:21:40 +02:00
|
|
|
if self.opensuse:
|
|
|
|
url = osc.core.makeurl(self.apiurl, ('source', prj, package, '_link'))
|
|
|
|
root = ET.parse(osc.core.http_GET(url)).getroot()
|
|
|
|
if root.attrib.get('cicount'):
|
|
|
|
continue
|
2018-04-18 14:53:31 +02:00
|
|
|
# super hack, but we need to strip the suffix from the package name
|
|
|
|
# but bash.openSUSE_Leap_42.3_Update doesn't leave many options
|
|
|
|
# without reverse engineering OBS :(
|
|
|
|
package = re.sub(r'\.openSUSE_Leap_.*$', '.openSUSE', package)
|
2017-09-25 16:21:40 +02:00
|
|
|
if not shortest_pkg or len(package) < len(shortest_pkg):
|
|
|
|
shortest_pkg = package
|
|
|
|
if not shortest_pkg:
|
|
|
|
shortest_pkg = 'unknown'
|
|
|
|
match = re.match(r'^(.*)\.[^\.]*$', shortest_pkg)
|
2017-09-27 01:04:46 +02:00
|
|
|
|
|
|
|
return match.group(1) if match else shortest_pkg
|