blob: 49b4bfcb2b4c8d0f9437d620c49b394f924678a0 [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
60# the local csv file columns
61csv_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
150 try:
151 os.chdir(topdir + trunk_list[i]['gitPath'])
152 except:
153 exit('Error: NO such git path:', trunk_list[i]['gitPath'])
154
155 # compare trunk list and local blSrcGits, find match id
156 index = get_bltype_branch_id(trunk_list[i]['blType'], blSrcGits)
157 if index < 0:
158 exit("Error: NO found match blType:", trunk_list[i]['blType'])
159
160 # update target branch
161 git_src_update(os.getcwd(), blSrcGits[index]['gitRemote'], blSrcGits[index]['gitBranch'])
162
163 # run git log format and produce commit list
164 commit_list = git_cmt_parse(os.getcwd(), trunk_list[i]['lastCommit'], 'HEAD', 'TRUE')
165
166 git_cmt_2_csv(csvfile, trunk_list[i]['blType'], commit_list, blSrcGits[index], i)
167
168# Open json file, prase last commit
169def prase_json_file():
170 global trunk_list
171
172 with open(jsonfile,'r') as load_f:
173 try:
174 json_array = json.load(load_f, object_pairs_hook=OrderedDict)
175 except:
176 exit('Error: Incorrect json format!')
177
178 trunk_list = []
179 for item in json_array['source_gits']:
180 try:
181 store_details = {"blType":None, "gitPath":None, "lastCommit":None}
182 store_details['blType'] = item['blType']
183 store_details['gitPath'] = item['gitPath']
184 store_details['lastCommit'] = item['lastCommit']
185 trunk_list.append(store_details)
186 except:
187 exit('Error: get trunk last commit failed.\n')
188
189 return trunk_list
190
191# output instance of str
192def to_str(bytes_or_str):
193 if isinstance(bytes_or_str, bytes):
194 value = bytes_or_str.decode('utf-8')
195
196 else:
197 value = bytes_or_str
198
199 return value
200
201# run shell cmd return bytes
202def bash_command(cmd):
203 process = subprocess.Popen(cmd,
204 shell=True,
205 stdout=subprocess.PIPE,
206 stderr=subprocess.PIPE)
207
208 #stdout, stderr = process.communicate()
209
210 return process.stdout.read()
211
212# get commit list info
213def git_cmt_parse(gitPath, lastCommit, headCommit, isSrc):
214 local_path = os.path.join(gitPath)
215
216 repo = Repo(local_path)
217
218 # run git log --format
219 commit_log = repo.git.log('--pretty={"summary":"%s","commit":"%h","hash":"%H","author":"%ae","date":"%cd","pd":""}',
220 '--reverse', lastCommit + '...' + headCommit)
221
222 try:
223 log_list = commit_log.split("\n")
224 #if debug_enable:
225 # print(' > %s'%(log_list))
226 except:
227 pass
228
229 # deal with "Merge into" or "revert" commits
230 for i in range(len(log_list)):
231 try:
232 log_list[i] = str(re.sub(r'Merge "', r'Merge <', str(log_list[i])))
233 log_list[i] = str(re.sub(r'" into', r'> into', str(log_list[i])))
234 log_list[i] = str(re.sub(r'Revert "', r'Revert <', str(log_list[i])))
235 log_list[i] = str(re.sub(r'"",', r'>",', str(log_list[i])))
236
237 if debug_enable:
238 print(' > [%d] %s'%(i,log_list[i]))
239 except:
240 pass
241
242 # eval special special characters
243 try:
244 real_log_list = [eval(str(item)) for item in log_list]
245 except:
246 real_log_list = []
247 pass
248
249 # update real_log_list[i]['pd'] with JiraNo
250 for j in range(len(real_log_list)):
251 try:
252 cmd = 'git log ' + real_log_list[j]['commit'] + ' -1 | grep PD# | head -n 1'
253 res = to_str(bash_command(cmd)).replace('\n', '')
254
255 if res:
256 real_log_list[j]['pd'] = res.split("PD#")[1]
257
258 else:
259 real_log_list[j]['pd'] = 'NULL'
260
261 if debug_enable:
262 print(' > [%d] overwrite PD# = %s'%(j,real_log_list[j]['pd']))
263 except:
264 pass
265
266 try:
267 print(' > Commit list max number of rows = ', len(real_log_list))
268 except:
269 pass
270
271 return real_log_list
272
273# save commit info to csv
274def git_cmt_2_csv(csvfile, blType, commit_list, stream_dic, sheet_index):
275 global alignment
276
277 # Load a workbook object
278 wb = openpyxl.load_workbook(csvfile)
279
280 # Creat csv sheet named by blType
281 title = re.sub(r'[?|$|.|!|/|*]', r'_', blType)
282 sheet = wb.create_sheet(title, sheet_index)
283
284 sheet.title = title
285
286 # Set active sheet
287 wb.active = sheet_index
288
289 try:
290 # Set aligment
291 alignment = Alignment(horizontal="left", vertical="center", wrap_text=True)
292
293 for i in range(len(commit_list) + 1):
294
295 for j in range(len(csv_file_column)):
296 sheet.cell(row = i + 1, column = j + 1).alignment = alignment
297 except:
298 pass
299
300 # set cell(1,1): value and font
301 font = Font(size=11, bold=True)
302 sheet.cell(row = 1, column = 1).value = blType + " " + csv_file_column[0]['NAME']
303 sheet.cell(row = 1, column = 1).font = font
304
305 # set row 1: value and font
306 for i in range(1, len(csv_file_column)):
307 sheet.cell(row = 1, column = i + 1).value = csv_file_column[i]['NAME']
308 sheet.cell(row = 1, column = i + 1).font = font
309
310 # set row 1: height
311 # sheet.row_dimensions[1].height = 30
312
313 # set row 2-n: trunk commit
314 for i in range(len(commit_list)):
315 try:
316 # column 1: ID index
317 sheet.cell(row = i + 2, column = 1).value = i + 1
318
319 # column 2: Trunk Commit
320 if commit_list[i]['pd'] == 'NULL':
321 jira_pd = '\n'
322
323 else:
324 jira_pd = pdPrefix + commit_list[i]['pd'] + '\n'
325
326 sheet.cell(row = i + 2, column = 2).value = \
327 jira_pd + commit_list[i]['summary'] + '\n'\
328 'Commit: ' + commit_list[i]['commit'] + '\n'\
329 'Author: ' + commit_list[i]['author'] + '\n'\
330 'Date: ' + commit_list[i]['date']
331
332 # column 3:Trunk Cl Link
333 sheet.cell(row = i + 2, column = 3).value = scgitPrefix + \
334 stream_dic['upStream'] + \
335 commit_list[i]['hash']
336 except:
337 pass
338
339 # set column A-I width
340 for i in range(len(csv_file_column)):
341 sheet.column_dimensions[csv_file_column[i]['ID']].width = csv_file_column[i]['WIDTH']
342
343 # save the file
344 wb.save(csvfile)
345
346# Creat csv file to save cmt list
347def create_csv_file(outdir, inputfile):
348 global csvfile
349
350 # get csv file full name
351 localTime = time.strftime("_%Y%m%d_%H%M%S", time.localtime())
352
353 base_name=os.path.basename(inputfile)
354 file_name = os.path.splitext(base_name)[0]
355
356 csvfile = str(topdir) + str(outdir) + "/" + file_name + str(localTime) + ".xlsx"
357
358 # creat csv file, overwrite existing files
359 wb = openpyxl.Workbook()
360
361 wb.save(csvfile)
362
363# get argv
364def getOptions(args=sys.argv[1:]):
365 global jsonfile
366 global debug_enable
367
368 # crate prase progress
369 parser = argparse.ArgumentParser(description="Get all blx commits and save to csv file.")
370
371 # add argv support
372 parser.add_argument("-j", "--jsoncfg", required=True, help="Your input json file.")
373 parser.add_argument("-o", "--outDir", required=True, help="Your output .xlsx dir.")
374 parser.add_argument("-v", "--verbose", required=False, help="Increase output verbosity.", action="store_true")
375 parser.add_argument("-p", "--push", required=False, help="Push csv file to confluence.", action="store_true")
376
377 # prase argv
378 options = parser.parse_args(args)
379
380 # check debug on
381 if options.verbose:
382 debug_enable = 1
383 else:
384 debug_enable = 0
385
386 # check whether the outdir is available
387 jsonfile = str(options.jsoncfg)
388 if not os.path.isdir(options.outDir):
389 exit('Error: No such directory!')
390
391 # creat new csv file
392 create_csv_file(options.outDir, options.jsoncfg)
393 print('\n[CONFIG INFO]:')
394 print(' > Run in toplevel : ', topdir)
395 print(' > Input json file : ', options.jsoncfg)
396 print(' > Output xlsx dir : ', options.outDir)
397 print(' > Output xlsx name: ', csvfile)
398
399# Get the top-level directory which include .repo
400def get_top_dir():
401 global topdir
402
403 pwd = os.getcwd()
404 dirName = 'fip'
405
406 if not os.path.exists(dirName):
407 topdir = pwd + "/../"
408 else:
409 topdir = pwd + "/"
410
411 return topdir
412
413# Record summary info, and save to scv file
414def record_in_summary_sheet():
415 wb = openpyxl.load_workbook(csvfile)
416
417 default_ws = 'Sheet'
418 # active
419 for s in range(len(wb.sheetnames)):
420 if wb.sheetnames[s] == default_ws:
421 break
422
423 try:
424 wb.active = s
425 sheet = wb[default_ws]
426 sheet.title = 'summary'
427 except:
428 exit('Error: NOT found sheet: %s'%default_ws)
429
430 # set column width
431 try:
432 sheet.column_dimensions['A'].width = 10
433 sheet.column_dimensions['B'].width = 20
434 sheet.column_dimensions['C'].width = 15
435 except:
436 pass
437
438 # row 1: summary head
439 sheet.cell(row = 1, column = 1).value = 'ID'
440 sheet.cell(row = 1, column = 2).value = 'title'
441 sheet.cell(row = 1, column = 3).value = 'max_row'
442
443 # row n: summary info
444 for j in range(0,len(summary_list)):
445 try:
446 sheet.cell(row = j+2, column = 1).value = summary_list[j]['ID']
447 sheet.cell(row = j+2, column = 2).value = summary_list[j]['title']
448 sheet.cell(row = j+2, column = 3).value = summary_list[j]['max_row']
449
450 if debug_enable:
451 print('[%d] id:%d, title:%s, title:%2d'%(j+1,
452 summary_list[j]['ID'],
453 summary_list[j]['title'],
454 summary_list[j]['max_row']))
455 except:
456 pass
457
458 # Set aligment
459 for i in range(0, len(summary_list) + 1):
460
461 for j in range(0, 3):
462 try:
463 sheet.cell(row = i + 1, column = j + 1).alignment = alignment
464 except:
465 pass
466
467 wb.save(csvfile)
468 return
469
470# Printf scv file , such as max list num
471def summary_for_scv_sheets():
472 global summary_list
473
474 wb = openpyxl.load_workbook(csvfile)
475
476 summary_list = []
477
478 print('\n[SUMMARY SHEETS]:')
479 print('=========================================')
480
481 for i in range(0, len(wb.sheetnames)-1):
482 try:
483 sheet = wb[wb.sheetnames[i]]
484 print('[%d] Sheet: %-12s Max_Row: %2d'%(i+1, sheet.title, sheet.max_row-1))
485 summary_details = {"ID":0, "title":None, "max_row":0}
486 summary_details['ID'] = i+1
487 summary_details['title'] = sheet.title
488 summary_details['max_row'] = sheet.max_row-1
489 summary_list.append(summary_details)
490 except:
491 pass
492
493 print('=========================================\n')
494
495 #print('summary_list: ', summary_list)
496
497 wb.save(csvfile)
498 return
499
500# Main func
501if __name__ == "__main__":
502 # Set stderr with color
503 from IPython.core.ultratb import ColorTB
504 sys.excepthook = ColorTB()
505
506 # get repo top dir
507 get_top_dir()
508
509 # Prase argv in / out
510 getOptions(sys.argv[1:])
511
512 # Prase json file to get last commit
513 prase_json_file()
514
515 # Get commits list and src link
516 git_commits_to_src_link()
517
518 # Output csv summary
519 summary_for_scv_sheets()
520
521 # Record in summary sheet
522 record_in_summary_sheet()
523
524 print('OUTPUT csv: ', os.path.basename(csvfile))
525 exit('RUN OK !')
526