fromupstream: change bug, test, and cqdepend to save a list

args['bug'] is a string.  If multiple lines of bugs in args['bug'], the
content will be "bug1\nBUG=bug2\nBUG=bug3" (i.e. inline \nBUG= without a
prefix).  Before writing to the commit message, it prepends the prefix
'BUG='.  The implementation is suboptimal.

Changes to save a list of string.  Each string is a BUG= item.

The same changes also apply to test and cqdepend.

1. single -b

$ fromupstream.py --nosignoff -t none -b b:1 -c XXX \
  linux://238c30468f46b
$ git show -s
> [snip]
> BUG=b:1
> TEST=none
>
> Change-Id: XXX

2. multiple --crbug and --buganizer
$ fromupstream.py --nosignoff -t none -b b:1 --crbug 2 --crbug 3 \
  --buganizer 4 --buganizer 5 -c XXX linux://238c30468f46b
$ git show -s
> [snip]
> BUG=b:1, b:4, b:5, chromium:2, chromium:3
> TEST=none
>
> Change-Id: XXX

3. long test string

$ fromupstream.py --nosignoff -t "$(seq 1 100)" -b none -c XXX \
  linux://238c30468f46b
$ git show -s
> [snip]
> BUG=none
> TEST=1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
>      27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
>      50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
>      73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
>      96 97 98 99 100
>
> Change-Id: XXX

4. single --cqdepend

$ fromupstream.py --nosignoff -t none -b none --cqdepend XXX -c XXX \
  linux://238c30468f46b
$ git show -s
> [snip]
> BUG=none
> TEST=none
>
> Cq-Depend: XXX
> Change-Id: XXX

5. --replace multi-line BUG= TEST= Cq-Depend:

$ cat <<EOF | git commit --allow-empty -F -
> Commit title
>
> Description
>
> BUG=b:1
> TEST=1
> TEST=2
> TEST=line 1
> line 2
> TEST=another line 1
> another line 2
>
> TEST=yet another line 1
> yet another line 2
> BUG=b:2,b:3
>
> Cq-Depend: 1, 2, 3
> Cq-Depend: 4
> Change-Id: XXX
> EOF

$ fromupstream.py --nosignoff -r linux://238c30468f46b
$ git show -s
> [snip]
> BUG=b:1
> BUG=b:2,b:3
> TEST=1
> TEST=2
> TEST=line 1
> line 2
> TEST=another line 1
> another line 2
> TEST=yet another line 1
> yet another line 2
>
> Cq-Depend: 1, 2, 3
> Cq-Depend: 4
> Change-Id: XXX

BUG=none
TEST=as shown above

Change-Id: I8eddef3de14397d80c7dd1af2651e3053eee0940
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/dev-util/+/2356464
Tested-by: Tzung-Bi Shih <tzungbi@chromium.org>
Reviewed-by: Douglas Anderson <dianders@chromium.org>
Commit-Queue: Tzung-Bi Shih <tzungbi@chromium.org>
diff --git a/contrib/fromupstream.py b/contrib/fromupstream.py
index ce24178..c2743f8 100755
--- a/contrib/fromupstream.py
+++ b/contrib/fromupstream.py
@@ -448,16 +448,16 @@
 
     args = vars(parser.parse_args(args))
 
+    cq_depends = [args['cqdepend']] if args['cqdepend'] else []
+
     buglist = [args['bug']] if args['bug'] else []
     if args['buganizer']:
         buglist += ['b:{0}'.format(x) for x in args['buganizer']]
     if args['crbug']:
         buglist += ['chromium:{0}'.format(x) for x in args['crbug']]
-    if buglist:
-        args['bug'] = ', '.join(buglist)
+    bug_lines = [', '.join(buglist)] if buglist else []
 
-    if args['test']:
-        args['test'] = _wrap_commit_line('TEST', args['test'])
+    test_lines = [_wrap_commit_line('TEST', args['test'])] if args['test'] else []
 
     if args['replace']:
         old_commit_message = _git(['show', '-s', '--format=%B', 'HEAD'])
@@ -469,30 +469,28 @@
         if args['changeid'] is None and changeid_match:
             args['changeid'] = changeid_match.group(1)
 
-        cq_depends = re.findall(r'^Cq-Depend:\s+(.*)$',
-                                old_commit_message, re.MULTILINE)
-        if args['cqdepend'] is None and cq_depends:
-            args['cqdepend'] = '\nCq-Depend: '.join(cq_depends)
+        if not cq_depends:
+            cq_depends = re.findall(r'^Cq-Depend:\s+(.*)$',
+                                    old_commit_message, re.MULTILINE)
 
-        bugs = re.findall('^BUG=(.*)$', old_commit_message, re.MULTILINE)
-        if args['bug'] is None and bugs:
-            args['bug'] = '\nBUG='.join(bugs)
+        if not bug_lines:
+            bug_lines = re.findall(r'^BUG=(.*)$',
+                                   old_commit_message, re.MULTILINE)
 
-        # Note: use (?=...) to avoid to consume the source string
-        tests = re.findall(r"""
-            ^TEST=(.*?)     # Match start from TEST= until
-            \n              # (to remove the tailing newlines)
-            (?=^$|          # a blank line
-               ^Cq-Depend:| # or Cq-Depend:
-               ^Change-Id:| # or Change-Id:
-               ^BUG=|       # or following BUG=
-               ^TEST=)      # or another TEST=
-            """,
-            old_commit_message, re.MULTILINE | re.DOTALL | re.VERBOSE)
-        if args['test'] is None and tests:
-            args['test'] = '\nTEST='.join(tests)
+        if not test_lines:
+            # Note: use (?=...) to avoid to consume the source string
+            test_lines = re.findall(r"""
+                ^TEST=(.*?)     # Match start from TEST= until
+                \n              # (to remove the tailing newlines)
+                (?=^$|          # a blank line
+                   ^Cq-Depend:| # or Cq-Depend:
+                   ^Change-Id:| # or Change-Id:
+                   ^BUG=|       # or following BUG=
+                   ^TEST=)      # or another TEST=
+                """,
+                old_commit_message, re.MULTILINE | re.DOTALL | re.VERBOSE)
 
-    if args['bug'] is None or args['test'] is None:
+    if not bug_lines or not test_lines:
         parser.error('BUG=/TEST= lines are required; --replace can help '
                      'automate, or set via --bug/--test')
 
@@ -546,8 +544,10 @@
         # next commands know where to work on
         commit_message += '\n'
         commit_message += conflicts
-        commit_message += '\n' + 'BUG=' + args['bug']
-        commit_message += '\n' + 'TEST=' + args['test']
+        commit_message += '\n'
+        commit_message += '\n'.join('BUG=%s' % bug for bug in bug_lines)
+        commit_message += '\n'
+        commit_message += '\n'.join('TEST=%s' % t for t in test_lines)
 
         extra = []
         if args['signoff']:
@@ -574,9 +574,10 @@
                                     args['changeid'], commit_message)
             args['changeid'] = None
 
-        if args['cqdepend'] is not None:
+        if cq_depends:
             commit_message = re.sub(
-                r'(Change-Id: \w+)', r'Cq-Depend: %s\n\1' % args['cqdepend'],
+                r'(Change-Id: \w+)',
+                r'%s\n\1' % '\n'.join('Cq-Depend: %s' % c for c in cq_depends),
                 commit_message)
 
         # decorate it that it's from outside