Enables containerized overcloud deployments
[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, or merged
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         if change['status'] == 'MERGED':
41             logging.info('Change {} is merged, ignoring...'
42                          .format(change_id))
43             return None
44         else:
45             return change
46
47     except KeyError:
48         logging.error('Failed to get valid change data structure from url '
49                       '{}/{}, data returned: \n{}'
50                       .format(change_id, change_str, change))
51         raise
52
53
54 def clone_fork(args):
55     ref = None
56     logging.info("Cloning {}".format(args.repo))
57
58     try:
59         cm = git.Repo(search_parent_directories=True).commit().message
60     except git.exc.InvalidGitRepositoryError:
61         logging.debug('Current Apex directory is not a git repo: {}'
62                       .format(os.getcwd()))
63         cm = ''
64
65     logging.info("Current commit message: {}".format(cm))
66     m = re.search('{}:\s*(\S+)'.format(args.repo), cm)
67
68     if m:
69         change_id = m.group(1)
70         logging.info("Using change ID {} from {}".format(change_id, args.repo))
71         change = get_change(args.url, args.repo, args.branch, change_id)
72         if change:
73             current_revision = change['current_revision']
74             ref = change['revisions'][current_revision]['ref']
75             logging.info('setting ref to {}'.format(ref))
76
77     # remove existing file or directory named repo
78     if os.path.exists(args.repo):
79         if os.path.isdir(args.repo):
80             shutil.rmtree(args.repo)
81         else:
82             os.remove(args.repo)
83
84     ws = git.Repo.clone_from("{}/{}".format(args.url, args.repo),
85                              args.repo, b=args.branch)
86     if ref:
87         git_cmd = ws.git
88         git_cmd.fetch("{}/{}".format(args.url, args.repo), ref)
89         git_cmd.checkout('FETCH_HEAD')
90         logging.info('Checked out commit:\n{}'.format(ws.head.commit.message))
91
92
93 def strip_patch_sections(patch, sections=['releasenotes']):
94     """
95     Removes patch sections from a diff which contain a file path
96     :param patch:  patch to strip
97     :param sections: list of keywords to use to strip out of the patch file
98     :return: stripped patch
99     """
100
101     append_line = True
102     tmp_patch = []
103     for line in patch.split("\n"):
104         if re.match('diff\s', line):
105             for section in sections:
106                 if re.search(section, line):
107                     logging.debug("Stripping {} from patch: {}".format(
108                         section, line))
109                     append_line = False
110                     break
111                 else:
112                     append_line = True
113         if append_line:
114             tmp_patch.append(line)
115     return '\n'.join(tmp_patch)
116
117
118 def get_patch(change_id, repo, branch, url=con.OPENSTACK_GERRIT):
119     logging.info("Fetching patch for change id {}".format(change_id))
120     change = get_change(url, repo, branch, change_id)
121     if change:
122         current_revision = change['current_revision']
123         rest = GerritRestAPI(url=url)
124         change_path = "{}~{}~{}".format(quote_plus(repo), quote_plus(branch),
125                                         change_id)
126         patch_url = "changes/{}/revisions/{}/patch".format(change_path,
127                                                            current_revision)
128         return strip_patch_sections(rest.get(patch_url))
129
130
131 def get_parser():
132     parser = argparse.ArgumentParser()
133     parser.add_argument('--debug', action='store_true', default=False,
134                         help="Turn on debug messages")
135     subparsers = parser.add_subparsers()
136     fork = subparsers.add_parser('clone-fork',
137                                  help='Clone fork of dependent repo')
138     fork.add_argument('-r', '--repo', required=True, help='Name of repository')
139     fork.add_argument('-u', '--url',
140                       default='https://gerrit.opnfv.org/gerrit',
141                       help='Gerrit URL of repository')
142     fork.add_argument('-b', '--branch',
143                       default='master',
144                       help='Branch to checkout')
145     fork.set_defaults(func=clone_fork)
146     return parser
147
148
149 def main():
150     parser = get_parser()
151     args = parser.parse_args(sys.argv[1:])
152     if args.debug:
153         logging_level = logging.DEBUG
154     else:
155         logging_level = logging.INFO
156
157     logging.basicConfig(format='%(asctime)s %(levelname)s: %(message)s',
158                         datefmt='%m/%d/%Y %I:%M:%S %p',
159                         level=logging_level)
160     if hasattr(args, 'func'):
161         args.func(args)
162     else:
163         parser.print_help()
164         exit(1)
165
166
167 if __name__ == "__main__":
168     main()