Bump deploy timeout
[apex.git] / apex / build_utils.py
1 ##############################################################################
2 # Copyright (c) 2017 Feng Pan (fpan@redhat.com) and others.
3 #
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
9
10 import argparse
11 import git
12 import logging
13 import os
14 from pygerrit2.rest import GerritRestAPI
15 import re
16 import shutil
17 import sys
18
19 from apex.common import constants as con
20 from urllib.parse import quote_plus
21
22
23 def get_change(url, repo, branch, change_id):
24     """
25     Fetches a change from upstream repo
26     :param url: URL of upstream gerrit
27     :param repo: name of repo
28     :param branch: branch of repo
29     :param change_id: SHA change id
30     :return: change if found and not abandoned, closed
31     """
32     rest = GerritRestAPI(url=url)
33     change_path = "{}~{}~{}".format(quote_plus(repo), quote_plus(branch),
34                                     change_id)
35     change_str = "changes/{}?o=CURRENT_REVISION".format(change_path)
36     change = rest.get(change_str)
37     try:
38         assert change['status'] not in 'ABANDONED' 'CLOSED', \
39             'Change {} is in {} state'.format(change_id, change['status'])
40         logging.debug('Change found: {}'.format(change))
41         return change
42
43     except KeyError:
44         logging.error('Failed to get valid change data structure from url '
45                       '{}/{}, data returned: \n{}'
46                       .format(change_id, change_str, change))
47         raise
48
49
50 def clone_fork(args):
51     ref = None
52     logging.info("Cloning {}".format(args.repo))
53
54     try:
55         cm = git.Repo(search_parent_directories=True).commit().message
56     except git.exc.InvalidGitRepositoryError:
57         logging.debug('Current Apex directory is not a git repo: {}'
58                       .format(os.getcwd()))
59         cm = ''
60
61     logging.info("Current commit message: {}".format(cm))
62     m = re.search('{}:\s*(\S+)'.format(args.repo), cm)
63
64     if m:
65         change_id = m.group(1)
66         logging.info("Using change ID {} from {}".format(change_id, args.repo))
67         change = get_change(args.url, args.repo, args.branch, change_id)
68         if change:
69             current_revision = change['current_revision']
70             ref = change['revisions'][current_revision]['ref']
71             logging.info('setting ref to {}'.format(ref))
72
73     # remove existing file or directory named repo
74     if os.path.exists(args.repo):
75         if os.path.isdir(args.repo):
76             shutil.rmtree(args.repo)
77         else:
78             os.remove(args.repo)
79
80     ws = git.Repo.clone_from("{}/{}".format(args.url, args.repo),
81                              args.repo, b=args.branch)
82     if ref:
83         git_cmd = ws.git
84         git_cmd.fetch("{}/{}".format(args.url, args.repo), ref)
85         git_cmd.checkout('FETCH_HEAD')
86         logging.info('Checked out commit:\n{}'.format(ws.head.commit.message))
87
88
89 def strip_patch_sections(patch, sections=['releasenotes', 'tests']):
90     """
91     Removes patch sections from a diff which contain a file path
92     :param patch:  patch to strip
93     :param sections: list of keywords to use to strip out of the patch file
94     :return: stripped patch
95     """
96
97     append_line = True
98     tmp_patch = []
99     for line in patch.split("\n"):
100         if re.match('diff\s', line):
101             for section in sections:
102                 if re.search(section, line):
103                     logging.debug("Stripping {} from patch: {}".format(
104                         section, line))
105                     append_line = False
106                     break
107                 else:
108                     append_line = True
109         if append_line:
110             tmp_patch.append(line)
111     return '\n'.join(tmp_patch)
112
113
114 def get_patch(change_id, repo, branch, url=con.OPENSTACK_GERRIT):
115     logging.info("Fetching patch for change id {}".format(change_id))
116     change = get_change(url, repo, branch, change_id)
117     if change:
118         current_revision = change['current_revision']
119         rest = GerritRestAPI(url=url)
120         change_path = "{}~{}~{}".format(quote_plus(repo), quote_plus(branch),
121                                         change_id)
122         patch_url = "changes/{}/revisions/{}/patch".format(change_path,
123                                                            current_revision)
124         return strip_patch_sections(rest.get(patch_url))
125
126
127 def get_parser():
128     parser = argparse.ArgumentParser()
129     parser.add_argument('--debug', action='store_true', default=False,
130                         help="Turn on debug messages")
131     subparsers = parser.add_subparsers()
132     fork = subparsers.add_parser('clone-fork',
133                                  help='Clone fork of dependent repo')
134     fork.add_argument('-r', '--repo', required=True, help='Name of repository')
135     fork.add_argument('-u', '--url',
136                       default='https://gerrit.opnfv.org/gerrit',
137                       help='Gerrit URL of repository')
138     fork.add_argument('-b', '--branch',
139                       default='master',
140                       help='Branch to checkout')
141     fork.set_defaults(func=clone_fork)
142     return parser
143
144
145 def main():
146     parser = get_parser()
147     args = parser.parse_args(sys.argv[1:])
148     if args.debug:
149         logging_level = logging.DEBUG
150     else:
151         logging_level = logging.INFO
152
153     logging.basicConfig(format='%(asctime)s %(levelname)s: %(message)s',
154                         datefmt='%m/%d/%Y %I:%M:%S %p',
155                         level=logging_level)
156     if hasattr(args, 'func'):
157         args.func(args)
158     else:
159         parser.print_help()
160         exit(1)
161
162
163 if __name__ == "__main__":
164     main()