1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# Copyright 2020 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Git helper functions."""
8
9from __future__ import print_function
10
11import collections
12import os
13import re
14import subprocess
15import tempfile
16
17CommitContents = collections.namedtuple('CommitContents', ['url', 'cl_number'])
18
19
20def InChroot():
21  """Returns True if currently in the chroot."""
22  return 'CROS_WORKON_SRCROOT' in os.environ
23
24
25def VerifyOutsideChroot():
26  """Checks whether the script invoked was executed in the chroot.
27
28  Raises:
29    AssertionError: The script was run inside the chroot.
30  """
31
32  assert not InChroot(), 'Script should be run outside the chroot.'
33
34
35def CreateBranch(repo, branch):
36  """Creates a branch in the given repo.
37
38  Args:
39    repo: The absolute path to the repo.
40    branch: The name of the branch to create.
41
42  Raises:
43    ValueError: Failed to create a repo in that directory.
44  """
45
46  if not os.path.isdir(repo):
47    raise ValueError('Invalid directory path provided: %s' % repo)
48
49  subprocess.check_output(['git', '-C', repo, 'reset', 'HEAD', '--hard'])
50
51  subprocess.check_output(['repo', 'start', branch], cwd=repo)
52
53
54def DeleteBranch(repo, branch):
55  """Deletes a branch in the given repo.
56
57  Args:
58    repo: The absolute path of the repo.
59    branch: The name of the branch to delete.
60
61  Raises:
62    ValueError: Failed to delete the repo in that directory.
63  """
64
65  if not os.path.isdir(repo):
66    raise ValueError('Invalid directory path provided: %s' % repo)
67
68  subprocess.check_output(['git', '-C', repo, 'checkout', 'cros/master'])
69
70  subprocess.check_output(['git', '-C', repo, 'reset', 'HEAD', '--hard'])
71
72  subprocess.check_output(['git', '-C', repo, 'branch', '-D', branch])
73
74
75def UploadChanges(repo, branch, commit_messages):
76  """Uploads the changes in the specifed branch of the given repo for review.
77
78  Args:
79    repo: The absolute path to the repo where changes were made.
80    branch: The name of the branch to upload.
81    commit_messages: A string of commit message(s) (i.e. '[message]'
82    of the changes made.
83
84  Returns:
85    A nametuple that has two (key, value) pairs, where the first pair is the
86    Gerrit commit URL and the second pair is the change list number.
87
88  Raises:
89    ValueError: Failed to create a commit or failed to upload the
90    changes for review.
91  """
92
93  if not os.path.isdir(repo):
94    raise ValueError('Invalid path provided: %s' % repo)
95
96  # Create a git commit.
97  with tempfile.NamedTemporaryFile(mode='w+t') as f:
98    f.write('\n'.join(commit_messages))
99    f.flush()
100
101    subprocess.check_output(['git', 'commit', '-F', f.name], cwd=repo)
102
103  # Upload the changes for review.
104  out = subprocess.check_output(
105      ['repo', 'upload', '--yes', '--ne', '--no-verify',
106       '--br=%s' % branch],
107      stderr=subprocess.STDOUT,
108      cwd=repo,
109      encoding='utf-8')
110
111  print(out)
112
113  found_url = re.search(
114      r'https://chromium-review.googlesource.com/c/'
115      r'chromiumos/overlays/chromiumos-overlay/\+/([0-9]+)', out.rstrip())
116
117  if not found_url:
118    raise ValueError('Failed to find change list URL.')
119
120  return CommitContents(
121      url=found_url.group(0), cl_number=int(found_url.group(1)))
122