blob: 3b5720912d809fe96d6417828cd51f22c91e959d [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)]
Brad Bishop5d7dcfb2020-05-28 14:39:03 -0400154 if not args.dry_run:
155 git.push(*push_args, _cwd=meta)
Brad Bishop35982432018-08-30 17:27:20 -0400156
157
158def main():
159 app_description = '''OpenBMC bitbake recipe bumping tool.
160
161Find bitbake metadata files (recipes) that use the git fetcher
162and check the project repository for newer revisions.
163
164Generate commits that update bitbake metadata files with SRCREV.
165
166Push generated commits to the OpenBMC Gerrit instance for review.
167 '''
168 parser = argparse.ArgumentParser(
169 description=app_description,
170 formatter_class=argparse.RawDescriptionHelpFormatter)
171
172 parser.set_defaults(branch='master')
173 parser.add_argument(
Brad Bishop5d7dcfb2020-05-28 14:39:03 -0400174 '-d', '--dry-run', dest='dry_run', action='store_true',
175 help='perform a dry run only')
176 parser.add_argument(
Brad Bishop35982432018-08-30 17:27:20 -0400177 '-m', '--meta-repository', dest='meta_repository', action='append',
178 help='meta repository to check for updates')
179 parser.add_argument(
180 '-v', '--verbose', dest='noisy', action='store_true',
181 help='enable verbose status messages')
182 parser.add_argument(
183 'ssh_config_host', metavar='SSH_CONFIG_HOST_ENTRY',
184 help='SSH config host entry for Gerrit connectivity')
185
186 args = parser.parse_args()
187 setattr(args, 'gerrit', sh.ssh.bake(args.ssh_config_host, 'gerrit'))
188
189 metas = getattr(args, 'meta_repository')
190 if metas is None:
191 metas = args.gerrit('ls-projects', '-m', 'meta-')
192 metas = metas.stdout.decode('utf-8').split()
193 metas = [os.path.split(x)[-1] for x in metas]
194
195 for meta in metas:
196 find_and_process_bumps(meta, args)
197
198
199if __name__ == '__main__':
200 sys.exit(0 if main() else 1)