2019년 1월 18일 금요일

[Python] module - QB build 기능 추가하여 class로 만들기 #2



- class로 바꾸었음 (중간 중간 다른 일 하면서 작성해서 구성은 개선점이 많음)
- 이전 base/partial과 build id 기준 왔다갔다 하면서 동작 가능
- command-line interface 추가 및 pyinstaller를 이용한 실행파일 작성
- P4 관련 내용은 삭제
- qb를 여러개 사용하여 여러 branch 동시 빌드 가능하도록 수정

* 아래 comment 참고해서 xml을 위한 file access를 없애자.

==================================================
import time
import re

import requests
from xml.etree.ElementTree import ElementTree as ETR
from xml.etree.ElementTree import Element as ET
from xml.etree.ElementTree import parse


BUILD_XML = 'build_data.xml'
BUILDINFOXML = 'buildinfo.xml'

QB_URL = 'https://android.qb.sec.samsung.net/rest/'
QB_TOKENRQ = 'https://android.qb.sec.samsung.net/signin?1-1.IFormSubmitListener-form'
QB_CONFPATH = 'ids?configuration_path='
QB_BUILDRQ = 'build_requests'
QB_BUILDS  = 'builds/'

class QB:
    def __init__(self):
        self.__qb_cookies = dict()

    def qblogin(self, user, password, reflash=False):
        if len(self.__qb_cookies) != 0:
            return self.session, self.__qb_cookies

        token_url = QB_TOKENRQ
        token_data = {'userName': user, 'password': password}
        self.session = requests.session()
        self.session.post(token_url, token_data)
        self.__qb_cookies = requests.utils.dict_from_cookiejar(self.session.cookies)

        if len(self.__qb_cookies) < 3:
            print('Quickbuild login failed ...\n')

        time.sleep(1)

        return self.session, self.__qb_cookies

    def qb_getconfid(self, path):
        if len(self.__qb_cookies) == 0:
            print('quickbuild has not been logged ...')
            return 0

        txt = requests.get(QB_URL + QB_CONFPATH + path, cookies=self.__qb_cookies, verify=True).text
        time.sleep(1)

        return txt

    def qb_getbuild(self, bid:str):
        if len(self.__qb_cookies) == 0:
            print('quickbuild has not been logged ...')
            return 0

        txt = requests.get(QB_URL + QB_BUILDS + bid, cookies=self.__qb_cookies, verify=True).text

        # string을 그냥 return하는 것으로 변경...

        with open(BUILDINFOXML, 'wt', encoding='utf-8') as f:
            n = f.write(txt)
        return n

    def qb_getclinfo(self):
        base = ''
        partials = ''

        # string을 받아 tree.getroot()대신 바로 ElementTree.fromstring()으로 root 획득

        with open(BUILDINFOXML, 'rt', encoding='utf-8') as f:
            tree = parse(BUILDINFOXML)
            root = tree.getroot()
            sav = root.find('secretAwareVariableValues')
            entlist = sav.getchildren()
            for ent in entlist:
                l = ent.getchildren()
                if l[0].text == 'CL_SYNC':
                    base = l[1].getchildren()[0].text
                if l[0].text == 'CL_PARTIAL' :
                    sublist = l[1].getchildren()
                    if len(sublist) != 0:
                        partials = l[1].getchildren()[0].text
        return base, partials

    def __qb_entry(self, vname, vval):
        ent = ET('entry')
        name = ET('string')
        name.text = vname
        ent.append(name)
        val = ET('string')
        val.text = vval
        ent.append(val)
        return ent

    def qb_data(self, confid, base, partials, usermode=True, csc=None):
        if type(confid) != str or type(base) != str or (partials != None and type(partials) != str):
            raise TypeError

        breq = ET('com.pmease.quickbuild.BuildRequest')

        cid = ET('configurationId')
        cid.text = confid
        breq.append(cid)

        variables = ET('variables')

        if usermode==True:
            variables.append(self.__qb_entry('DEFAULT_BUILD_MODE', 'USER_BUILD_DEFAULT_OPTIONS'))
        else:
            variables.append(self.__qb_entry('DEFAULT_BUILD_MODE', 'ENG_BUILD_DEFAULT_OPTIONS'))
        variables.append(self.__qb_entry('CL_SYNC', base))
        variables.append(self.__qb_entry('CL_PARTIAL', partials))
        variables.append(self.__qb_entry('CL_SHELVE', ''))
        if csc!=None:
            variables.append(self.__qb_entry('BUILD_CSC', csc))

        breq.append(variables)

        # ETR(breq).tostring()을 이용해서 string을 return
        ETR(breq).write(BUILD_XML, encoding='utf-8', method='xml', xml_declaration=True)

    def qb_build(self, confid, base, partials, usermode=True):
        qb_header = {'Content-Type': 'text/xml', 'charset': 'utf-8'}
        self.qb_data(confid, base, partials, usermode)

        # xml을 string으로 받아 바로 data에 설정하는 것으로 변경
        rtn = requests.post(QB_URL + QB_BUILDRQ, data=open(BUILD_XML, 'rt', encoding='utf-8').read().replace('\n', ''), cookies=self.__qb_cookies, headers = qb_header)
        time.sleep(1)

        return rtn

CLLIST = 'cl_list.txt'
CHKCL = 'checkcl.txt'
CLFORM = re.compile(r'\d+')
ENC = 'utf-8'
ENC2 = 'euc-kr'
ENC3 = 'cp949'

#################################################################################

class CL:
    qb = None

    def reset(self):
        self.refmode = 0        # 0 : use base and partial, 1 : user build-id
        self.kormode = 0
        self.rbid = self.cbid = ''
        self.e = self.k = self.a = self.s = self.m = self.orgstr = self.errmsg = self.basecl = self.qb_base = self.qb_partials = ''
        self.eurclset = set()
        self.korclset = set()
        self.addclset = set()
        self.subclset = set()
        self.mustset  = set()
        self.diff1 = set()
        self.diff2 = set()
        self.resultlist = []
        self.mustlist = []
        self.underlist = []
        self.newlist = []
        self.config = []
        self.confid = dict()

    def setfile(self, fn):
        self.filename = fn

    def __init__(self, qbinst=None, fn=None):
        self.qb = qbinst if qbinst != None else None
        self.setfile(CLLIST if fn==None else fn)
        self.reset()

    def setqb(qbinst):
        self.qb = qbinst

    def str2set(self, cls):
        CL = re.compile(r'\w+')
        clset = set()
        #
        for match in CL.finditer(cls):
            cl = match.group()
            clset.add(cl)
        return clset

    def set2str(self, clset):
        clstr = ''
        for cl in clset:
            clstr += str(cl)
            clstr += ','
        return clstr[:-1]

    def set2str2(self, clset, chkset):
        clstr = ''
        for cl in clset:
            clstr += str(cl)
            if cl in chkset:
                clstr += '*'
            clstr += ','
        return clstr[:-1]

    def __match_comment(self, line):
        temp = re.findall('^\s*#(.*)', line)
        if temp != []:
            return True
        return False

    def __matchcls(self, base, matchstr, line):
        match = False
        temp = re.findall('^\s*'+matchstr+'\s*=', line)
        if len(temp) > 0:
            match = True
        temp = re.findall('^\s*'+matchstr+'\s*=\s*([\d,\s]*)#{0,1}', line)
        if temp != []:
            if base != '':
                base += ','
            base += temp[0].replace(' ','').replace('\n','')
        return match, base

    def __getcls(self):
        self.reset()
        with open(self.filename, mode='rt', encoding='utf-8') as f:
            #
            ln = 0
            while(1):
                line = f.readline()
                ln += 1

                self.orgstr += line
                if line == '' or line[:13] == '# end of data':
                    break

                temp = []
                #
                temp = re.findall('\s*', line)
                if temp[0] == line:
                    continue

                temp = re.findall('^qb\s*=\s*([\w/]+)', line)
                if temp != []:
                    self.config.append(temp[0])
                    continue

                temp = re.findall('^ref_buildid\s*=\s*([\d]+)', line)
                if temp != []:
                    self.rbid = temp[0]
                    try:
                        self.qb.qb_getbuild(self.rbid)
                        self.basecl, self.e = self.qb.qb_getclinfo()
                    except:
                        print('Error occured !!! : ref - self.basecl = %s, self.e = %s' % (self.basecl, self.e))

                    self.refmode = 1
                    continue

                temp = re.findall('^cur_buildid', line)
                if temp != []:
                    temp = re.findall('^cur_buildid\s*=\s*([\d]+)', line)
                    if temp != []:
                        self.cbid = temp[0]
                        try:
                            self.qb.qb_getbuild(self.cbid)
                            tempbase, self.k = self.qb.qb_getclinfo()
                        except:
                            print('Error occured !!! : cur - tempbase = %s, self.k = %s' % (tempbase, self.k))

                        if tempbase != self.basecl:
                            print("Warning !!! ... current base is not the same one as ref base's ")

                    self.kormode = 1
                    continue

                if(self.refmode == 0):
                    temp = re.findall('^base\s*=\s*([\d]+)', line)
                    if temp != []:
                        self.basecl = temp[0]
                        continue
                #
                match = mt = False

                if(self.refmode == 0):
                    mt, self.e = self.__matchcls(self.e, 'ref_partials', line)
                    match |= mt
                if(self.kormode == 0):
                    mt, self.k = self.__matchcls(self.k, 'cur_partials', line)
                    match |= mt
                mt, self.a = self.__matchcls(self.a, 'add_partials', line)
                match |= mt
                mt, self.s = self.__matchcls(self.s, 'sub_partials', line)
                match |= mt
                mt, self.m = self.__matchcls(self.m, 'must_partials', line)
                match |= mt
                match |= self.__match_comment(line)
                if match == False :
                    print('Error in line #%d' % ln)
                    self.errmsg += 'Error in line #'
                    self.errmsg += str(ln)
                    self.errmsg += '\n'

                self.eurclset = self.str2set(self.e)
                self.korclset = self.str2set(self.k)
                self.addclset = self.str2set(self.a)
                self.subclset = self.str2set(self.s)
                self.mustset  = self.str2set(self.m)


    def __putcls(self):
        result_str = '------------------------------------------\n' +         \
        self.errmsg                                                      + '\n'    \
        '1. REF - CUR : \n' +                 \
        self.set2str2(self.diff1, self.subclset) + '\n\n' +                                             \
        '2. CUR - REF : (Please check whether to add or not!) \n' + \
        self.set2str2(self.diff2, self.addclset) + '\n\n' + \
        '3. New CL list = REF + ADD CL - SUB CL (' + str(len(self.newcllist)) + ' CLs)\n\n' + 'Base : ' + str(self.basecl) + '\nPartial : \n' +   \
        str([int(x) for x in self.newcllist]).replace(', ',',')[1:-1] + '\n\n' +   \
        '***** CLs below the Base *****\n' +                               \
        str([int(x) for x in self.underlist]).replace(', ',',')[1:-1] + '\n'    
        #
        with open(self.filename, mode='wt+', encoding='utf-8') as f:
            f.write(self.orgstr)
            f.write(result_str)
    #
    def update(self):
        print('##### process CL list "%s" ... #####' % self.filename)
     
        self.reset()
        self.__getcls()
     
        self.diff1 = self.eurclset - self.korclset # - subclset
        self.diff2 = self.korclset - self.eurclset # - addclset
     
        # self.korclset = self.korclset.union(eurclset)
        # self.korclset = self.korclset.union(addclset)
        self.korclset = self.eurclset | self.addclset   # korclset에 넣어둔 CL때문에 잘못 나오는 것이 있어 로직 변경

        self.resultlist = list(self.korclset - self.subclset)
        self.mustlist = list(self.mustset)

        self.underlist = [ x for x in self.resultlist if int(x) < int(self.basecl) ]
        self.newcllist = [ x for x in self.resultlist if int(x) > int(self.basecl) ]

        self.underlist.sort()
        self.newcllist.sort()
        self.mustlist.sort()

        for cl in self.mustlist:
            if cl not in self.newcllist:
                self.newcllist.append(cl)
     
        self.__putcls()
        print('##### Completed #####\n')

        # prepare variables to build
        self.qb_base = str(self.basecl)
        self.qb_partials = str([int(x) for x in self.newcllist]).replace(', ',',')[1:-1]

##################################################################

class Release:
    def __init__(self):
        self.qb = QB()
        self.cl = CL(self.qb, CLLIST)
 
    def update(self):
        self.cl.update()

    def build(self, target='all'):
        for x in self.cl.config:
            print('   Triggering build for %s ...\n' % x)        
            cid = self.cl.confid.get(x, self.qb.qb_getconfid(x))
            if target=='user':
                self.qb.qb_build(x, self.cl.qb_base, self.cl.qb_partials, True)
            if target=='eng' :
                self.qb.qb_build(x, self.cl.qb_base, self.cl.qb_partials, False)
            if target=='all' :
                self.qb.qb_build(x, self.cl.qb_base, self.cl.qb_partials, True)
                time.sleep(2)
                self.qb.qb_build(x, self.cl.qb_base, self.cl.qb_partials, False)
            time.sleep(2)

##################################################################

# release = Release()

# update = lambda : release.u()
# build = lambda : release.b()

##################################################################

class ReleaseUI:
    user = ''
    password = ''

    def __qblogin(self):
        if self.user == '' or self.password == '':
           print('You should enter login information agian.\nMenu login finished.')

        print('Quickbuild log-in ...\n')
        self.release.qb.qblogin(self.user, self.password)

    def doLogin(self):
        if self.user == '':
            self.user = input('userName cannot be null.\nPlease enter userName : ')
        if self.password == '':
            self.password = input('password cannot be null.\nPlease enter password : ')

        self.__qblogin()

    def doReLogin(self):
        self.user = input('Please enter userName : ')
        self.password = input('Please tner password : ')
        self.doLogin()

    def __init__(self):
        print('Release Tool v1.0\n' + '-'*10 + '\n')
        print('Please be sure to place "cl_list.txt" at the same folder with this Program !!! \n')
        self.release = Release()
        self.doReLogin()

    def doUpdate(self):
        self.release.update()

    def doBuild(self, target='all'):
        if target == 'all' or target == 'user' or target == 'eng':
            self.release.build(target)
        else:
            print('Cannot recognize build option : %s' % target)
     
    def doHelp(self):
        print('Release Tool v1.0\n' + '-'*10 + '\n')
        print('Please be sure to place "cl_list.txt" at the same folder with this Program !!! \n')
        print('commands')
        print('- help or ? : this screen')
        print('- update : update cl_list.txt')
        print('- login  : login on Quickbuild server')
        print('- relogin  : login on Quickbuild server with new user and password')
        print('- build  : build with respect to the CLs on cl_list.txt')
        print('           build or build all - build user and eng binary')
        print('           build user         - build user binary')
        print('           build eng          - build eng binary')
        print('- exit   : Exit from menu')

    def __call__(self):
        while True:
            instr = input('Enter command ? ')
            command = re.findall(r'([\w]+|\?)', instr)
            cmd = [x.lower() for x in command]
            if len(cmd) == 0:
                continue

            if cmd[0] == 'update':
                self.doUpdate()
            elif cmd[0] == 'login':
                self.doLogin()
            elif cmd[0] == 'relogin':
                self.doReLogin()
            elif cmd[0] == 'build' :
                if len(cmd) > 1:
                    self.doBuild(target=cmd[1])
                else:
                    self.doBuild()
            elif cmd[0] == 'help' or cmd[0] == '?':
                self.doHelp()
            elif cmd[0] == 'exit':
                break;
            else:
                print('Invalid command : %s' % command[0])

if __name__ == '__main__':
    try:
        with open(CLLIST, 'rt') as f:
            pass
        R = ReleaseUI()
        R()
    except:
        print('Please be sure to place "cl_list.txt" at the same folder with this Program !!! \n')

댓글 없음:

댓글 쓰기

200926.가오리코스 라이딩

9/26 골절인지 아닌지 확인 안됨. 이후 미세골절여부 확인 핸드폰을 드는 것도 어려움 9/29 x ray 다시 찍지 않고 이후 재 방문 요청 ...