blob: 24e62453bae6ece00d9a7a09951a36a1818104c5 [file] [log] [blame]
Monty Taylorfa743022017-02-14 07:36:34 -06001#! /usr/bin/env python
2# Copyright (C) 2011 OpenStack, LLC.
3# Copyright (c) 2012 Hewlett-Packard Development Company, L.P.
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations
15# under the License.
16
17# manage_projects.py reads a config file called projects.ini
18# It should look like:
19
20# [projects]
21# homepage=http://openstack.org
22# gerrit-host=review.openstack.org
23# local-git-dir=/var/lib/git
24# gerrit-key=/home/gerrit2/review_site/etc/ssh_host_rsa_key
25# gerrit-committer=Project Creator <openstack-infra@lists.openstack.org>
26# gerrit-replicate=True
27# has-github=True
28# has-wiki=False
29# has-issues=False
30# has-downloads=False
31# acl-dir=/home/gerrit2/acls
32# acl-base=/home/gerrit2/acls/project.config
33#
34# manage_projects.py reads a project listing file called projects.yaml
35# It should look like:
36# - project: PROJECT_NAME
37# options:
38# - has-wiki
39# - has-issues
40# - has-downloads
41# - has-pull-requests
42# - track-upstream
43# homepage: Some homepage that isn't http://openstack.org
44# description: This is a great project
45# upstream: https://gerrit.googlesource.com/gerrit
46# upstream-prefix: upstream
47# acl-config: /path/to/gerrit/project.config
48# acl-append:
49# - /path/to/gerrit/project.config
50# acl-parameters:
51# project: OTHER_PROJECT_NAME
52
53import argparse
54import json
55import logging
56import os
57
58import gerritlib.gerrit
59
60import jeepyb.log as l
61import jeepyb.utils as u
62
63registry = u.ProjectsRegistry()
64
65log = logging.getLogger("track_upstream")
66orgs = None
67
68
Monty Taylorfa743022017-02-14 07:36:34 -060069def update_local_copy(repo_path, track_upstream, git_opts, ssh_env):
70 # first do a clean of the branch to prevent possible
71 # problems due to previous runs
72 u.git_command(repo_path, "clean -fdx")
73
74 has_upstream_remote = (
75 'upstream' in u.git_command_output(repo_path, 'remote')[1])
76 if track_upstream:
77 # If we're configured to track upstream but the repo
78 # does not have an upstream remote, add one
79 if not has_upstream_remote:
80 u.git_command(
81 repo_path,
82 "remote add upstream %(upstream)s" % git_opts)
83
84 # If we're configured to track upstream, make sure that
85 # the upstream URL matches the config
86 else:
87 u.git_command(
88 repo_path,
89 "remote set-url upstream %(upstream)s" % git_opts)
90
91 # Now that we have any upstreams configured, fetch all of the refs
92 # we might need, pruning remote branches that no longer exist
93 u.git_command(
94 repo_path, "remote update --prune", env=ssh_env)
95 else:
96 # If we are not tracking upstream, then we do not need
97 # an upstream remote configured
98 if has_upstream_remote:
99 u.git_command(repo_path, "remote rm upstream")
100
101 # TODO(mordred): This is here so that later we can
102 # inspect the master branch for meta-info
103 # Checkout master and reset to the state of origin/master
104 u.git_command(repo_path, "checkout -B master origin/master")
105
106
107def fsck_repo(repo_path):
108 rc, out = u.git_command_output(repo_path, 'fsck --full')
109 # Check for non zero return code or warnings which should
110 # be treated as errors. In this case zeroPaddedFilemodes
111 # will not be accepted by Gerrit/jgit but are accepted by C git.
112 if rc != 0 or 'zeroPaddedFilemode' in out:
113 log.error('git fsck of %s failed:\n%s' % (repo_path, out))
114 raise Exception('git fsck failed not importing')
115
116
117def push_to_gerrit(repo_path, project, push_string, remote_url, ssh_env):
118 try:
119 u.git_command(repo_path, push_string % remote_url, env=ssh_env)
120 u.git_command(repo_path, "push --tags %s" % remote_url, env=ssh_env)
121 except Exception:
122 log.exception(
123 "Error pushing %s to Gerrit." % project)
124
125
126def sync_upstream(repo_path, project, ssh_env, upstream_prefix):
127 u.git_command(
128 repo_path,
129 "remote update upstream --prune", env=ssh_env)
130 # Any branch that exists in the upstream remote, we want
131 # a local branch of, optionally prefixed with the
132 # upstream prefix value
133 for branch in u.git_command_output(
134 repo_path, "branch -a")[1].split('\n'):
135 if not branch.strip().startswith("remotes/upstream"):
136 continue
137 if "->" in branch:
138 continue
139 local_branch = branch.split()[0][len('remotes/upstream/'):]
140 if upstream_prefix:
141 local_branch = "%s/%s" % (
142 upstream_prefix, local_branch)
143
144 # Check out an up to date copy of the branch, so that
145 # we can push it and it will get picked up below
146 u.git_command(
147 repo_path, "checkout -B %s %s" % (local_branch, branch))
148
149 try:
150 # Push all of the local branches to similarly named
151 # Branches on gerrit. Also, push all of the tags
152 u.git_command(
153 repo_path,
154 "push origin refs/heads/*:refs/heads/*",
155 env=ssh_env)
156 u.git_command(repo_path, 'push origin --tags', env=ssh_env)
157 except Exception:
158 log.exception(
159 "Error pushing %s to Gerrit." % project)
160
161
162def main():
163 parser = argparse.ArgumentParser(description='Manage projects')
164 l.setup_logging_arguments(parser)
165 parser.add_argument('--nocleanup', action='store_true',
166 help='do not remove temp directories')
167 parser.add_argument('projects', metavar='project', nargs='*',
168 help='name of project(s) to process')
169 args = parser.parse_args()
170 l.configure_logging(args)
171
172 JEEPYB_CACHE_DIR = registry.get_defaults('jeepyb-cache-dir',
173 '/var/lib/jeepyb')
174 IMPORT_DIR = os.path.join(JEEPYB_CACHE_DIR, 'import')
175 GERRIT_HOST = registry.get_defaults('gerrit-host')
176 GERRIT_PORT = int(registry.get_defaults('gerrit-port', '29418'))
177 GERRIT_USER = registry.get_defaults('gerrit-user')
178 GERRIT_KEY = registry.get_defaults('gerrit-key')
179 GERRIT_GITID = registry.get_defaults('gerrit-committer')
180
181 PROJECT_CACHE_FILE = os.path.join(JEEPYB_CACHE_DIR, 'project.cache')
182 project_cache = {}
183 if os.path.exists(PROJECT_CACHE_FILE):
184 project_cache = json.loads(open(PROJECT_CACHE_FILE, 'r').read())
185
186 gerrit = gerritlib.gerrit.Gerrit(GERRIT_HOST,
187 GERRIT_USER,
188 GERRIT_PORT,
189 GERRIT_KEY)
190 project_list = gerrit.listProjects()
191 ssh_env = u.make_ssh_wrapper(GERRIT_USER, GERRIT_KEY)
192 try:
193
194 for section in registry.configs_list:
195 project = section['project']
196 if args.projects and project not in args.projects:
197 continue
198
199 try:
200 log.info("Processing project: %s" % project)
201
202 # Figure out all of the options
203 options = section.get('options', dict())
204 track_upstream = 'track-upstream' in options
205 if not track_upstream:
206 continue
207
208 # If this project doesn't want to use gerrit, exit cleanly.
209 if 'no-gerrit' in options:
210 continue
211
212 upstream = section.get('upstream', None)
213 upstream_prefix = section.get('upstream-prefix', None)
214 repo_path = os.path.join(IMPORT_DIR, project)
215
216 project_git = "%s.git" % project
217 remote_url = "ssh://%s:%s/%s" % (
218 GERRIT_HOST,
219 GERRIT_PORT,
220 project)
221 git_opts = dict(upstream=upstream,
222 repo_path=repo_path,
223 remote_url=remote_url)
224 project_cache.setdefault(project, {})
225 if not project_cache[project]['pushed-to-gerrit']:
226 continue
227
228 # Make Local repo
229 if not os.path.exists(repo_path):
Monty Taylor36f4fa42017-02-14 12:24:13 -0600230 u.make_local_copy(
Monty Taylorfa743022017-02-14 07:36:34 -0600231 repo_path, project, project_list,
232 git_opts, ssh_env, upstream, GERRIT_HOST,
233 GERRIT_PORT, project_git, GERRIT_GITID)
234 else:
235 update_local_copy(
236 repo_path, track_upstream, git_opts, ssh_env)
237
238 fsck_repo(repo_path)
239 sync_upstream(repo_path, project, ssh_env, upstream_prefix)
240
241 except Exception:
242 log.exception(
243 "Problems creating %s, moving on." % project)
244 continue
245 finally:
246 os.unlink(ssh_env['GIT_SSH'])
247
248if __name__ == "__main__":
249 main()