blob: f7dba38ef163a85b019d73d49a18d5853d6c4937 [file] [log] [blame]
dongqing.li93ffff32022-07-25 10:38:31 +08001#!/usr/bin/python3
2# -*- coding:UTF-8 -*-
3
4# =====================================================================
5# @module: Get all blx commits and save to csv file
6# @author: Li Dongqing (dongqing.li@amlogic.com)
7# @License: Copyright (c) 2019 Amlogic, Inc. All rights reserved.
8# @Changes:
9# 1. 2022.07.20 v0.1 create for stable branch update.
10# =====================================================================
11
dongqing.li23bd26f2022-12-05 20:30:43 +080012# Import modules
dongqing.li93ffff32022-07-25 10:38:31 +080013try:
14 import sys
15 import os
16 import re
17 import json
18 import time
19 import argparse
20 import subprocess
21 import IPython
22 import openpyxl
23 from git.repo import Repo
24 from openpyxl.styles import Font, Alignment
25 from collections import OrderedDict
26except Exception as e:
27 print(e)
dongqing.li23bd26f2022-12-05 20:30:43 +080028 exit('Please install modules, eg: pip3 install os')
dongqing.li93ffff32022-07-25 10:38:31 +080029
30# jason example: bl3.4.5-20220711-pre-ver.json
31# {
32# "source_gits" : [
33# {"blType" : "bl2_sc2", "gitPath" : "bl2/core_sc2", "lastCommit" : "feebe5301418c038a06d45d0216c780ae9ea0033"},
34# {"blType" : "bl2_s4", "gitPath" : "bl2/core_s4", "lastCommit" : "817779a738e99b81081a31035ed784840cace44c"},
35# {"blType" : "bl2_ree", "gitPath" : "bl2/ree", "lastCommit" : "09d5c246638e95e2598a264b36da7f6ede7f6ea8"},
36# {"blType" : "bl2_tee", "gitPath" : "bl2/tee", "lastCommit" : "115e9fc38721c3564435f582875fc02908787b53"},
37# {"blType" : "bl30_aocpu", "gitPath" : "bl30/src_ao", "lastCommit" : "8c0b17692bd51f9c3311f8c51cd28bdf808a27a3"},
38# {"blType" : "bl31_1.3", "gitPath" : "bl31_1.3/src", "lastCommit" : "cf6108ce548d7ad3bfe268dedf801358664b6ead"},
39# {"blType" : "bl32_3.8", "gitPath" : "bl32_3.8/src", "lastCommit" : "c5ef42f2ce59304e2a4df7cf2dcbb12ab7ccefd1"},
40# {"blType" : "bl33", "gitPath" : "bl33/v2019", "lastCommit" : "f03ed9bc121114e9f31f1ee924d3adc176f13faa"},
41# {"blType" : "fip", "gitPath" : "fip", "lastCommit" : "d60ea7c9adfe8e537d0a11d2c2ce8e8097de5035"}
42# ]
43# }
44
45# bootloader trunk branch and remote info
46pdPrefix = "https://jira.amlogic.com/browse/"
47scgitPrefix = "https://scgit.amlogic.com/plugins/gitiles/"
48blSrcGits = [
49 {"blType" : "bl2_sc2", "gitBranch" : "projects/sc2", "gitRemote" : "firmware", "upStream" : "bootloader/amlogic-advanced-bootloader/core/+/"},
50 {"blType" : "bl2_s4", "gitBranch" : "projects/s4", "gitRemote" : "firmware", "upStream" : "bootloader/amlogic-advanced-bootloader/core/+/"},
51 {"blType" : "bl2_ree", "gitBranch" : "projects/amlogic-dev", "gitRemote" : "firmware", "upStream" : "bootloader/amlogic-advanced-bootloader/ree/+/"},
52 {"blType" : "bl2_tee", "gitBranch" : "projects/amlogic-dev", "gitRemote" : "firmware", "upStream" : "bootloader/amlogic-advanced-bootloader/tee/+/"},
53 {"blType" : "bl30_aocpu", "gitBranch" : "projects/amlogic-dev", "gitRemote" : "firmware", "upStream" : "firmware/aocpu/+/"},
54 {"blType" : "bl31_1.3", "gitBranch" : "amlogic-dev-1.3", "gitRemote" : "firmware", "upStream" : "ARM-software/arm-trusted-firmware/+/"},
55 {"blType" : "bl32_3.8", "gitBranch" : "amlogic-dev-3.8.0", "gitRemote" : "firmware", "upStream" : "OP-TEE/optee_os/+/"},
56 {"blType" : "bl33", "gitBranch" : "amlogic-dev-2019", "gitRemote" : "uboot", "upStream" : "uboot/+/"},
57 {"blType" : "fip", "gitBranch" : "amlogic-dev", "gitRemote" : "fip", "upStream" : "amlogic/tools/fip/+/"}
58]
59
wenbo.wang79145cc2023-03-01 10:41:59 +080060# the local csv file columns
dongqing.li93ffff32022-07-25 10:38:31 +080061csv_file_column = [
62 {"ID" : "A", "WIDTH" : 12, "NAME" : "Index"},
63 {"ID" : "B", "WIDTH" : 45, "NAME" : "Trunk Commit"},
64 {"ID" : "C", "WIDTH" : 30, "NAME" : "Trunk Cl Link"},
65 {"ID" : "D", "WIDTH" : 20, "NAME" : "Is Force Patch?"},
66 {"ID" : "E", "WIDTH" : 20, "NAME" : "Is Secure Patch?"},
67 {"ID" : "F", "WIDTH" : 20, "NAME" : "Reviewer"},
68 {"ID" : "G", "WIDTH" : 16, "NAME" : "Related to other CL ?"},
69 {"ID" : "H", "WIDTH" : 20, "NAME" : "QA Test Cases"},
70 {"ID" : "I", "WIDTH" : 16, "NAME" : "QA Verify Result"},
71 {"ID" : "J", "WIDTH" : 20, "NAME" : "New CL"}
72]
73
74# git update branch
75def git_src_update(gitPath, gitRemote, gitBranch):
76 local_path = os.path.join(gitPath)
77 repo = Repo(local_path)
78
79 try:
80 repo.git.branch('-D', 'test')
81 except:
82 pass
83
84 try:
85 repo.git.checkout('-b', 'test')
86 except:
87 try:
88 repo.git.checkout('test')
89 except:
90 pass
91 pass
92
93 try:
94 repo.git.branch('-D', gitBranch)
95 except:
96 pass
97
98 try:
99 #repo.git.clean('-d', '-fx')
100 repo.git.checkout('-t', 'remotes/' + gitRemote + '/' + gitBranch)
101 except:
102 try:
103 repo.git.checkout(gitBranch)
104 except:
105 exit('Error: check out branch (%s / %s) failed!'%(gitRemote,gitBranch))
106 pass
107
108 try:
109 repo.git.branch('-D', 'test')
110 except:
111 pass
112
113 try:
114 repo.git.fetch('--all')
115 except:
116 pass
117
118 try:
119 repo.git.reset('--hard', gitRemote + '/' + gitBranch)
120 except:
121 exit('Error: git reset branch (%s / %s) failed!'%(gitRemote,gitBranch))
122
123 try:
124 #repo.git.pull(gitRemote, gitBranch)
125 repo.git.pull()
126 except:
127 exit('Error: git pull branch (%s) failed!'%gitBranch)
128
129# git update branch
130def get_bltype_branch_id(bltype, list):
131 for i in range(len(list)):
132 stream_dic = {"blType":None, "gitBranch":None, "gitRemote":None, "upStream":None}
133 stream_dic = list[i]
134
135 if str(bltype) == str(stream_dic['blType']):
136 print(' > Match the local bltype ID = ', i+1)
137 return i
138
139 else:
140 return -1
141
142# Prase json array to get commits info
143def git_commits_to_src_link():
144 print('\n[TRUNK LIST]:')
145
146 # prase each repo
147 for i in range(len(trunk_list)):
148 print(' > [%d] gitPath: %-12s lastCommit: %s'%(i+1, trunk_list[i]['gitPath'], trunk_list[i]['lastCommit']))
149
dongqing.lia8d42d02023-02-22 17:04:33 +0800150 if len(trunk_list[i]['lastCommit']) == 0:
151 print(' > lastCommit is NULL !')
152 continue
153
dongqing.li93ffff32022-07-25 10:38:31 +0800154 try:
155 os.chdir(topdir + trunk_list[i]['gitPath'])
156 except:
157 exit('Error: NO such git path:', trunk_list[i]['gitPath'])
158
159 # compare trunk list and local blSrcGits, find match id
160 index = get_bltype_branch_id(trunk_list[i]['blType'], blSrcGits)
161 if index < 0:
162 exit("Error: NO found match blType:", trunk_list[i]['blType'])
163
164 # update target branch
165 git_src_update(os.getcwd(), blSrcGits[index]['gitRemote'], blSrcGits[index]['gitBranch'])
166
167 # run git log format and produce commit list
168 commit_list = git_cmt_parse(os.getcwd(), trunk_list[i]['lastCommit'], 'HEAD', 'TRUE')
169
170 git_cmt_2_csv(csvfile, trunk_list[i]['blType'], commit_list, blSrcGits[index], i)
171
172# Open json file, prase last commit
173def prase_json_file():
174 global trunk_list
175
176 with open(jsonfile,'r') as load_f:
177 try:
178 json_array = json.load(load_f, object_pairs_hook=OrderedDict)
179 except:
180 exit('Error: Incorrect json format!')
181
182 trunk_list = []
183 for item in json_array['source_gits']:
184 try:
185 store_details = {"blType":None, "gitPath":None, "lastCommit":None}
186 store_details['blType'] = item['blType']
187 store_details['gitPath'] = item['gitPath']
188 store_details['lastCommit'] = item['lastCommit']
189 trunk_list.append(store_details)
190 except:
191 exit('Error: get trunk last commit failed.\n')
192
193 return trunk_list
194
195# output instance of str
196def to_str(bytes_or_str):
197 if isinstance(bytes_or_str, bytes):
198 value = bytes_or_str.decode('utf-8')
199
200 else:
201 value = bytes_or_str
202
203 return value
204
205# run shell cmd return bytes
206def bash_command(cmd):
207 process = subprocess.Popen(cmd,
208 shell=True,
209 stdout=subprocess.PIPE,
210 stderr=subprocess.PIPE)
211
212 #stdout, stderr = process.communicate()
213
214 return process.stdout.read()
215
216# get commit list info
217def git_cmt_parse(gitPath, lastCommit, headCommit, isSrc):
218 local_path = os.path.join(gitPath)
219
220 repo = Repo(local_path)
221
222 # run git log --format
223 commit_log = repo.git.log('--pretty={"summary":"%s","commit":"%h","hash":"%H","author":"%ae","date":"%cd","pd":""}',
224 '--reverse', lastCommit + '...' + headCommit)
225
226 try:
227 log_list = commit_log.split("\n")
228 #if debug_enable:
229 # print(' > %s'%(log_list))
230 except:
231 pass
wenbo.wang79145cc2023-03-01 10:41:59 +0800232 #Remove invalid commits
233 autosubmit = []
234 log_list_len = len(log_list)
235 for i in range(log_list_len):
236 if "gerrit.autosubmit@amlogic.com" in log_list[i]:
237 autosubmit.append(i)
238 log_list = [log_list[i] for i in range(log_list_len) if (i not in autosubmit)]
dongqing.li93ffff32022-07-25 10:38:31 +0800239
wenbo.wang79145cc2023-03-01 10:41:59 +0800240 # deal with "Merge into" or "revert" commits
dongqing.li93ffff32022-07-25 10:38:31 +0800241 for i in range(len(log_list)):
242 try:
243 log_list[i] = str(re.sub(r'Merge "', r'Merge <', str(log_list[i])))
244 log_list[i] = str(re.sub(r'" into', r'> into', str(log_list[i])))
245 log_list[i] = str(re.sub(r'Revert "', r'Revert <', str(log_list[i])))
246 log_list[i] = str(re.sub(r'"",', r'>",', str(log_list[i])))
dongqing.lia8d42d02023-02-22 17:04:33 +0800247 log_list[i] = str(re.sub(r' "', r' <', str(log_list[i])))
248 log_list[i] = str(re.sub(r'" ', r'> ', str(log_list[i])))
dongqing.li93ffff32022-07-25 10:38:31 +0800249
250 if debug_enable:
251 print(' > [%d] %s'%(i,log_list[i]))
252 except:
253 pass
dongqing.li93ffff32022-07-25 10:38:31 +0800254 # eval special special characters
255 try:
256 real_log_list = [eval(str(item)) for item in log_list]
257 except:
258 real_log_list = []
dongqing.lia8d42d02023-02-22 17:04:33 +0800259 if debug_enable:
260 print(' > eval(str(item)) ERROR!')
261 print(' > %s'%(log_list))
dongqing.li93ffff32022-07-25 10:38:31 +0800262 pass
dongqing.li93ffff32022-07-25 10:38:31 +0800263 # update real_log_list[i]['pd'] with JiraNo
264 for j in range(len(real_log_list)):
265 try:
266 cmd = 'git log ' + real_log_list[j]['commit'] + ' -1 | grep PD# | head -n 1'
267 res = to_str(bash_command(cmd)).replace('\n', '')
268
269 if res:
270 real_log_list[j]['pd'] = res.split("PD#")[1]
271
272 else:
273 real_log_list[j]['pd'] = 'NULL'
274
275 if debug_enable:
276 print(' > [%d] overwrite PD# = %s'%(j,real_log_list[j]['pd']))
277 except:
278 pass
279
280 try:
281 print(' > Commit list max number of rows = ', len(real_log_list))
282 except:
283 pass
284
285 return real_log_list
286
287# save commit info to csv
288def git_cmt_2_csv(csvfile, blType, commit_list, stream_dic, sheet_index):
289 global alignment
290
291 # Load a workbook object
292 wb = openpyxl.load_workbook(csvfile)
293
294 # Creat csv sheet named by blType
295 title = re.sub(r'[?|$|.|!|/|*]', r'_', blType)
296 sheet = wb.create_sheet(title, sheet_index)
297
298 sheet.title = title
299
300 # Set active sheet
301 wb.active = sheet_index
302
303 try:
304 # Set aligment
305 alignment = Alignment(horizontal="left", vertical="center", wrap_text=True)
306
307 for i in range(len(commit_list) + 1):
308
309 for j in range(len(csv_file_column)):
310 sheet.cell(row = i + 1, column = j + 1).alignment = alignment
311 except:
312 pass
313
314 # set cell(1,1): value and font
315 font = Font(size=11, bold=True)
316 sheet.cell(row = 1, column = 1).value = blType + " " + csv_file_column[0]['NAME']
317 sheet.cell(row = 1, column = 1).font = font
318
319 # set row 1: value and font
320 for i in range(1, len(csv_file_column)):
321 sheet.cell(row = 1, column = i + 1).value = csv_file_column[i]['NAME']
322 sheet.cell(row = 1, column = i + 1).font = font
323
324 # set row 1: height
325 # sheet.row_dimensions[1].height = 30
326
327 # set row 2-n: trunk commit
328 for i in range(len(commit_list)):
329 try:
330 # column 1: ID index
331 sheet.cell(row = i + 2, column = 1).value = i + 1
332
333 # column 2: Trunk Commit
334 if commit_list[i]['pd'] == 'NULL':
335 jira_pd = '\n'
336
337 else:
338 jira_pd = pdPrefix + commit_list[i]['pd'] + '\n'
339
340 sheet.cell(row = i + 2, column = 2).value = \
341 jira_pd + commit_list[i]['summary'] + '\n'\
342 'Commit: ' + commit_list[i]['commit'] + '\n'\
343 'Author: ' + commit_list[i]['author'] + '\n'\
344 'Date: ' + commit_list[i]['date']
345
346 # column 3:Trunk Cl Link
347 sheet.cell(row = i + 2, column = 3).value = scgitPrefix + \
348 stream_dic['upStream'] + \
349 commit_list[i]['hash']
350 except:
351 pass
352
353 # set column A-I width
354 for i in range(len(csv_file_column)):
355 sheet.column_dimensions[csv_file_column[i]['ID']].width = csv_file_column[i]['WIDTH']
356
357 # save the file
358 wb.save(csvfile)
359
360# Creat csv file to save cmt list
361def create_csv_file(outdir, inputfile):
362 global csvfile
363
364 # get csv file full name
365 localTime = time.strftime("_%Y%m%d_%H%M%S", time.localtime())
366
367 base_name=os.path.basename(inputfile)
368 file_name = os.path.splitext(base_name)[0]
369
370 csvfile = str(topdir) + str(outdir) + "/" + file_name + str(localTime) + ".xlsx"
371
372 # creat csv file, overwrite existing files
373 wb = openpyxl.Workbook()
374
375 wb.save(csvfile)
376
377# get argv
378def getOptions(args=sys.argv[1:]):
379 global jsonfile
380 global debug_enable
381
382 # crate prase progress
383 parser = argparse.ArgumentParser(description="Get all blx commits and save to csv file.")
384
385 # add argv support
386 parser.add_argument("-j", "--jsoncfg", required=True, help="Your input json file.")
387 parser.add_argument("-o", "--outDir", required=True, help="Your output .xlsx dir.")
388 parser.add_argument("-v", "--verbose", required=False, help="Increase output verbosity.", action="store_true")
389 parser.add_argument("-p", "--push", required=False, help="Push csv file to confluence.", action="store_true")
390
391 # prase argv
392 options = parser.parse_args(args)
393
394 # check debug on
395 if options.verbose:
396 debug_enable = 1
397 else:
398 debug_enable = 0
399
400 # check whether the outdir is available
401 jsonfile = str(options.jsoncfg)
402 if not os.path.isdir(options.outDir):
403 exit('Error: No such directory!')
404
405 # creat new csv file
406 create_csv_file(options.outDir, options.jsoncfg)
407 print('\n[CONFIG INFO]:')
408 print(' > Run in toplevel : ', topdir)
409 print(' > Input json file : ', options.jsoncfg)
410 print(' > Output xlsx dir : ', options.outDir)
411 print(' > Output xlsx name: ', csvfile)
412
413# Get the top-level directory which include .repo
414def get_top_dir():
415 global topdir
416
417 pwd = os.getcwd()
418 dirName = 'fip'
419
420 if not os.path.exists(dirName):
421 topdir = pwd + "/../"
422 else:
423 topdir = pwd + "/"
424
425 return topdir
426
427# Record summary info, and save to scv file
428def record_in_summary_sheet():
429 wb = openpyxl.load_workbook(csvfile)
430
431 default_ws = 'Sheet'
432 # active
433 for s in range(len(wb.sheetnames)):
434 if wb.sheetnames[s] == default_ws:
435 break
436
437 try:
438 wb.active = s
439 sheet = wb[default_ws]
440 sheet.title = 'summary'
441 except:
442 exit('Error: NOT found sheet: %s'%default_ws)
443
444 # set column width
445 try:
446 sheet.column_dimensions['A'].width = 10
447 sheet.column_dimensions['B'].width = 20
448 sheet.column_dimensions['C'].width = 15
449 except:
450 pass
451
452 # row 1: summary head
453 sheet.cell(row = 1, column = 1).value = 'ID'
454 sheet.cell(row = 1, column = 2).value = 'title'
455 sheet.cell(row = 1, column = 3).value = 'max_row'
456
457 # row n: summary info
458 for j in range(0,len(summary_list)):
459 try:
460 sheet.cell(row = j+2, column = 1).value = summary_list[j]['ID']
461 sheet.cell(row = j+2, column = 2).value = summary_list[j]['title']
462 sheet.cell(row = j+2, column = 3).value = summary_list[j]['max_row']
463
464 if debug_enable:
465 print('[%d] id:%d, title:%s, title:%2d'%(j+1,
466 summary_list[j]['ID'],
467 summary_list[j]['title'],
468 summary_list[j]['max_row']))
469 except:
470 pass
471
472 # Set aligment
473 for i in range(0, len(summary_list) + 1):
474
475 for j in range(0, 3):
476 try:
477 sheet.cell(row = i + 1, column = j + 1).alignment = alignment
478 except:
479 pass
480
481 wb.save(csvfile)
482 return
483
484# Printf scv file , such as max list num
485def summary_for_scv_sheets():
486 global summary_list
487
488 wb = openpyxl.load_workbook(csvfile)
489
490 summary_list = []
491
492 print('\n[SUMMARY SHEETS]:')
493 print('=========================================')
494
495 for i in range(0, len(wb.sheetnames)-1):
496 try:
497 sheet = wb[wb.sheetnames[i]]
498 print('[%d] Sheet: %-12s Max_Row: %2d'%(i+1, sheet.title, sheet.max_row-1))
499 summary_details = {"ID":0, "title":None, "max_row":0}
500 summary_details['ID'] = i+1
501 summary_details['title'] = sheet.title
502 summary_details['max_row'] = sheet.max_row-1
503 summary_list.append(summary_details)
504 except:
505 pass
506
507 print('=========================================\n')
508
509 #print('summary_list: ', summary_list)
510
511 wb.save(csvfile)
512 return
513
514# Main func
515if __name__ == "__main__":
516 # Set stderr with color
517 from IPython.core.ultratb import ColorTB
518 sys.excepthook = ColorTB()
519
520 # get repo top dir
521 get_top_dir()
522
523 # Prase argv in / out
524 getOptions(sys.argv[1:])
525
526 # Prase json file to get last commit
527 prase_json_file()
528
529 # Get commits list and src link
530 git_commits_to_src_link()
531
532 # Output csv summary
533 summary_for_scv_sheets()
534
535 # Record in summary sheet
536 record_in_summary_sheet()
537
538 print('OUTPUT csv: ', os.path.basename(csvfile))
539 exit('RUN OK !')
540