#!/usr/bin/python2
# coding: utf-8
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
import os
import sys
import re
import urllib
import urlparse
import shutil
import subprocess
import urllib
import argparse
from collections import defaultdict
import xml.etree.cElementTree as ET
from requests.auth import HTTPDigestAuth
from pygerrit.rest import GerritRestAPI
HEADER="""
"""
XML_ITEM=""" \n"""
FOOTER=""""""
class GerritClient(object):
def __init__(self, gerrit_url, user=None, passwd=None):
auth = None
if user and passwd:
auth = HTTPDigestAuth(user, passwd)
self.api = GerritRestAPI(url='https://review.tizen.org/gerrit', auth=auth)
def get_branches(self, project_name):
quoted_path = urllib.quote(project_name,safe='')
branches = self.api.get("/projects/%s/branches/" % quoted_path)
return [br['ref'].replace("refs/heads/", "") for br in branches]
def parse_buildxml(baseurl):
"""Get build id and build target of remote repo"""
if not baseurl.endswith('/'):
baseurl += '/'
repomd_url = urlparse.urljoin(baseurl, 'build.xml')
fobj = urllib.urlopen(repomd_url)
root = ET.fromstring(fobj.read())
data = {}
data['id'] = root.findtext('id')
data['buildtargets'] = []
targets = root.find('buildtargets')
if targets:
for target in targets.findall('buildtarget'):
data['buildtargets'].append(target.get('name'))
return data
def parse_manifest(fname):
etree = ET.parse(fname)
root = etree.getroot()
nodes = root.findall('project')
projects = {node.get('name'):dict(node.items()) for node in nodes}
return projects
def diff_projects(file1, file2):
"""Compare the difference between two manifest file"""
projects_1 = parse_manifest(file1)
projects_2 = parse_manifest(file2)
ret = defaultdict(list)
for prj_name in projects_1:
if prj_name not in projects_2:
ret['Removed'].append(prj_name)
for prj_name, prj_info in projects_2.items():
if prj_name not in projects_1:
ret['Added'].append(prj_name)
else:
try:
old_revision = prj_info['revision']
new_revision = projects_1[prj_name]['revision']
except KeyError:
pass
else:
if old_revision != new_revision:
ret['Changed'].append(
dict(name=prj_name, old_revision=old_revision, new_revision=new_revision)
)
return ret
def get_rev_parse(project_dir, treeish):
cmd = 'git --git-dir=%s rev-parse %s' % (project_dir, treeish)
try:
revision = subprocess.check_output(cmd.split()).strip()
except subprocess.CalledProcessError, err:
print head, 'not found in', prj
return None
return revision
def gen_repo_manifest(projects, profile):
"""generate manifest for specified profile using projects list"""
manifest_head = HEADER
manifest_body = ""
manifest_tail = FOOTER
for project in sorted(projects):
manifest_body +=XML_ITEM %(project, project, profile)
return manifest_head + manifest_body + manifest_tail
def get_rev_parse(project_dir, treeish):
cmd = 'git --git-dir=%s rev-parse %s' % (project_dir, treeish)
try:
revision = subprocess.check_output(cmd.split()).strip()
except subprocess.CalledProcessError, err:
print head, 'not found in', prj
return None
return revision
if __name__ == '__main__':
repo_alias = {
'common-latest': 'http://download.tizen.org/snapshots/tizen/common/latest/',
'ivi-latest': 'http://download.tizen.org/snapshots/tizen/ivi/latest/',
'mobile-latest': 'http://download.tizen.org/snapshots/tizen/mobile/latest',
'tv-latest': 'http://download.tizen.org/snapshots/tizen/tv/latest/',
'wearable-latest': 'http://download.tizen.org/snapshots/tizen/wearable/latest/',
}
parser = argparse.ArgumentParser(description="Check manifest changes")
parser.add_argument('--url', required=True, help='snaptshot repo url contain builddata directory')
parser.add_argument('--diff', action='store_true', help='check the difference between two projects xml file')
parser.add_argument('--update', action='store_true', help='update manifest xml file')
parser.add_argument('--tizen-src', required=True, help='specify tizen source directory')
parser.add_argument('-p', '--profile', required=True, help='profile, valid value is ivi and common')
parser.add_argument('--sync', action='store_true', help='run repo sync')
parser.add_argument('--outdir', help='output directory')
args = parser.parse_args()
args.tizen_src = os.path.abspath(os.path.expanduser(args.tizen_src))
if args.url in repo_alias:
args.url = repo_alias[args.url]
data = parse_buildxml(args.url)
projects = {}
manifest_file = None
local_manifest = os.path.join(args.tizen_src, '.repo/manifests', args.profile, 'projects.xml')
for buildtarget in data['buildtargets']:
manifest_file_url = args.url + '/builddata/manifest/' + data['id'] + '_' + buildtarget + '.xml'
print 'downloading', manifest_file_url
manifest_file = os.path.basename(manifest_file_url)
urllib.urlretrieve(manifest_file_url, manifest_file)
projects.update(parse_manifest(manifest_file))
if args.update:
subprocess.call(["git", "reset", "--hard", "HEAD"],
cwd=os.path.join(args.tizen_src, '.repo/manifests'))
manifest_string = gen_repo_manifest(projects, args.profile)
with open(manifest_file, 'w') as fobj:
fobj.write(manifest_string)
ret = diff_projects(local_manifest, manifest_file)
shutil.copy(manifest_file, local_manifest)
if 'Removed' in ret:
print len(ret['Removed']), "projects have been removed"
for prj in ret['Removed']:
print "\t", prj
if 'Added' in ret:
print len(ret['Added']), "projects have been added"
for prj in ret['Added']:
print "\t", prj
packages_no_acc_branches = []
gc = GerritClient('https://review.tizen.org/gerrit','','')
for prj in ret['Added']:
if not 'accepted/tizen_'+args.profile in gc.get_branches(prj):
packages_no_acc_branches.append(prj)
if packages_no_acc_branches:
print "%d packages have no accepted/tizen_%s branch" % \
(len(packages_no_acc_branches), args.profile)
for prj in packages_no_acc_branches:
print "\t", prj
if args.sync:
cmd = "repo sync -f"
print "Running", cmd
try:
subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT, cwd=args.tizen_src)
except subprocess.CalledProcessError, err:
print re.findall("revision .* in .* not found", err.output)
if args.diff:
tizen_source = os.path.abspath(args.tizen_src)
print len(projects), "projects in total"
print "found out if tizen source version mismath with obs "
head = 'HEAD' if not args.profile else 'tizen-gerrit/accepted/tizen_' + args.profile
report_file = 'tizen_%s_revision_diff.csv' % args.profile
if args.outdir:
if not os.path.exists(args.outdir):
os.mkdir(args.outdir)
out_file = os.path.join(args.outdir, report_file)
else:
out_file = report_file
output = open(out_file, 'w')
result = ['Project name,OBS revision,accepted branch revision,OBS is newer']
for prj, prjinfo in projects.items():
local_project = os.path.join(tizen_source, prj, '.git')
if not os.path.exists(local_project):
print 'remote project: %s does not exist locally' % prj
continue
version = get_rev_parse(local_project, head)
prjinfo['revision'] = get_rev_parse(local_project, prjinfo['revision'])
rev_list = subprocess.check_output(['git', '--git-dir=' + local_project, 'rev-list', head])
newer = 'Y'
if prjinfo['revision'] in rev_list:
newer = 'N'
if version != prjinfo['revision']:
result.append(','.join([prj, prjinfo['revision'], version, newer]))
#TODO: update accepted/tizen_{ivi,common} branch using OBS revision:prjinfo['revision']
output.write('\n'.join(sorted(result)))
print "Save to", out_file