blob: 243a73eec3692e3200984bdde51d80234578d4bf [file] [log] [blame]
Brad Bishop35982432018-08-30 17:27:20 -04001#!/usr/bin/env python3
2
3# Contributors Listed Below - COPYRIGHT 2018
4# [+] International Business Machines Corp.
5#
6#
7# Licensed under the Apache License, Version 2.0 (the "License");
8# you may not use this file except in compliance with the License.
9# You may obtain a copy of the License at
10#
11# http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS,
15# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
16# implied. See the License for the specific language governing
17# permissions and limitations under the License.
18
19import argparse
20import os
21import sh
22import sys
23
24git = sh.git.bake('--no-pager')
25
26
27def log(msg, args):
28 if args.noisy:
29 sys.stderr.write('{}\n'.format(msg))
30
31
32def git_clone_or_reset(local_name, remote, args):
33 if not os.path.exists(local_name):
34 log('cloning into {}...'.format(local_name), args)
35 git.clone(remote, local_name)
36 else:
37 log('{} exists, updating...'.format(local_name), args)
38 git.fetch(_cwd=local_name)
39 git.reset('--hard', 'FETCH_HEAD', _cwd=local_name)
40
41
42def extract_sha_from_recipe(recipe):
43 with open(recipe) as fp:
44 project = None
45 sha = None
46
47 for line in fp:
48 if 'SRCREV' in line:
49 sha = line.split('=')[-1].replace('"', '').strip()
50 elif '_URI' in line and 'github.com/openbmc' in line:
Brad Bishopbd19f5e2020-05-27 13:34:18 -040051 uri = line.split(';')[0]
52 uri = uri.split('=')[-1].replace('"', '').strip()
Brad Bishop35982432018-08-30 17:27:20 -040053 project = uri.split('/')[-1].replace('.git', '')
54
55 if project and sha:
56 return (project, sha)
57
58 raise RuntimeError('No SRCREV or URI found in {}'.format(recipe))
59
60
61def find_candidate_recipes(meta, args):
62 remote_fmt_args = (args.ssh_config_host, meta)
63 remote = 'ssh://{}/openbmc/{}'.format(*remote_fmt_args)
64 try:
65 git_clone_or_reset(meta, remote, args)
66 except sh.ErrorReturnCode as e:
67 log('{}'.format(e), args)
68 return []
69
70 grep_args = ['-l', '-e', '_URI', '--and', '-e', 'github.com/openbmc']
71 try:
72 return git.grep(*grep_args, _cwd=meta).stdout.decode('utf-8').split()
73 except sh.ErrorReturnCode_1:
74 pass
75 except sh.ErrorReturnCode as e:
76 log('{}'.format(e), args)
77
78 return []
79
80
81def find_and_process_bumps(meta, args):
82 candidate_recipes = find_candidate_recipes(meta, args)
83
84 for recipe in candidate_recipes:
85 full_recipe_path = os.path.join(meta, recipe)
86 recipe_basename = os.path.basename(full_recipe_path)
87 project_name, recipe_sha = extract_sha_from_recipe(full_recipe_path)
88
89 remote_fmt_args = (args.ssh_config_host, project_name)
90 remote = 'ssh://{}/openbmc/{}'.format(*remote_fmt_args)
91 ls_remote_args = [remote, 'refs/heads/{}'.format(args.branch)]
92 try:
93 project_sha = git('ls-remote', *ls_remote_args)
94 project_sha = project_sha.stdout.decode('utf-8').split()[0]
95 except sh.ErrorReturnCode as e:
96 log('{}'.format(e), args)
97 continue
98
99 if project_sha == recipe_sha:
100 message_args = (recipe_basename, recipe_sha[:10])
101 print('{} is up to date ({})'.format(*message_args))
102 continue
103
104 change_id = 'autobump {} {} {}'.format(recipe, recipe_sha, project_sha)
105 hash_object_args = ['-t', 'blob', '--stdin']
106 change_id = git(sh.echo(change_id), 'hash-object', *hash_object_args)
107 change_id = 'I{}'.format(change_id.strip())
108
109 query_args = ['query', 'change:{}'.format(change_id)]
110 gerrit_query_result = args.gerrit(*query_args)
111 gerrit_query_result = gerrit_query_result.stdout.decode('utf-8')
112
113 if (change_id in gerrit_query_result):
114 message_args = (recipe_basename, change_id)
115 print('{} {} already exists'.format(*message_args))
116 continue
117
118 message_args = (recipe_basename, recipe_sha[:10], project_sha[:10])
119 print('{} updating from {} to {}'.format(*message_args))
120
121 remote_args = (args.ssh_config_host, project_name)
122 remote = 'ssh://{}/openbmc/{}'.format(*remote_args)
123 git_clone_or_reset(project_name, remote, args)
124
125 try:
126 revlist = '{}..{}'.format(recipe_sha, project_sha)
127 shortlog = git.shortlog(revlist, _cwd=project_name)
128 shortlog = shortlog.stdout.decode('utf-8')
129 except sh.ErrorReturnCode as e:
130 log('{}'.format(e), args)
131 continue
132
133 reset_args = ['--hard', 'origin/{}'.format(args.branch)]
134 git.reset(*reset_args, _cwd=meta)
135
136 recipe_content = None
137 with open(full_recipe_path) as fd:
138 recipe_content = fd.read()
139
140 recipe_content = recipe_content.replace(recipe_sha, project_sha)
141 with open(full_recipe_path, 'w') as fd:
142 fd.write(recipe_content)
143
144 git.add(recipe, _cwd=meta)
145
146 commit_summary_args = (project_name, recipe_sha[:10], project_sha[:10])
147 commit_msg = '{}: srcrev bump {}..{}'.format(*commit_summary_args)
148 commit_msg += '\n\n{}'.format(shortlog)
149 commit_msg += '\n\nChange-Id: {}'.format(change_id)
150
151 git.commit(sh.echo(commit_msg), '-s', '-F', '-', _cwd=meta)
152
153 push_args = ['origin', 'HEAD:refs/for/{}/autobump'.format(args.branch)]
154 git.push(*push_args, _cwd=meta)
155
156
157def main():
158 app_description = '''OpenBMC bitbake recipe bumping tool.
159
160Find bitbake metadata files (recipes) that use the git fetcher
161and check the project repository for newer revisions.
162
163Generate commits that update bitbake metadata files with SRCREV.
164
165Push generated commits to the OpenBMC Gerrit instance for review.
166 '''
167 parser = argparse.ArgumentParser(
168 description=app_description,
169 formatter_class=argparse.RawDescriptionHelpFormatter)
170
171 parser.set_defaults(branch='master')
172 parser.add_argument(
173 '-m', '--meta-repository', dest='meta_repository', action='append',
174 help='meta repository to check for updates')
175 parser.add_argument(
176 '-v', '--verbose', dest='noisy', action='store_true',
177 help='enable verbose status messages')
178 parser.add_argument(
179 'ssh_config_host', metavar='SSH_CONFIG_HOST_ENTRY',
180 help='SSH config host entry for Gerrit connectivity')
181
182 args = parser.parse_args()
183 setattr(args, 'gerrit', sh.ssh.bake(args.ssh_config_host, 'gerrit'))
184
185 metas = getattr(args, 'meta_repository')
186 if metas is None:
187 metas = args.gerrit('ls-projects', '-m', 'meta-')
188 metas = metas.stdout.decode('utf-8').split()
189 metas = [os.path.split(x)[-1] for x in metas]
190
191 for meta in metas:
192 find_and_process_bumps(meta, args)
193
194
195if __name__ == '__main__':
196 sys.exit(0 if main() else 1)