Add option --codereview-user <username> to add Gerrit repos
[mirror/qt/qt5.git] / init-repository
1 #!/usr/bin/env perl
2 #############################################################################
3 ##
4 ## Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
5 ## All rights reserved.
6 ## Contact: Nokia Corporation (qt-info@nokia.com)
7 ##
8 ## This file is part of the utilities of the Qt Toolkit.
9 ##
10 ## $QT_BEGIN_LICENSE:LGPL$
11 ## No Commercial Usage
12 ## This file contains pre-release code and may not be distributed.
13 ## You may use this file in accordance with the terms and conditions
14 ## contained in the Technology Preview License Agreement accompanying
15 ## this package.
16 ##
17 ## GNU Lesser General Public License Usage
18 ## Alternatively, this file may be used under the terms of the GNU Lesser
19 ## General Public License version 2.1 as published by the Free Software
20 ## Foundation and appearing in the file LICENSE.LGPL included in the
21 ## packaging of this file.  Please review the following information to
22 ## ensure the GNU Lesser General Public License version 2.1 requirements
23 ## will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ##
25 ## In addition, as a special exception, Nokia gives you certain additional
26 ## rights.  These rights are described in the Nokia Qt LGPL Exception
27 ## version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ##
29 ## If you have questions regarding the use of this file, please contact
30 ## Nokia at qt-info@nokia.com.
31 ##
32 ##
33 ##
34 ##
35 ##
36 ##
37 ##
38 ##
39 ## $QT_END_LICENSE$
40 ##
41 #############################################################################
42
43 use v5.10;
44 use strict;
45 use warnings;
46
47 package Qt::InitRepository;
48
49
50 =head1 NAME
51
52 init-repository - initialize the Qt5 repository and all submodules
53
54 =head1 SYNOPSIS
55
56   ./init-repository [options]
57
58 This script may be run after an initial `git clone' of Qt5 in order to check
59 out all submodules.
60
61
62 =head1 OPTIONS
63
64 B<Global options:>
65
66 =over
67
68 =item --force, -f
69
70 Force initialization (even if the submodules are already checked out).
71
72
73 =item --quiet, -q
74
75 Be quiet.  Will exit cleanly if the repository is already initialized.
76
77 =back
78
79
80 B<Module options:>
81
82 =over
83
84 =item --no-webkit
85
86 Skip webkit and webkit examples submodules.
87 It may be desirable to skip these modules due to the large size of the webkit
88 git repository.
89
90
91 =item --no-update
92
93 Skip the `git submodule update' command.
94
95
96 =item --ignore-submodules
97
98 Set git config to ignore submodules by default when doing operations on the
99 qt5 repo, such as `pull', `fetch', `diff' etc.
100
101 This option is default for --nokia-developer/--brisbane.
102
103 After using this option, pass `--ignore-submodules=none' to git to override
104 it as needed.
105
106 =back
107
108
109 B<Repository options:>
110
111 =over
112
113 =item --nokia-developer
114
115 Switch to internal Nokia URLs.
116
117
118 =item --brisbane
119
120 Switch to internal Nokia URLs and make use of the Brisbane git mirrors.
121 (Implies `--mirror' and `--mirror-webkit').
122
123
124 =item --ssh
125
126 Use the SSH protocol for git operations.  This may be useful if the git
127 protocol is blocked by a firewall. Note that this requires a user account
128 with an uploaded SSH key on all servers used.  (Implies `--nokia-developer').
129
130
131 =item --http
132
133 Use the HTTP protocol for git operations.  This may be useful if the git
134 protocol is blocked by a firewall.  Note that this only works with the
135 external Gitorious server.
136
137
138 =item --codereview-username <Gerrit/JIRA username>
139
140 Adds a gerrit alias to repos under Gerrit codereview management.
141 This requires a username for SSH access to the codereview.qt.nokia.com
142 server, which will be the same username you have for the bugtracker at
143 bugreports.qt.nokia.com.
144
145
146 =item --alternates <path to other Qt5 repo>
147
148 Adds alternates for each submodule to another full qt5 checkout. This makes
149 this qt5 checkout very small, as it will use the object store of the
150 alternates before unique objects are stored in its own object store.
151
152 This option has no effect when using `--no-update'.
153
154 B<NOTE:> This will make this repo dependent on the alternate, which is
155 potentially dangerous!  The dependency can be broken by also using
156 the `--copy-objects' option, or by running C<git repack -a> in each
157 submodule, where required.  Please read the note about the `--shared' option
158 in the documentation of `git clone' for more information.
159
160
161 =item --copy-objects
162
163 When `--alternates' is used, automatically do a C<git repack -a> in each
164 submodule after cloning, to ensure that the repositories are independent
165 from the source used as a reference for cloning.
166
167 Note that this negates the disk usage benefits gained from the use of
168 `--alternates'.
169
170
171 =item --mirror <url-base>
172
173 Uses <url-base> as the base URL for submodule git mirrors.
174
175 For example:
176
177   --mirror user@machine:/foo/bar
178
179 ...will use the following as a mirror for qtbase:
180
181   user@machine:/foo/bar/qtbase.git
182
183
184 =item --mirror-webkit <url>
185
186 Uses <url> as the URL for the webkit git mirror.
187
188 =back
189
190 =cut
191
192 use Carp         qw( confess             );
193 use English      qw( -no_match_vars      );
194 use Getopt::Long qw( GetOptionsFromArray );
195 use Pod::Usage   qw( pod2usage           );
196 use Cwd          qw( getcwd              );
197
198 my %PROTOCOLS = (
199     'internal'  => 'git://scm.dev.nokia.troll.no/' ,
200     'ssh'       => 'git@scm.dev.nokia.troll.no:'   ,
201     'http'      => 'http://git.gitorious.org/'     ,
202 );
203
204 my %STAGING_REPOS = map { $_ => "git://gitorious.org/qt/$_-staging.git" } qw(
205     qt5
206     qt3support
207     qtactiveqt
208     qtbase
209     qtdeclarative
210     qtdoc
211     qtmultimedia
212     qtphonon
213     qtqa
214     qtscript
215     qtsvg
216     qttools
217     qttranslations
218     qtwebkit-examples-and-demos
219     qtxmlpatterns
220     qtlocation
221     qtsensors
222 );
223
224 my %GERRIT_REPOS = map { $_ => "codereview.qt.nokia.com:qt/$_.git" } qw(
225     qtbase
226 );
227
228 my $BNE_MIRROR_URL_BASE
229     = 'git://bq-git.apac.nokia.com/qtsoftware/qt/';
230
231 my $BNE_MIRROR_WEBKIT_URL
232     = 'git://bq-git.apac.nokia.com/qtsoftware/research/gitorious-org-webkit-qtwebkit-mirror.git';
233
234 sub new
235 {
236     my ($class, @arguments) = @_;
237
238     my $self = {};
239     bless $self, $class;
240     $self->parse_arguments(@arguments);
241
242     return $self;
243 }
244
245 # Like `system', but possibly log the command, and die on non-zero exit code
246 sub exe
247 {
248     my ($self, @cmd) = @_;
249
250     if (!$self->{quiet}) {
251         print "+ @cmd\n";
252     }
253
254     if (system(@cmd) != 0) {
255         confess "@cmd exited with status $CHILD_ERROR";
256     }
257
258     return;
259 }
260
261 sub parse_arguments
262 {
263     my ($self, @args) = @_;
264
265     %{$self} = (%{$self},
266         'alternates'          => "",
267         'codereview-username' => "",
268         'detach-alternates'   => 0 ,
269         'force'               => 0 ,
270         'ignore-submodules'   => 0 ,
271         'mirror-url'          => "",
272         'mirror-webkit-url'   => "",
273         'nokia-developer'     => 0 ,
274         'protocol'            => "",
275         'update'              => 1 ,
276         'webkit'              => 1 ,
277     );
278
279     GetOptionsFromArray(\@args,
280         'alternates=s'      =>  \$self->{qw{ alternates        }},
281         'codereview-username=s' => \$self->{qw{ codereview-username }},
282         'copy-objects'      =>  \$self->{qw{ detach-alternates }},
283         'force'             =>  \$self->{qw{ force             }},
284         'ignore-submodules' =>  \$self->{qw{ ignore-submodules }},
285         'mirror-webkit=s'   =>  \$self->{qw{ mirror-webkit-url }},
286         'mirror=s'          =>  \$self->{qw{ mirror-url        }},
287         'nokia-developer'   =>  \$self->{qw{ nokia-developer   }},
288         'quiet'             =>  \$self->{qw{ quiet             }},
289         'update!'           =>  \$self->{qw{ update            }},
290         'webkit!'           =>  \$self->{qw{ webkit            }},
291
292         'help|?'            =>  sub { pod2usage(1);               },
293         'http'              =>  sub { $self->{protocol} = 'http'; },
294         'ssh|ssh-protocol'  =>  sub { $self->{protocol} = 'ssh';  },
295
296         'brisbane|brisbane-nokia-developer' => sub {
297             $self->{'nokia-developer'}   = 1;
298             $self->{'protocol'}          = 'internal';
299             $self->{'mirror-url'}        = $BNE_MIRROR_URL_BASE;
300             $self->{'mirror-webkit-url'} = $BNE_MIRROR_WEBKIT_URL;
301             $self->{'ignore-submodules'} = 1;
302         },
303
304         'nokia-developer' => sub {
305             $self->{'nokia-developer'}   = 1;
306             $self->{'protocol'}          = 'internal';
307             $self->{'ignore-submodules'} = 1;
308         },
309     ) || pod2usage(2);
310
311     if ($self->{'nokia-developer'} && $self->{'protocol'} eq 'http') {
312         print "*** Ignoring use of HTTP protocol, as it's only usable with external server\n";
313         $self->{'protocol'} = '';
314     }
315
316     # Replace any double trailing slashes from end of mirror
317     $self->{'mirror-url'} =~ s{//+$}{/};
318
319     return;
320 }
321
322 sub check_if_already_initialized
323 {
324     my ($self) = @_;
325
326     # We consider the repo as `initialized' if submodule.qtbase.url is set
327     if (qx(git config --get submodule.qtbase.url)) {
328         if ($self->{force}) {
329             my @configresult = qx(git config -l);
330             foreach (@configresult) {
331                 # Example line: submodule.qtqa.url=git://gitorious.org/qt/qtqa.git
332                 if (/(submodule\.[^.=]+)\.url=.*/) {
333                     $self->exe('git', 'config', '--remove-section', $1);
334                 }
335             }
336         }
337         else {
338             exit 0 if ($self->{quiet});
339             print "Will not reinitialize already initialized repository (use -f to force)!\n";
340             exit 1;
341         }
342     }
343
344     return;
345 }
346
347 sub git_submodule_init
348 {
349     my ($self) = @_;
350
351     my @init_args;
352     if ($self->{quiet}) {
353         push @init_args, '--quiet';
354     }
355     $self->exe('git', 'submodule', 'init', @init_args);
356
357     my $template = getcwd()."/.commit-template";
358     if (-e $template) {
359         $self->exe('git', 'config', 'commit.template', $template);
360     }
361
362     return;
363 }
364
365 sub git_disable_webkit_submodule
366 {
367     my ($self) = @_;
368
369     $self->exe('git', 'config', '--remove', 'submodule.qtwebkit');
370     $self->exe('git', 'config', '--remove', 'submodule.qtwebkit-examples-and-demos');
371
372     return;
373 }
374
375 sub git_set_submodule_config
376 {
377     my ($self) = @_;
378
379     my @configresult          = qx(git config -l);
380     my $protocol              = $self->{protocol};
381     my $url_base_for_protocol = $PROTOCOLS{$protocol};
382
383     foreach my $line (@configresult) {
384         # Example line: submodule.qtqa.url=git://gitorious.org/qt/qtqa.git
385         next if ($line !~ /(submodule\.[^.=]+\.url)=(.*)/);
386
387         my $key   = $1;
388         my $value = $2;
389
390         if ($protocol) {
391             # WebKit is special, and has only external link.
392             if ($key ne 'submodule.qtwebkit.url') {
393                 # qt-labs projects are still hosted under qt internally.
394                 if ($protocol ne 'http') {
395                     $value =~ s,^git://gitorious\.org/qt-labs/,${url_base_for_protocol}qt/,;
396                 }
397                 $value =~ s,^git://gitorious\.org/,$url_base_for_protocol,;
398             }
399         }
400
401         $self->exe('git', 'config', $key, $value);
402
403         if ($self->{'ignore-submodules'}) {
404             $key =~ s,\.url,.ignore,;
405             $self->exe('git', 'config', $key, 'all');
406         }
407     }
408
409     return;
410 }
411
412 sub git_clone_all_submodules
413 {
414     my ($self) = @_;
415
416     # manually clone each repo here, so we can easily use reference repos, mirrors and
417     # add all staging repos
418     my @configresult = qx(git config -l);
419     foreach my $line (@configresult) {
420         if ($line =~ /submodule\.([^.=]+)\.url=(.*)/) {
421             $self->git_clone_one_submodule($1, $2);
422         }
423     }
424
425     $self->exe('git', 'submodule', 'update');
426
427     return;
428 }
429
430 sub git_add_remotes
431 {
432     my ($self, $repo_basename) = @_;
433
434     my $protocol              = $self->{protocol};
435     my $url_base_for_protocol = $PROTOCOLS{$protocol};
436
437     my %current_remotes;
438     for my $line (qx(git remote show)) {
439         chomp $line;
440         $current_remotes{$line} = 1;
441     }
442
443     my @gerrit = grep { /^$repo_basename$/; } keys %GERRIT_REPOS;
444     if (!$current_remotes{'gerrit'} && $self->{'codereview-username'}) {
445         foreach my $gerrit_repo (@gerrit) {
446             my $gerrit_repo_url = $GERRIT_REPOS{$gerrit_repo};
447             $self->exe('git', 'remote', 'add', 'gerrit', $self->{'codereview-username'}."@".$gerrit_repo_url);
448         }
449     }
450
451     my @staging = grep { /^$repo_basename$/; } keys %STAGING_REPOS;
452     if (!$current_remotes{'staging'}) {
453         foreach my $staging_repo (@staging) {
454             my $staging_repo_url = $STAGING_REPOS{$staging_repo};
455             if ($protocol) {
456                 if ($protocol ne 'http') {
457                     $staging_repo_url =~ s,^git://gitorious\.org/qt-labs/,${url_base_for_protocol}qt/,;
458                 }
459                 $staging_repo_url =~ s,^git://gitorious\.org/,$url_base_for_protocol,;
460             }
461             $self->exe('git', 'remote', 'add', 'staging', $staging_repo_url);
462         }
463     }
464
465     # if repo has no staging repo defined, alias it to gerrit or origin
466     if (!$current_remotes{'staging'} && !@staging) {
467         my @configresult = qx(git remote -v);
468         my $staging_set = 0;
469         foreach (@configresult) {
470             if (/^gerrit\s+(\S+) \(fetch\)/) {
471                 $self->exe('git', 'remote', 'add', 'staging', $1);
472                 $staging_set = 1;
473             }
474         }
475         unless($staging_set) {
476             foreach (@configresult) {
477                 if (/^origin\s+(\S+) \(fetch\)/) {
478                     $self->exe('git', 'remote', 'add', 'staging', $1);
479                 }
480             }
481         }
482     }
483     #if repo has no gerrit repo defined, alias it to whatever staging now points to (could be origin)
484     if (!$current_remotes{'gerrit'} && !@gerrit) {
485         my @configresult = qx(git remote -v);
486         foreach (@configresult) {
487             if (/^staging\s+(\S+) \(fetch\)/) {
488                 $self->exe('git', 'remote', 'add', 'gerrit', $1);
489             }
490         }
491     }
492
493     return;
494 }
495
496 sub git_clone_one_submodule
497 {
498     my ($self, $submodule, $url) = @_;
499
500     my $alternates            = $self->{ 'alternates'        };
501     my $mirror_url            = $self->{ 'mirror-url'        };
502     my $mirror_webkit_url     = $self->{ 'mirror-webkit-url' };
503
504     # `--reference FOO' args for the clone, if any.
505     my @reference_args;
506
507     if ($alternates) {
508         # alternates is a qt5 repo, so the submodule will be under that.
509         if (-d "$alternates/$submodule") {
510             @reference_args = ('--reference', "$alternates/$submodule");
511         }
512         else {
513             print " *** $alternates/$submodule not found, ignoring alternate for this submodule\n";
514         }
515     }
516
517     my $mirror;
518     if ($mirror_url && ($submodule ne 'qtwebkit')) {
519         $mirror = $mirror_url.$submodule;
520         $mirror .= ".git" unless (-d $mirror); # Support local disk mirror
521     }
522     elsif ($mirror_webkit_url && ($submodule eq 'qtwebkit')) {
523         $mirror = $mirror_webkit_url;
524     }
525
526     my $do_clone = (! -d "$submodule/.git");
527     if ($do_clone) {
528         $self->exe('git', 'clone', @reference_args, ($mirror ? $mirror : $url), $submodule);
529     }
530
531     chdir($submodule) or confess "chdir $submodule: $OS_ERROR";
532
533     if (!$do_clone) {
534         $self->exe('git', 'fetch', ($mirror ? $mirror : $url));
535     }
536
537     my $template = getcwd()."/../.commit-template";
538     if (-e $template) {
539         $self->exe('git', 'config', 'commit.template', $template);
540     }
541
542     if ($mirror) {
543         $self->exe('git', 'config', 'remote.origin.url', $url);
544
545         # In `force' mode, remove the mirror if it already exists,
546         # since we may be reinitializing the module.
547         if ($self->{force}) {
548             eval { $self->exe('git', 'remote', 'rm', 'mirror'); }; # failure is OK
549         }
550
551         $self->exe('git', 'remote', 'add', 'mirror', $mirror);
552     }
553
554     $self->git_add_remotes($submodule);
555
556     if ($self->{'detach-alternates'}) {
557         $self->exe('git', 'repack', '-a');
558
559         my $alternates_path = '.git/objects/info/alternates';
560         unlink($alternates_path) || confess "unlink $alternates_path: $OS_ERROR";
561     }
562
563     chdir("..") or confess "cd ..: $OS_ERROR";
564
565     return;
566 }
567
568 sub run
569 {
570     my ($self) = @_;
571
572     $self->check_if_already_initialized;
573     $self->git_submodule_init;
574
575     if (!$self->{webkit}) {
576         $self->git_disable_webkit_submodule;
577     }
578
579     $self->git_set_submodule_config;
580
581     if ($self->{update}) {
582         $self->git_clone_all_submodules;
583     }
584
585     $self->git_add_remotes('qt5');
586
587     return;
588 }
589
590 #==============================================================================
591
592 Qt::InitRepository->new(@ARGV)->run if (!caller);
593 1;