Monty Taylor | 16390e2 | 2012-11-04 22:20:36 +0100 | [diff] [blame] | 1 | #! /usr/bin/env python |
| 2 | # Copyright (C) 2011 OpenStack, LLC. |
James E. Blair | 18e8256 | 2013-04-04 16:20:32 +0000 | [diff] [blame] | 3 | # Copyright (c) 2012 Hewlett-Packard Development Company, L.P. |
Monty Taylor | 16390e2 | 2012-11-04 22:20:36 +0100 | [diff] [blame] | 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 | |
James E. Blair | 18e8256 | 2013-04-04 16:20:32 +0000 | [diff] [blame] | 17 | # manage_projects.py reads a project config file called projects.yaml |
| 18 | # It should look like: |
| 19 | |
| 20 | # - homepage: http://openstack.org |
| 21 | # gerrit-host: review.openstack.org |
| 22 | # local-git-dir: /var/lib/git |
| 23 | # gerrit-key: /home/gerrit2/review_site/etc/ssh_host_rsa_key |
Sean Dague | 9023a46 | 2013-05-06 06:53:10 -0400 | [diff] [blame] | 24 | # gerrit-committer: Project Creator <openstack-infra@lists.openstack.org> |
Sean Dague | 5906b83 | 2013-05-03 18:08:26 -0400 | [diff] [blame] | 25 | # has-github: True |
James E. Blair | 18e8256 | 2013-04-04 16:20:32 +0000 | [diff] [blame] | 26 | # has-wiki: False |
| 27 | # has-issues: False |
| 28 | # has-downloads: False |
| 29 | # acl-dir: /home/gerrit2/acls |
| 30 | # acl-base: /home/gerrit2/acls/project.config |
| 31 | # --- |
| 32 | # - project: PROJECT_NAME |
| 33 | # options: |
| 34 | # - has-wiki |
| 35 | # - has-issues |
| 36 | # - has-downloads |
| 37 | # - has-pull-requests |
Monty Taylor | 90a630f | 2013-07-03 12:45:26 -0700 | [diff] [blame] | 38 | # - track-upstream |
James E. Blair | 18e8256 | 2013-04-04 16:20:32 +0000 | [diff] [blame] | 39 | # homepage: Some homepage that isn't http://openstack.org |
| 40 | # description: This is a great project |
Monty Taylor | 90a630f | 2013-07-03 12:45:26 -0700 | [diff] [blame] | 41 | # upstream: https://gerrit.googlesource.com/gerrit |
| 42 | # upstream-prefix: upstream |
James E. Blair | 18e8256 | 2013-04-04 16:20:32 +0000 | [diff] [blame] | 43 | # acl-config: /path/to/gerrit/project.config |
| 44 | # acl-append: |
| 45 | # - /path/to/gerrit/project.config |
| 46 | # acl-parameters: |
| 47 | # project: OTHER_PROJECT_NAME |
| 48 | |
James E. Blair | eab9261 | 2013-08-30 09:47:32 -0700 | [diff] [blame] | 49 | import argparse |
Monty Taylor | 16390e2 | 2012-11-04 22:20:36 +0100 | [diff] [blame] | 50 | import ConfigParser |
| 51 | import logging |
| 52 | import os |
| 53 | import re |
| 54 | import shlex |
| 55 | import subprocess |
| 56 | import tempfile |
Clark Boylan | 320d150 | 2013-09-25 11:05:42 -0700 | [diff] [blame] | 57 | import time |
James E. Blair | 18e8256 | 2013-04-04 16:20:32 +0000 | [diff] [blame] | 58 | import yaml |
Monty Taylor | 16390e2 | 2012-11-04 22:20:36 +0100 | [diff] [blame] | 59 | |
Monty Taylor | 16390e2 | 2012-11-04 22:20:36 +0100 | [diff] [blame] | 60 | import gerritlib.gerrit |
Monty Taylor | 061919f | 2013-06-02 11:35:42 -0400 | [diff] [blame] | 61 | import github |
Monty Taylor | 16390e2 | 2012-11-04 22:20:36 +0100 | [diff] [blame] | 62 | |
Clark Boylan | da402e5 | 2012-12-04 15:23:43 -0800 | [diff] [blame] | 63 | import jeepyb.gerritdb |
| 64 | |
Monty Taylor | 16390e2 | 2012-11-04 22:20:36 +0100 | [diff] [blame] | 65 | log = logging.getLogger("manage_projects") |
| 66 | |
| 67 | |
| 68 | def run_command(cmd, status=False, env={}): |
| 69 | cmd_list = shlex.split(str(cmd)) |
| 70 | newenv = os.environ |
| 71 | newenv.update(env) |
| 72 | log.debug("Executing command: %s" % " ".join(cmd_list)) |
| 73 | p = subprocess.Popen(cmd_list, stdout=subprocess.PIPE, |
| 74 | stderr=subprocess.STDOUT, env=newenv) |
| 75 | (out, nothing) = p.communicate() |
| 76 | log.debug("Return code: %s" % p.returncode) |
| 77 | log.debug("Command said: %s" % out.strip()) |
| 78 | if status: |
| 79 | return (p.returncode, out.strip()) |
| 80 | return out.strip() |
| 81 | |
| 82 | |
| 83 | def run_command_status(cmd, env={}): |
| 84 | return run_command(cmd, True, env) |
| 85 | |
| 86 | |
| 87 | def git_command(repo_dir, sub_cmd, env={}): |
| 88 | git_dir = os.path.join(repo_dir, '.git') |
| 89 | cmd = "git --git-dir=%s --work-tree=%s %s" % (git_dir, repo_dir, sub_cmd) |
| 90 | status, _ = run_command(cmd, True, env) |
| 91 | return status |
| 92 | |
| 93 | |
Clark Boylan | 808b513 | 2012-11-14 15:52:02 -0800 | [diff] [blame] | 94 | def git_command_output(repo_dir, sub_cmd, env={}): |
| 95 | git_dir = os.path.join(repo_dir, '.git') |
| 96 | cmd = "git --git-dir=%s --work-tree=%s %s" % (git_dir, repo_dir, sub_cmd) |
| 97 | status, out = run_command(cmd, True, env) |
| 98 | return (status, out) |
| 99 | |
| 100 | |
Monty Taylor | 863a671 | 2013-03-03 09:52:36 -0500 | [diff] [blame] | 101 | def write_acl_config(project, acl_dir, acl_base, acl_append, parameters): |
| 102 | project_parts = os.path.split(project) |
| 103 | if len(project_parts) > 1: |
| 104 | repo_base = os.path.join(acl_dir, *project_parts[:-1]) |
| 105 | if not os.path.exists(repo_base): |
James E. Blair | 22e1c69 | 2013-03-14 11:40:37 -0700 | [diff] [blame] | 106 | os.makedirs(repo_base) |
Monty Taylor | 863a671 | 2013-03-03 09:52:36 -0500 | [diff] [blame] | 107 | if not os.path.isdir(repo_base): |
| 108 | return 1 |
Monty Taylor | e8bb6e4 | 2013-03-08 15:01:56 -0500 | [diff] [blame] | 109 | project = project_parts[-1] |
| 110 | config_file = os.path.join(repo_base, "%s.config" % project) |
Monty Taylor | 863a671 | 2013-03-03 09:52:36 -0500 | [diff] [blame] | 111 | else: |
| 112 | config_file = os.path.join(acl_dir, "%s.config" % project) |
James E. Blair | 18e8256 | 2013-04-04 16:20:32 +0000 | [diff] [blame] | 113 | if 'project' not in parameters: |
| 114 | parameters['project'] = project |
Monty Taylor | 863a671 | 2013-03-03 09:52:36 -0500 | [diff] [blame] | 115 | with open(config_file, 'w') as config: |
| 116 | if acl_base and os.path.exists(acl_base): |
| 117 | config.write(open(acl_base, 'r').read()) |
| 118 | for acl_snippet in acl_append: |
| 119 | if not os.path.exists(acl_snippet): |
| 120 | acl_snippet = os.path.join(acl_dir, acl_snippet) |
| 121 | if not os.path.exists(acl_snippet): |
| 122 | continue |
| 123 | with open(acl_snippet, 'r') as append_content: |
| 124 | config.write(append_content.read() % parameters) |
| 125 | |
| 126 | |
Monty Taylor | 16390e2 | 2012-11-04 22:20:36 +0100 | [diff] [blame] | 127 | def fetch_config(project, remote_url, repo_path, env={}): |
| 128 | status = git_command(repo_path, "fetch %s +refs/meta/config:" |
| 129 | "refs/remotes/gerrit-meta/config" % remote_url, env) |
| 130 | if status != 0: |
Clark Boylan | 796eada | 2013-09-25 10:51:35 -0700 | [diff] [blame] | 131 | log.error("Failed to fetch refs/meta/config for project: %s" % project) |
Monty Taylor | 16390e2 | 2012-11-04 22:20:36 +0100 | [diff] [blame] | 132 | return False |
| 133 | # Because the following fails if executed more than once you should only |
| 134 | # run fetch_config once in each repo. |
Monty Taylor | 7126209 | 2013-10-12 20:47:21 -0400 | [diff] [blame] | 135 | status = git_command(repo_path, "checkout -B config " |
Monty Taylor | 16390e2 | 2012-11-04 22:20:36 +0100 | [diff] [blame] | 136 | "remotes/gerrit-meta/config") |
| 137 | if status != 0: |
Clark Boylan | 796eada | 2013-09-25 10:51:35 -0700 | [diff] [blame] | 138 | log.error("Failed to checkout config for project: %s" % project) |
Monty Taylor | 16390e2 | 2012-11-04 22:20:36 +0100 | [diff] [blame] | 139 | return False |
| 140 | |
| 141 | return True |
| 142 | |
| 143 | |
| 144 | def copy_acl_config(project, repo_path, acl_config): |
| 145 | if not os.path.exists(acl_config): |
| 146 | return False |
| 147 | |
| 148 | acl_dest = os.path.join(repo_path, "project.config") |
| 149 | status, _ = run_command("cp %s %s" % |
| 150 | (acl_config, acl_dest), status=True) |
| 151 | if status == 0: |
Clark Boylan | 333f4cb | 2013-04-04 16:55:01 -0700 | [diff] [blame] | 152 | status = git_command(repo_path, "diff --quiet") |
Monty Taylor | 16390e2 | 2012-11-04 22:20:36 +0100 | [diff] [blame] | 153 | if status != 0: |
| 154 | return True |
| 155 | return False |
| 156 | |
| 157 | |
Sean Dague | 9023a46 | 2013-05-06 06:53:10 -0400 | [diff] [blame] | 158 | def push_acl_config(project, remote_url, repo_path, gitid, env={}): |
| 159 | cmd = "commit -a -m'Update project config.' --author='%s'" % gitid |
Monty Taylor | 16390e2 | 2012-11-04 22:20:36 +0100 | [diff] [blame] | 160 | status = git_command(repo_path, cmd) |
| 161 | if status != 0: |
Clark Boylan | 796eada | 2013-09-25 10:51:35 -0700 | [diff] [blame] | 162 | log.error("Failed to commit config for project: %s" % project) |
Monty Taylor | 16390e2 | 2012-11-04 22:20:36 +0100 | [diff] [blame] | 163 | return False |
Clark Boylan | 808b513 | 2012-11-14 15:52:02 -0800 | [diff] [blame] | 164 | status, out = git_command_output(repo_path, |
Monty Taylor | da3bada | 2012-11-22 09:38:22 -0800 | [diff] [blame] | 165 | "push %s HEAD:refs/meta/config" % |
| 166 | remote_url, env) |
Monty Taylor | 16390e2 | 2012-11-04 22:20:36 +0100 | [diff] [blame] | 167 | if status != 0: |
Clark Boylan | 796eada | 2013-09-25 10:51:35 -0700 | [diff] [blame] | 168 | log.error("Failed to push config for project: %s" % project) |
Monty Taylor | 16390e2 | 2012-11-04 22:20:36 +0100 | [diff] [blame] | 169 | return False |
| 170 | return True |
| 171 | |
| 172 | |
Clark Boylan | 320d150 | 2013-09-25 11:05:42 -0700 | [diff] [blame] | 173 | def _get_group_uuid(group): |
Clark Boylan | da402e5 | 2012-12-04 15:23:43 -0800 | [diff] [blame] | 174 | cursor = jeepyb.gerritdb.connect().cursor() |
| 175 | query = "SELECT group_uuid FROM account_groups WHERE name = %s" |
| 176 | cursor.execute(query, group) |
| 177 | data = cursor.fetchone() |
Monty Taylor | 16390e2 | 2012-11-04 22:20:36 +0100 | [diff] [blame] | 178 | if data: |
Clark Boylan | da402e5 | 2012-12-04 15:23:43 -0800 | [diff] [blame] | 179 | return data[0] |
Monty Taylor | 16390e2 | 2012-11-04 22:20:36 +0100 | [diff] [blame] | 180 | return None |
| 181 | |
| 182 | |
Clark Boylan | 320d150 | 2013-09-25 11:05:42 -0700 | [diff] [blame] | 183 | def _wait_for_group(gerrit, group): |
| 184 | """Wait for up to 10 seconds for the group to be created.""" |
| 185 | for x in range(10): |
| 186 | groups = gerrit.listGroups() |
| 187 | if group in groups: |
| 188 | break |
| 189 | time.sleep(1) |
| 190 | |
| 191 | |
Monty Taylor | 16390e2 | 2012-11-04 22:20:36 +0100 | [diff] [blame] | 192 | def get_group_uuid(gerrit, group): |
Clark Boylan | 320d150 | 2013-09-25 11:05:42 -0700 | [diff] [blame] | 193 | uuid = _get_group_uuid(group) |
Monty Taylor | 16390e2 | 2012-11-04 22:20:36 +0100 | [diff] [blame] | 194 | if uuid: |
| 195 | return uuid |
Clark Boylan | 68ce329 | 2013-07-01 11:45:46 -0700 | [diff] [blame] | 196 | gerrit.createGroup(group) |
Clark Boylan | 320d150 | 2013-09-25 11:05:42 -0700 | [diff] [blame] | 197 | _wait_for_group(gerrit, group) |
| 198 | uuid = _get_group_uuid(group) |
Monty Taylor | 16390e2 | 2012-11-04 22:20:36 +0100 | [diff] [blame] | 199 | if uuid: |
| 200 | return uuid |
| 201 | return None |
| 202 | |
| 203 | |
| 204 | def create_groups_file(project, gerrit, repo_path): |
| 205 | acl_config = os.path.join(repo_path, "project.config") |
| 206 | group_file = os.path.join(repo_path, "groups") |
| 207 | uuids = {} |
| 208 | for line in open(acl_config, 'r'): |
Clark Boylan | 5c9670f | 2012-11-14 17:48:55 -0800 | [diff] [blame] | 209 | r = re.match(r'^\s+.*group\s+(.*)$', line) |
Monty Taylor | 16390e2 | 2012-11-04 22:20:36 +0100 | [diff] [blame] | 210 | if r: |
| 211 | group = r.group(1) |
| 212 | if group in uuids.keys(): |
| 213 | continue |
| 214 | uuid = get_group_uuid(gerrit, group) |
| 215 | if uuid: |
| 216 | uuids[group] = uuid |
| 217 | else: |
Clark Boylan | 796eada | 2013-09-25 10:51:35 -0700 | [diff] [blame] | 218 | log.error("Unable to get UUID for group %s." % group) |
Monty Taylor | 16390e2 | 2012-11-04 22:20:36 +0100 | [diff] [blame] | 219 | return False |
| 220 | if uuids: |
| 221 | with open(group_file, 'w') as fp: |
| 222 | for group, uuid in uuids.items(): |
| 223 | fp.write("%s\t%s\n" % (uuid, group)) |
| 224 | status = git_command(repo_path, "add groups") |
| 225 | if status != 0: |
Clark Boylan | 796eada | 2013-09-25 10:51:35 -0700 | [diff] [blame] | 226 | log.error("Failed to add groups file for project: %s" % project) |
Monty Taylor | 16390e2 | 2012-11-04 22:20:36 +0100 | [diff] [blame] | 227 | return False |
| 228 | return True |
| 229 | |
| 230 | |
| 231 | def make_ssh_wrapper(gerrit_user, gerrit_key): |
| 232 | (fd, name) = tempfile.mkstemp(text=True) |
| 233 | os.write(fd, '#!/bin/bash\n') |
| 234 | os.write(fd, |
| 235 | 'ssh -i %s -l %s -o "StrictHostKeyChecking no" $@\n' % |
| 236 | (gerrit_key, gerrit_user)) |
| 237 | os.close(fd) |
Sergey Lukjanov | db8e707 | 2013-07-22 12:13:35 +0400 | [diff] [blame] | 238 | os.chmod(name, 0o755) |
Monty Taylor | 16390e2 | 2012-11-04 22:20:36 +0100 | [diff] [blame] | 239 | return dict(GIT_SSH=name) |
| 240 | |
| 241 | |
Sean Dague | 5906b83 | 2013-05-03 18:08:26 -0400 | [diff] [blame] | 242 | def create_github_project(defaults, options, project, description, homepage): |
James E. Blair | 18e8256 | 2013-04-04 16:20:32 +0000 | [diff] [blame] | 243 | default_has_issues = defaults.get('has-issues', False) |
| 244 | default_has_downloads = defaults.get('has-downloads', False) |
| 245 | default_has_wiki = defaults.get('has-wiki', False) |
Sean Dague | 5906b83 | 2013-05-03 18:08:26 -0400 | [diff] [blame] | 246 | has_issues = 'has-issues' in options or default_has_issues |
| 247 | has_downloads = 'has-downloads' in options or default_has_downloads |
| 248 | has_wiki = 'has-wiki' in options or default_has_wiki |
Monty Taylor | 16390e2 | 2012-11-04 22:20:36 +0100 | [diff] [blame] | 249 | |
James E. Blair | 18e8256 | 2013-04-04 16:20:32 +0000 | [diff] [blame] | 250 | GITHUB_SECURE_CONFIG = defaults.get( |
| 251 | 'github-config', |
| 252 | '/etc/github/github-projects.secure.config') |
| 253 | |
Monty Taylor | da3bada | 2012-11-22 09:38:22 -0800 | [diff] [blame] | 254 | secure_config = ConfigParser.ConfigParser() |
| 255 | secure_config.read(GITHUB_SECURE_CONFIG) |
Monty Taylor | 16390e2 | 2012-11-04 22:20:36 +0100 | [diff] [blame] | 256 | |
Monty Taylor | da3bada | 2012-11-22 09:38:22 -0800 | [diff] [blame] | 257 | # Project creation doesn't work via oauth |
| 258 | ghub = github.Github(secure_config.get("github", "username"), |
| 259 | secure_config.get("github", "password")) |
| 260 | orgs = ghub.get_user().get_orgs() |
| 261 | orgs_dict = dict(zip([o.login.lower() for o in orgs], orgs)) |
Monty Taylor | 16390e2 | 2012-11-04 22:20:36 +0100 | [diff] [blame] | 262 | |
Sean Dague | 5906b83 | 2013-05-03 18:08:26 -0400 | [diff] [blame] | 263 | # Find the project's repo |
| 264 | project_split = project.split('/', 1) |
| 265 | org_name = project_split[0] |
| 266 | if len(project_split) > 1: |
| 267 | repo_name = project_split[1] |
| 268 | else: |
| 269 | repo_name = project |
| 270 | |
| 271 | try: |
| 272 | org = orgs_dict[org_name.lower()] |
| 273 | except KeyError: |
| 274 | # We do not have control of this github org ignore the project. |
Sean Dague | 433783d | 2013-05-04 07:47:08 -0400 | [diff] [blame] | 275 | return |
Sean Dague | 5906b83 | 2013-05-03 18:08:26 -0400 | [diff] [blame] | 276 | try: |
| 277 | repo = org.get_repo(repo_name) |
| 278 | except github.GithubException: |
| 279 | repo = org.create_repo(repo_name, |
| 280 | homepage=homepage, |
| 281 | has_issues=has_issues, |
| 282 | has_downloads=has_downloads, |
| 283 | has_wiki=has_wiki) |
| 284 | if description: |
| 285 | repo.edit(repo_name, description=description) |
| 286 | if homepage: |
| 287 | repo.edit(repo_name, homepage=homepage) |
| 288 | |
| 289 | repo.edit(repo_name, has_issues=has_issues, |
| 290 | has_downloads=has_downloads, |
| 291 | has_wiki=has_wiki) |
| 292 | |
| 293 | if 'gerrit' not in [team.name for team in repo.get_teams()]: |
| 294 | teams = org.get_teams() |
| 295 | teams_dict = dict(zip([t.name.lower() for t in teams], teams)) |
| 296 | teams_dict['gerrit'].add_to_repos(repo) |
| 297 | |
| 298 | |
Monty Taylor | 90a630f | 2013-07-03 12:45:26 -0700 | [diff] [blame] | 299 | # TODO(mordred): Inspect repo_dir:master for a description |
| 300 | # override |
| 301 | def find_description_override(repo_path): |
| 302 | return None |
| 303 | |
| 304 | |
Sean Dague | 5906b83 | 2013-05-03 18:08:26 -0400 | [diff] [blame] | 305 | def main(): |
James E. Blair | eab9261 | 2013-08-30 09:47:32 -0700 | [diff] [blame] | 306 | parser = argparse.ArgumentParser(description='Manage projects') |
| 307 | parser.add_argument('-v', dest='verbose', action='store_true', |
| 308 | help='verbose output') |
| 309 | parser.add_argument('--nocleanup', action='store_true', |
| 310 | help='do not remove temp directories') |
| 311 | parser.add_argument('projects', metavar='project', nargs='*', |
| 312 | help='name of project(s) to process') |
| 313 | args = parser.parse_args() |
| 314 | |
| 315 | if args.verbose: |
| 316 | logging.basicConfig(level=logging.DEBUG) |
| 317 | else: |
| 318 | logging.basicConfig(level=logging.ERROR) |
Sean Dague | 5906b83 | 2013-05-03 18:08:26 -0400 | [diff] [blame] | 319 | |
| 320 | PROJECTS_YAML = os.environ.get('PROJECTS_YAML', |
| 321 | '/home/gerrit2/projects.yaml') |
| 322 | configs = [config for config in yaml.load_all(open(PROJECTS_YAML))] |
| 323 | defaults = configs[0][0] |
| 324 | default_has_github = defaults.get('has-github', True) |
| 325 | |
| 326 | LOCAL_GIT_DIR = defaults.get('local-git-dir', '/var/lib/git') |
Monty Taylor | 90a630f | 2013-07-03 12:45:26 -0700 | [diff] [blame] | 327 | JEEPYB_CACHE_DIR = defaults.get('jeepyb-cache-dir', '/var/lib/jeepyb') |
Sean Dague | 5906b83 | 2013-05-03 18:08:26 -0400 | [diff] [blame] | 328 | ACL_DIR = defaults.get('acl-dir') |
| 329 | GERRIT_HOST = defaults.get('gerrit-host') |
Jay Pipes | a8068d3 | 2013-06-22 20:33:41 -0400 | [diff] [blame] | 330 | GERRIT_PORT = int(defaults.get('gerrit-port', '29418')) |
Sean Dague | 5906b83 | 2013-05-03 18:08:26 -0400 | [diff] [blame] | 331 | GERRIT_USER = defaults.get('gerrit-user') |
| 332 | GERRIT_KEY = defaults.get('gerrit-key') |
Sean Dague | 9023a46 | 2013-05-06 06:53:10 -0400 | [diff] [blame] | 333 | GERRIT_GITID = defaults.get('gerrit-committer') |
Jay Pipes | 2f13d56 | 2013-06-16 12:01:09 -0400 | [diff] [blame] | 334 | GERRIT_SYSTEM_USER = defaults.get('gerrit-system-user', 'gerrit2') |
| 335 | GERRIT_SYSTEM_GROUP = defaults.get('gerrit-system-group', 'gerrit2') |
Sean Dague | 5906b83 | 2013-05-03 18:08:26 -0400 | [diff] [blame] | 336 | |
Monty Taylor | da3bada | 2012-11-22 09:38:22 -0800 | [diff] [blame] | 337 | gerrit = gerritlib.gerrit.Gerrit('localhost', |
| 338 | GERRIT_USER, |
Monty Taylor | 805a301 | 2013-06-21 02:32:08 -0400 | [diff] [blame] | 339 | GERRIT_PORT, |
Monty Taylor | da3bada | 2012-11-22 09:38:22 -0800 | [diff] [blame] | 340 | GERRIT_KEY) |
| 341 | project_list = gerrit.listProjects() |
| 342 | ssh_env = make_ssh_wrapper(GERRIT_USER, GERRIT_KEY) |
| 343 | try: |
Monty Taylor | 16390e2 | 2012-11-04 22:20:36 +0100 | [diff] [blame] | 344 | |
James E. Blair | 18e8256 | 2013-04-04 16:20:32 +0000 | [diff] [blame] | 345 | for section in configs[1]: |
| 346 | project = section['project'] |
James E. Blair | eab9261 | 2013-08-30 09:47:32 -0700 | [diff] [blame] | 347 | if args.projects and project not in args.projects: |
| 348 | continue |
James E. Blair | 18e8256 | 2013-04-04 16:20:32 +0000 | [diff] [blame] | 349 | options = section.get('options', dict()) |
| 350 | description = section.get('description', None) |
| 351 | homepage = section.get('homepage', defaults.get('homepage', None)) |
| 352 | upstream = section.get('upstream', None) |
Monty Taylor | 90a630f | 2013-07-03 12:45:26 -0700 | [diff] [blame] | 353 | upstream_prefix = section.get('upstream-prefix', None) |
| 354 | track_upstream = 'track-upstream' in options |
James E. Blair | 18e8256 | 2013-04-04 16:20:32 +0000 | [diff] [blame] | 355 | |
Monty Taylor | da3bada | 2012-11-22 09:38:22 -0800 | [diff] [blame] | 356 | project_git = "%s.git" % project |
Monty Taylor | 90a630f | 2013-07-03 12:45:26 -0700 | [diff] [blame] | 357 | remote_url = "ssh://localhost:%s/%s" % (GERRIT_PORT, project) |
| 358 | |
Jeremy Stanley | d211fa5 | 2013-10-16 19:34:07 +0000 | [diff] [blame] | 359 | # Create the project in Gerrit first, since it will fail |
| 360 | # spectacularly if its project directory or local replica |
| 361 | # already exist on disk |
| 362 | project_created = False |
| 363 | if project not in project_list: |
| 364 | try: |
| 365 | gerrit.createProject(project) |
| 366 | project_created = True |
| 367 | except Exception: |
| 368 | log.exception( |
| 369 | "Exception creating %s in Gerrit." % project) |
| 370 | |
Monty Taylor | 90a630f | 2013-07-03 12:45:26 -0700 | [diff] [blame] | 371 | # Create the repo for the local git mirror |
| 372 | git_mirror_path = os.path.join(LOCAL_GIT_DIR, project_git) |
| 373 | if not os.path.exists(git_mirror_path): |
| 374 | run_command("git --bare init %s" % git_mirror_path) |
| 375 | run_command("chown -R %s:%s %s" |
| 376 | % (GERRIT_SYSTEM_USER, GERRIT_SYSTEM_GROUP, |
| 377 | git_mirror_path)) |
| 378 | |
| 379 | # Start processing the local JEEPYB cache |
| 380 | repo_path = os.path.join(JEEPYB_CACHE_DIR, project) |
| 381 | git_opts = dict(upstream=upstream, |
| 382 | repo_path=repo_path, |
| 383 | remote_url=remote_url) |
| 384 | |
| 385 | # If we don't have a local copy already, get one |
Jeremy Stanley | d211fa5 | 2013-10-16 19:34:07 +0000 | [diff] [blame] | 386 | if not os.path.exists(repo_path) or project_created: |
Monty Taylor | 90a630f | 2013-07-03 12:45:26 -0700 | [diff] [blame] | 387 | if not os.path.exists(os.path.dirname(repo_path)): |
| 388 | os.makedirs(os.path.dirname(repo_path)) |
| 389 | # Three choices |
| 390 | # - If gerrit has it, get from gerrit |
| 391 | # - If gerrit doesn't have it: |
| 392 | # - If it has an upstream, clone that |
| 393 | # - If it doesn't, create it |
| 394 | |
| 395 | # Gerrit knows about the project, clone it |
| 396 | if project in project_list: |
| 397 | run_command( |
| 398 | "git clone %(remote_url)s %(repo_path)s" % git_opts, |
| 399 | env=ssh_env) |
| 400 | if upstream: |
| 401 | git_command( |
| 402 | repo_path, |
| 403 | "remote add -f upstream %(upstream)s" % git_opts) |
| 404 | |
| 405 | # Gerrit doesn't have it, but it has an upstream configured |
| 406 | # We're probably importing it for the first time, clone |
| 407 | # upstream, but then ongoing we want gerrit to ge origin |
| 408 | # and upstream to be only there for ongoing tracking |
| 409 | # purposes, so rename origin to upstream and add a new |
| 410 | # origin remote that points at gerrit |
| 411 | elif upstream: |
| 412 | run_command( |
| 413 | "git clone %(upstream)s %(repo_path)s" % git_opts, |
| 414 | env=ssh_env) |
| 415 | git_command( |
| 416 | repo_path, |
| 417 | "fetch origin +refs/heads/*:refs/copy/heads/*", |
| 418 | env=ssh_env) |
| 419 | git_command(repo_path, "remote rename origin upstream") |
| 420 | git_command( |
| 421 | repo_path, |
| 422 | "remote add origin %(remote_url)s" % git_opts) |
| 423 | push_string = "push %s +refs/copy/heads/*:refs/heads/*" |
| 424 | |
| 425 | # Neither gerrit has it, nor does it have an upstream, |
| 426 | # just create a whole new one |
| 427 | else: |
| 428 | run_command("git init %s" % repo_path) |
| 429 | git_command( |
| 430 | repo_path, |
Monty Taylor | f5b1118 | 2013-10-18 19:32:03 -0400 | [diff] [blame] | 431 | "remote add origin %(remote_url)s" % git_opts) |
Monty Taylor | 90a630f | 2013-07-03 12:45:26 -0700 | [diff] [blame] | 432 | with open(os.path.join(repo_path, |
| 433 | ".gitreview"), |
| 434 | 'w') as gitreview: |
| 435 | gitreview.write("""[gerrit] |
| 436 | host=%s |
| 437 | port=%s |
| 438 | project=%s |
| 439 | """ % (GERRIT_HOST, GERRIT_PORT, project_git)) |
| 440 | git_command(repo_path, "add .gitreview") |
| 441 | cmd = ("commit -a -m'Added .gitreview' --author='%s'" |
| 442 | % GERRIT_GITID) |
| 443 | git_command(repo_path, cmd) |
| 444 | push_string = "push %s HEAD:refs/heads/master" |
| 445 | |
| 446 | # We do have a local copy of it already, make sure it's |
| 447 | # in shape to have work done. |
| 448 | else: |
| 449 | if project in project_list: |
| 450 | |
| 451 | # If we're configured to track upstream but the repo |
| 452 | # does not have an upstream remote, add one |
| 453 | if (track_upstream and |
| 454 | 'upstream' not in git_command_output( |
| 455 | repo_path, 'remote')[1]): |
| 456 | git_command( |
| 457 | repo_path, |
| 458 | "remote add upstream %(upstream)s" % git_opts) |
| 459 | |
| 460 | # If we're configured to track upstream, make sure that |
| 461 | # the upstream URL matches the config |
| 462 | else: |
| 463 | git_command( |
| 464 | repo_path, |
| 465 | "remote set-url upstream %(upstream)s" % git_opts) |
| 466 | |
| 467 | # Now that we have any upstreams configured, fetch all |
| 468 | # of the refs we might need, pruning remote branches |
| 469 | # that no longer exist |
| 470 | git_command( |
| 471 | repo_path, "remote update --prune", env=ssh_env) |
| 472 | |
| 473 | # TODO(mordred): This is here so that later we can |
| 474 | # inspect the master branch for meta-info |
| 475 | # Checkout master and reset to the state of origin/master |
| 476 | git_command(repo_path, "checkout -B master origin/master") |
| 477 | |
| 478 | description = find_description_override(repo_path) or description |
Monty Taylor | 16390e2 | 2012-11-04 22:20:36 +0100 | [diff] [blame] | 479 | |
Sean Dague | 5906b83 | 2013-05-03 18:08:26 -0400 | [diff] [blame] | 480 | if 'has-github' in options or default_has_github: |
| 481 | create_github_project(defaults, options, project, |
| 482 | description, homepage) |
Monty Taylor | da3bada | 2012-11-22 09:38:22 -0800 | [diff] [blame] | 483 | |
Jeremy Stanley | d211fa5 | 2013-10-16 19:34:07 +0000 | [diff] [blame] | 484 | if project_created: |
Monty Taylor | da3bada | 2012-11-22 09:38:22 -0800 | [diff] [blame] | 485 | try: |
Monty Taylor | da3bada | 2012-11-22 09:38:22 -0800 | [diff] [blame] | 486 | git_command(repo_path, |
Clark Boylan | e0c725b | 2012-11-30 15:15:55 -0800 | [diff] [blame] | 487 | push_string % remote_url, |
Monty Taylor | da3bada | 2012-11-22 09:38:22 -0800 | [diff] [blame] | 488 | env=ssh_env) |
| 489 | git_command(repo_path, |
| 490 | "push --tags %s" % remote_url, env=ssh_env) |
Clark Boylan | 796eada | 2013-09-25 10:51:35 -0700 | [diff] [blame] | 491 | except Exception: |
| 492 | log.exception( |
Jeremy Stanley | d211fa5 | 2013-10-16 19:34:07 +0000 | [diff] [blame] | 493 | "Error pushing %s to Gerrit." % project) |
Monty Taylor | 90a630f | 2013-07-03 12:45:26 -0700 | [diff] [blame] | 494 | |
| 495 | # If we're configured to track upstream, make sure we have |
| 496 | # upstream's refs, and then push them to the appropriate |
| 497 | # branches in gerrit |
| 498 | if track_upstream: |
| 499 | git_command( |
| 500 | repo_path, |
| 501 | "remote update upstream --prune", env=ssh_env) |
| 502 | # Any branch that exists in the upstream remote, we want |
| 503 | # a local branch of, optionally prefixed with the |
| 504 | # upstream prefix value |
| 505 | for branch in git_command_output( |
| 506 | repo_path, "branch -a")[1].split(): |
| 507 | if not branch.strip().startswith("remotes/upstream"): |
| 508 | continue |
| 509 | if "->" in branch: |
| 510 | continue |
| 511 | local_branch = branch[len('remotes/upstream/'):] |
| 512 | if upstream_prefix: |
| 513 | local_branch = "%s/%s" % ( |
| 514 | upstream_prefix, local_branch) |
| 515 | |
| 516 | # Check out an up to date copy of the branch, so that |
| 517 | # we can push it and it will get picked up below |
| 518 | git_command(repo_path, "checkout -B %s %s" % ( |
| 519 | local_branch, branch)) |
| 520 | |
| 521 | try: |
| 522 | # Push all of the local branches to similarly named |
| 523 | # Branches on gerrit. Also, push all of the tags |
| 524 | git_command( |
| 525 | repo_path, |
Monty Taylor | a54b0af | 2013-10-15 13:42:59 -0300 | [diff] [blame] | 526 | "push origin refs/heads/*:refs/heads/*", |
Monty Taylor | 90a630f | 2013-07-03 12:45:26 -0700 | [diff] [blame] | 527 | env=ssh_env) |
Monty Taylor | e5c1eed | 2013-10-23 08:55:13 -0500 | [diff] [blame] | 528 | git_command(repo_path, 'push origin --tags', env=ssh_env) |
Monty Taylor | 90a630f | 2013-07-03 12:45:26 -0700 | [diff] [blame] | 529 | except Exception: |
| 530 | log.exception( |
| 531 | "Error pushing %s to Gerrit." % project) |
Monty Taylor | da3bada | 2012-11-22 09:38:22 -0800 | [diff] [blame] | 532 | |
Jeremy Stanley | 8b9f93f | 2013-01-21 02:44:21 +0000 | [diff] [blame] | 533 | try: |
James E. Blair | 18e8256 | 2013-04-04 16:20:32 +0000 | [diff] [blame] | 534 | acl_config = section.get('acl-config', |
| 535 | '%s.config' % os.path.join(ACL_DIR, |
| 536 | project)) |
| 537 | except AttributeError: |
| 538 | acl_config = None |
| 539 | |
| 540 | if acl_config: |
| 541 | if not os.path.isfile(acl_config): |
| 542 | write_acl_config(project, |
| 543 | ACL_DIR, |
| 544 | section.get('acl-base', None), |
| 545 | section.get('acl-append', []), |
| 546 | section.get('acl-parameters', {})) |
James E. Blair | 18e8256 | 2013-04-04 16:20:32 +0000 | [diff] [blame] | 547 | try: |
James E. Blair | 18e8256 | 2013-04-04 16:20:32 +0000 | [diff] [blame] | 548 | if (fetch_config(project, |
| 549 | remote_url, |
| 550 | repo_path, |
| 551 | ssh_env) and |
| 552 | copy_acl_config(project, repo_path, |
| 553 | acl_config) and |
| 554 | create_groups_file(project, gerrit, repo_path)): |
| 555 | push_acl_config(project, |
| 556 | remote_url, |
| 557 | repo_path, |
Sean Dague | 9023a46 | 2013-05-06 06:53:10 -0400 | [diff] [blame] | 558 | GERRIT_GITID, |
James E. Blair | 18e8256 | 2013-04-04 16:20:32 +0000 | [diff] [blame] | 559 | ssh_env) |
Clark Boylan | 796eada | 2013-09-25 10:51:35 -0700 | [diff] [blame] | 560 | except Exception: |
| 561 | log.exception( |
| 562 | "Exception processing ACLS for %s." % project) |
James E. Blair | 18e8256 | 2013-04-04 16:20:32 +0000 | [diff] [blame] | 563 | finally: |
Monty Taylor | f8b2b62 | 2013-10-12 20:51:25 -0400 | [diff] [blame] | 564 | git_command(repo_path, 'reset --hard') |
Monty Taylor | 90a630f | 2013-07-03 12:45:26 -0700 | [diff] [blame] | 565 | git_command(repo_path, 'checkout master') |
| 566 | git_command(repo_path, 'branch -D config') |
| 567 | |
Monty Taylor | da3bada | 2012-11-22 09:38:22 -0800 | [diff] [blame] | 568 | finally: |
| 569 | os.unlink(ssh_env['GIT_SSH']) |
Jeremy Stanley | 06efb06 | 2013-04-15 20:18:16 +0000 | [diff] [blame] | 570 | |
| 571 | if __name__ == "__main__": |
| 572 | main() |