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