blob: 559149aac9a342b212ea18bb596a3f26f19f9360 [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])))
dongqing.li93ffff32022-07-25 10:38:31 +0800260
261 if debug_enable:
262 print(' > [%d] %s'%(i,log_list[i]))
263 except:
264 pass
dongqing.li93ffff32022-07-25 10:38:31 +0800265 # eval special special characters
266 try:
267 real_log_list = [eval(str(item)) for item in log_list]
268 except:
269 real_log_list = []
dongqing.lia8d42d02023-02-22 17:04:33 +0800270 if debug_enable:
271 print(' > eval(str(item)) ERROR!')
272 print(' > %s'%(log_list))
dongqing.li93ffff32022-07-25 10:38:31 +0800273 pass
dongqing.li93ffff32022-07-25 10:38:31 +0800274 # update real_log_list[i]['pd'] with JiraNo
275 for j in range(len(real_log_list)):
276 try:
277 cmd = 'git log ' + real_log_list[j]['commit'] + ' -1 | grep PD# | head -n 1'
278 res = to_str(bash_command(cmd)).replace('\n', '')
279
280 if res:
281 real_log_list[j]['pd'] = res.split("PD#")[1]
282
283 else:
284 real_log_list[j]['pd'] = 'NULL'
285
286 if debug_enable:
287 print(' > [%d] overwrite PD# = %s'%(j,real_log_list[j]['pd']))
288 except:
289 pass
290
291 try:
292 print(' > Commit list max number of rows = ', len(real_log_list))
293 except:
294 pass
295
296 return real_log_list
297
298# save commit info to csv
299def git_cmt_2_csv(csvfile, blType, commit_list, stream_dic, sheet_index):
300 global alignment
301
302 # Load a workbook object
303 wb = openpyxl.load_workbook(csvfile)
304
305 # Creat csv sheet named by blType
306 title = re.sub(r'[?|$|.|!|/|*]', r'_', blType)
307 sheet = wb.create_sheet(title, sheet_index)
308
309 sheet.title = title
310
311 # Set active sheet
312 wb.active = sheet_index
313
314 try:
315 # Set aligment
316 alignment = Alignment(horizontal="left", vertical="center", wrap_text=True)
317
318 for i in range(len(commit_list) + 1):
319
320 for j in range(len(csv_file_column)):
321 sheet.cell(row = i + 1, column = j + 1).alignment = alignment
322 except:
323 pass
324
325 # set cell(1,1): value and font
326 font = Font(size=11, bold=True)
327 sheet.cell(row = 1, column = 1).value = blType + " " + csv_file_column[0]['NAME']
328 sheet.cell(row = 1, column = 1).font = font
329
330 # set row 1: value and font
331 for i in range(1, len(csv_file_column)):
332 sheet.cell(row = 1, column = i + 1).value = csv_file_column[i]['NAME']
333 sheet.cell(row = 1, column = i + 1).font = font
334
335 # set row 1: height
336 # sheet.row_dimensions[1].height = 30
337
338 # set row 2-n: trunk commit
339 for i in range(len(commit_list)):
340 try:
341 # column 1: ID index
342 sheet.cell(row = i + 2, column = 1).value = i + 1
343
344 # column 2: Trunk Commit
345 if commit_list[i]['pd'] == 'NULL':
346 jira_pd = '\n'
347
348 else:
349 jira_pd = pdPrefix + commit_list[i]['pd'] + '\n'
350
351 sheet.cell(row = i + 2, column = 2).value = \
352 jira_pd + commit_list[i]['summary'] + '\n'\
353 'Commit: ' + commit_list[i]['commit'] + '\n'\
354 'Author: ' + commit_list[i]['author'] + '\n'\
355 'Date: ' + commit_list[i]['date']
356
357 # column 3:Trunk Cl Link
358 sheet.cell(row = i + 2, column = 3).value = scgitPrefix + \
359 stream_dic['upStream'] + \
360 commit_list[i]['hash']
361 except:
362 pass
363
364 # set column A-I width
365 for i in range(len(csv_file_column)):
366 sheet.column_dimensions[csv_file_column[i]['ID']].width = csv_file_column[i]['WIDTH']
367
368 # save the file
369 wb.save(csvfile)
370
371# Creat csv file to save cmt list
372def create_csv_file(outdir, inputfile):
373 global csvfile
374
375 # get csv file full name
376 localTime = time.strftime("_%Y%m%d_%H%M%S", time.localtime())
377
378 base_name=os.path.basename(inputfile)
379 file_name = os.path.splitext(base_name)[0]
380
381 csvfile = str(topdir) + str(outdir) + "/" + file_name + str(localTime) + ".xlsx"
382
383 # creat csv file, overwrite existing files
384 wb = openpyxl.Workbook()
385
386 wb.save(csvfile)
387
388# get argv
389def getOptions(args=sys.argv[1:]):
390 global jsonfile
391 global debug_enable
392
393 # crate prase progress
394 parser = argparse.ArgumentParser(description="Get all blx commits and save to csv file.")
395
396 # add argv support
397 parser.add_argument("-j", "--jsoncfg", required=True, help="Your input json file.")
398 parser.add_argument("-o", "--outDir", required=True, help="Your output .xlsx dir.")
399 parser.add_argument("-v", "--verbose", required=False, help="Increase output verbosity.", action="store_true")
400 parser.add_argument("-p", "--push", required=False, help="Push csv file to confluence.", action="store_true")
401
402 # prase argv
403 options = parser.parse_args(args)
404
405 # check debug on
406 if options.verbose:
407 debug_enable = 1
408 else:
409 debug_enable = 0
410
411 # check whether the outdir is available
412 jsonfile = str(options.jsoncfg)
413 if not os.path.isdir(options.outDir):
414 exit('Error: No such directory!')
415
416 # creat new csv file
417 create_csv_file(options.outDir, options.jsoncfg)
418 print('\n[CONFIG INFO]:')
419 print(' > Run in toplevel : ', topdir)
420 print(' > Input json file : ', options.jsoncfg)
421 print(' > Output xlsx dir : ', options.outDir)
422 print(' > Output xlsx name: ', csvfile)
423
424# Get the top-level directory which include .repo
425def get_top_dir():
426 global topdir
427
428 pwd = os.getcwd()
429 dirName = 'fip'
430
431 if not os.path.exists(dirName):
432 topdir = pwd + "/../"
433 else:
434 topdir = pwd + "/"
435
436 return topdir
437
438# Record summary info, and save to scv file
439def record_in_summary_sheet():
440 wb = openpyxl.load_workbook(csvfile)
441
442 default_ws = 'Sheet'
443 # active
444 for s in range(len(wb.sheetnames)):
445 if wb.sheetnames[s] == default_ws:
446 break
447
448 try:
449 wb.active = s
450 sheet = wb[default_ws]
451 sheet.title = 'summary'
452 except:
453 exit('Error: NOT found sheet: %s'%default_ws)
454
455 # set column width
456 try:
457 sheet.column_dimensions['A'].width = 10
458 sheet.column_dimensions['B'].width = 20
459 sheet.column_dimensions['C'].width = 15
460 except:
461 pass
462
463 # row 1: summary head
464 sheet.cell(row = 1, column = 1).value = 'ID'
465 sheet.cell(row = 1, column = 2).value = 'title'
466 sheet.cell(row = 1, column = 3).value = 'max_row'
467
468 # row n: summary info
469 for j in range(0,len(summary_list)):
470 try:
471 sheet.cell(row = j+2, column = 1).value = summary_list[j]['ID']
472 sheet.cell(row = j+2, column = 2).value = summary_list[j]['title']
473 sheet.cell(row = j+2, column = 3).value = summary_list[j]['max_row']
474
475 if debug_enable:
476 print('[%d] id:%d, title:%s, title:%2d'%(j+1,
477 summary_list[j]['ID'],
478 summary_list[j]['title'],
479 summary_list[j]['max_row']))
480 except:
481 pass
482
483 # Set aligment
484 for i in range(0, len(summary_list) + 1):
485
486 for j in range(0, 3):
487 try:
488 sheet.cell(row = i + 1, column = j + 1).alignment = alignment
489 except:
490 pass
491
492 wb.save(csvfile)
493 return
494
495# Printf scv file , such as max list num
496def summary_for_scv_sheets():
497 global summary_list
498
499 wb = openpyxl.load_workbook(csvfile)
500
501 summary_list = []
502
503 print('\n[SUMMARY SHEETS]:')
504 print('=========================================')
505
506 for i in range(0, len(wb.sheetnames)-1):
507 try:
508 sheet = wb[wb.sheetnames[i]]
509 print('[%d] Sheet: %-12s Max_Row: %2d'%(i+1, sheet.title, sheet.max_row-1))
510 summary_details = {"ID":0, "title":None, "max_row":0}
511 summary_details['ID'] = i+1
512 summary_details['title'] = sheet.title
513 summary_details['max_row'] = sheet.max_row-1
514 summary_list.append(summary_details)
515 except:
516 pass
517
518 print('=========================================\n')
519
520 #print('summary_list: ', summary_list)
521
522 wb.save(csvfile)
523 return
524
525# Main func
526if __name__ == "__main__":
527 # Set stderr with color
528 from IPython.core.ultratb import ColorTB
529 sys.excepthook = ColorTB()
530
531 # get repo top dir
532 get_top_dir()
533
534 # Prase argv in / out
535 getOptions(sys.argv[1:])
536
537 # Prase json file to get last commit
538 prase_json_file()
539
540 # Get commits list and src link
541 git_commits_to_src_link()
542
543 # Output csv summary
544 summary_for_scv_sheets()
545
546 # Record in summary sheet
547 record_in_summary_sheet()
548
549 print('OUTPUT csv: ', os.path.basename(csvfile))
550 exit('RUN OK !')
551