git-cl: add --preserve-tryjobs option for upload command.

CQ will soon start canceling tryjobs it triggered on no-longer-latest
patchsets if these tryjobs won't be re-used. New option can be used by
developers who upload new patchsets but actually care for prior
CQ-triggered tryjobs to complete.

This footer is effectively sticky, unless someone removes it by hand later.
We need to have the footer one way or another to tell the CQ what to do
(and provide a visible audit trail).

We may revisit the stickiness later based on accumulated usage data s.t.
the following uploads remove the footer unless the flag is passed.

Bug: 909895
Test: git cl upload --preserve-tryjobs
Change-Id: Ibbc6e917504c31eab8ab85296b3ecafd3add46df
Cq-Do-Not-Cancel-Tryjobs: true
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/1701506
Reviewed-by: Edward Lesmes <ehmaldonado@chromium.org>
Reviewed-by: Dirk Pranke <dpranke@chromium.org>
Commit-Queue: Andrii Shyshkalov <tandrii@chromium.org>
diff --git a/git_cl.py b/git_cl.py
index 9c7f76f..9b06bc0 100755
--- a/git_cl.py
+++ b/git_cl.py
@@ -2766,6 +2766,8 @@
       if options.reviewers or options.tbrs or options.add_owners_to:
         change_desc.update_reviewers(options.reviewers, options.tbrs,
                                      options.add_owners_to, change)
+      if options.preserve_tryjobs:
+        change_desc.set_preserve_tryjobs()
 
       remote, upstream_branch = self.FetchUpstreamTuple(self.GetBranch())
       parent = self._ComputeParent(remote, upstream_branch, custom_cl_base,
@@ -3239,6 +3241,14 @@
       if new_tbr_line:
         self.append_footer(new_tbr_line)
 
+  def set_preserve_tryjobs(self):
+    """Ensures description footer contains 'Cq-Do-Not-Cancel-Tryjobs: true'."""
+    footers = git_footers.parse_footers(self.description)
+    for v in footers.get('Cq-Do-Not-Cancel-Tryjobs', []):
+      if v.lower() == 'true':
+        return
+    self.append_footer('Cq-Do-Not-Cancel-Tryjobs: true')
+
   def prompt(self, bug=None, git_footer=True):
     """Asks the user to update the description."""
     self.set_description([
@@ -4751,6 +4761,10 @@
                     action='store_true',
                     help='Send the patchset to do a CQ dry run right after '
                          'upload.')
+  parser.add_option('--preserve-tryjobs', action='store_true',
+                    help='instruct the CQ to let tryjobs running even after '
+                         'new patchsets are uploaded instead of canceling '
+                         'prior patchset\' tryjobs')
   parser.add_option('--dependencies', action='store_true',
                     help='Uploads CLs of all the local branches that depend on '
                          'the current branch')