source: ProjectBuilder/devel/pb-modules/lib/ProjectBuilder/VCS.pm

Last change on this file was 2586, checked in by Bruno Cornec, 4 years ago

Fix #177 for svn pbconf and tested with one level

File size: 29.7 KB
Line 
1#!/usr/bin/perl -w
2#
3# Project Builder VCS module
4# VCS subroutines brought by the the Project-Builder project
5# which can be easily used across projects needing to perform
6# VCS related operations
7#
8# $Id$
9#
10# Copyright B. Cornec 2007-today
11# Eric Anderson's changes are (c) Copyright 2012 Hewlett Packard
12# Provided under the GPL v2
13
14package ProjectBuilder::VCS;
15
16use strict 'vars';
17use Carp 'confess';
18use Cwd 'abs_path';
19use Data::Dumper;
20use English;
21use File::Basename;
22use File::Copy;
23use POSIX qw(strftime);
24use lib qw (lib);
25use ProjectBuilder::Version;
26use ProjectBuilder::Base;
27use ProjectBuilder::Conf;
28
29# Inherit from the "Exporter" module which handles exporting functions.
30
31use vars qw(@ISA @EXPORT);
32use Exporter;
33
34# Export, by default, all the functions into the namespace of
35# any code which uses this module.
36
37our @ISA = qw(Exporter);
38our @EXPORT = qw(pb_vcs_init pb_vcs_export pb_vcs_get_uri pb_vcs_copy pb_vcs_checkout pb_vcs_up pb_vcs_checkin pb_vcs_isdiff pb_vcs_add pb_vcs_add_if_not_in pb_vcs_cmd pb_vcs_compliant pb_vcs_conf_update_v0);
39our ($VERSION,$REVISION,$PBCONFVER) = pb_version_init();
40
41=pod
42
43=head1 NAME
44
45ProjectBuilder::VCS, part of project-builder.org
46
47=head1 DESCRIPTION
48
49This modules provides version control system functions.
50
51=head1 USAGE
52
53=over 4
54
55
56=item B<pb_vcs_init>
57
58This function setup the environment for the VCS system related to the URL given by the pbprojurl configuration parameter.
59The potential parameter indicates whether we should inititate the context or not.
60It sets up environement variables (PBPROJDIR, PBDIR, PBREVISION, PBCMSLOGFILE)
61
62=cut
63
64sub pb_vcs_init {
65
66my $pbinit = shift;
67my $param = shift;
68
69my ($pbprojurl) = pb_conf_get("pbprojurl");
70confess "Undefined pbprojurl for $ENV{'PBPROJ'}\n" if ((not defined $pbprojurl) or (not defined $pbprojurl->{$ENV{'PBPROJ'}}));
71pb_log(2,"DEBUG: Project URL of $ENV{'PBPROJ'}: $pbprojurl->{$ENV{'PBPROJ'}}\n");
72my ($scheme, $account, $host, $port, $path) = pb_get_uri($pbprojurl->{$ENV{'PBPROJ'}});
73my $vcscmd = pb_vcs_cmd($scheme);
74
75my ($pbprojdir) = pb_conf_get_if("pbprojdir");
76
77if ((defined $pbprojdir) && (defined $pbprojdir->{$ENV{'PBPROJ'}})) {
78 $ENV{'PBPROJDIR'} = $pbprojdir->{$ENV{'PBPROJ'}};
79} else {
80 $ENV{'PBPROJDIR'} = "$ENV{'PBDEFDIR'}/$ENV{'PBPROJ'}";
81}
82# Expand potential env variable in it to allow string replacement
83eval { $ENV{'PBPROJDIR'} =~ s/(\$ENV.+\})/$1/eeg };
84
85
86# Computing the default dir for PBDIR.
87# what we have is PBPROJDIR so work from that.
88# Tree identical between PBCONFDIR and PBROOTDIR on one side and
89# PBPROJDIR and PBDIR on the other side.
90
91my $tmp = $ENV{'PBROOTDIR'};
92$tmp =~ s|^$ENV{'PBCONFDIR'}/||;
93# If no subdir, then replace again
94$tmp =~ s|^$ENV{'PBCONFDIR'}||;
95
96#
97# Check project cms compliance
98#
99my $turl = "$pbprojurl->{$ENV{'PBPROJ'}}/$tmp";
100$turl = $pbprojurl->{$ENV{'PBPROJ'}} if ($scheme =~ /^(flat)|(ftp)|(http)|(https)|(file)|(git)\b/o);
101# git svn is like svn
102$turl = "$pbprojurl->{$ENV{'PBPROJ'}}/$tmp" if ($scheme =~ /^git\+svn/o);
103pb_vcs_compliant(undef,'PBDIR',"$ENV{'PBPROJDIR'}/$tmp",$turl,$pbinit);
104
105
106if ($scheme =~ /^hg/) {
107 $tmp = `(cd "$ENV{'PBDIR'}" ; $vcscmd identify )`;
108 chomp($tmp);
109 $tmp =~ s/^.* //;
110 $ENV{'PBREVISION'}=$tmp;
111 $ENV{'PBCMSLOGFILE'}="hg.log";
112} elsif ($scheme =~ /^git/) {
113 if ($scheme =~ /svn/) {
114 $tmp = `(cd "$ENV{'PBDIR'}" ; LANGUAGE=C $vcscmd info | grep -E '^Revision:' | cut -d: -f2)`;
115 $tmp =~ s/\s+//;
116 } else {
117 $tmp = `(cd "$ENV{'PBDIR'}" ; $vcscmd log | head -1 | cut -f2)`;
118 $tmp =~ s/^.* //;
119 # Reduce length of commit id to 8 digit
120 $tmp = substr($tmp,1,8);
121 }
122 chomp($tmp);
123 $ENV{'PBREVISION'}=$tmp;
124 $ENV{'PBCMSLOGFILE'}="git.log";
125} elsif ($scheme =~ /^(flat)|(ftp)|(http)|(https)|(file)\b/o) {
126 $ENV{'PBREVISION'}="flat";
127 $ENV{'PBCMSLOGFILE'}="flat.log";
128} elsif ($scheme =~ /^svn/) {
129 # svnversion more precise than svn info if sbx
130 if ((defined $param) && ($param eq "CMS")) {
131 $tmp = `(LANGUAGE=C $vcscmd info $pbprojurl->{$ENV{'PBPROJ'}} | grep -E '^Revision:' | cut -d: -f2)`;
132 $tmp =~ s/\s+//;
133 } else {
134 $tmp = `(cd "$ENV{'PBDIR'}" ; $vcscmd"version" .)`;
135 }
136 chomp($tmp);
137 $ENV{'PBREVISION'}=$tmp;
138 $ENV{'PBCMSLOGFILE'}="svn.log";
139} elsif ($scheme =~ /^svk/) {
140 $tmp = `(cd "$ENV{'PBDIR'}" ; LANGUAGE=C $vcscmd info . | grep -E '^Revision:' | cut -d: -f2)`;
141 $tmp =~ s/\s+//;
142 chomp($tmp);
143 $ENV{'PBREVISION'}=$tmp;
144 $ENV{'PBCMSLOGFILE'}="svk.log";
145} elsif ($scheme =~ /^cvs/) {
146 # Way too slow
147 #$ENV{'PBREVISION'}=`(cd "$ENV{'PBROOTDIR'}" ; cvs rannotate -f . 2>&1 | awk '{print \$1}' | grep -E '^[0-9]' | cut -d. -f2 |sort -nu | tail -1)`;
148 #chomp($ENV{'PBREVISION'});
149 $ENV{'PBREVISION'}="cvs";
150 $ENV{'PBCMSLOGFILE'}="cvs.log";
151 $ENV{'CVS_RSH'} = "ssh" if ($scheme =~ /ssh/);
152} else {
153 die "cms $scheme unknown";
154}
155
156pb_log(1,"pb_vcs_init returns $scheme,$pbprojurl->{$ENV{'PBPROJ'}}\n");
157return($scheme,$pbprojurl->{$ENV{'PBPROJ'}});
158}
159
160
161=item B<pb_vcs_export>
162
163This function exports a VCS content to a directory.
164The first parameter is the URL of the VCS content.
165The second parameter is the directory in which it is locally exposed (result of a checkout). If undef, then use the original VCS content.
166The third parameter is the directory where we want to deliver it (result of export).
167It returns the original tar file if we need to preserve it and undef if we use the produced one.
168
169=cut
170
171sub pb_vcs_export {
172
173my $uri = shift;
174my $source = shift;
175my $destdir = shift;
176my $tmp;
177my $tmp1;
178
179pb_log(1,"pb_vcs_export uri: $uri - destdir: $destdir\n");
180pb_log(1,"pb_vcs_export source: $source\n") if (defined $source);
181my @date = pb_get_date();
182# If it's not flat, then we have a real uri as source
183my ($scheme, $account, $host, $port, $path) = pb_get_uri($uri);
184my $vcscmd = pb_vcs_cmd($scheme);
185$uri = pb_vcs_mod_socks($uri);
186
187if ($scheme =~ /^svn/) {
188 if (defined $source) {
189 if (-d $source) {
190 $tmp = $destdir;
191 } else {
192 $tmp = "$destdir/".basename($source);
193 }
194 $source = pb_vcs_mod_htftp($source,"svn");
195 pb_system("$vcscmd export $source $tmp","Exporting $source from $scheme to $tmp ");
196 } else {
197 $uri = pb_vcs_mod_htftp($uri,"svn");
198 pb_system("$vcscmd export $uri $destdir","Exporting $uri from $scheme to $destdir ");
199 }
200} elsif ($scheme eq "svk") {
201 my $src = $source;
202 if (defined $source) {
203 if (-d $source) {
204 $tmp = $destdir;
205 } else {
206 $tmp = "$destdir/".basename($source);
207 $src = dirname($source);
208 }
209 $source = pb_vcs_mod_htftp($source,"svk");
210 # This doesn't exist !
211 # pb_system("$vcscmd export $path $tmp","Exporting $path from $scheme to $tmp ");
212 pb_log(4,"$uri,$source,$destdir,$scheme, $account, $host, $port, $path,$tmp");
213 if (-d $source) {
214 pb_system("mkdir -p $tmp ; cd $tmp; tar -cf - -C $source . | tar xf -","Exporting $source from $scheme to $tmp ");
215 } else {
216 # If source is file do not use -C with source
217 pb_system("mkdir -p ".dirname($tmp)." ; cd ".dirname($tmp)."; tar -cf - -C $src ".basename($source)." | tar xf -","Exporting $src/".basename($source)." from $scheme to $tmp ");
218 }
219 } else {
220 # Look at svk admin hotcopy
221 confess "Unable to export from svk without a source defined";
222 }
223} elsif ($scheme eq "dir") {
224 pb_system("cp -r $path $destdir","Copying $uri from DIR to $destdir ");
225} elsif ($scheme =~ /^file/) {
226 eval
227 {
228 require File::MimeInfo;
229 File::MimeInfo->import();
230 };
231 if ($@) {
232 # File::MimeInfo not found
233 confess("ERROR: Install File::MimeInfo to handle scheme $scheme\n");
234 }
235
236 my $mm = mimetype($path);
237 pb_log(2,"mimetype: $mm\n");
238
239 # Check whether the file is well formed
240 # (containing already a directory with the project-version name)
241 #
242 # If it's not the case, we try to adapt, but distro needing
243 # to verify the checksum will have issues (Fedora)
244 # Then upstream should be notified that they need to change their rules
245 # This doesn't apply to patches or additional sources of course.
246 my ($pbwf) = pb_conf_get_if("pbwf");
247 if ((defined $pbwf) && (defined $pbwf->{$ENV{'PBPROJ'}}) && ($path !~ /\/pbpatch\//) && ($path !~ /\/pbsrc\//)) {
248 $destdir = dirname($destdir);
249 pb_log(2,"This is a well-formed file so destdir is now $destdir\n");
250 }
251 pb_mkdir_p($destdir);
252
253 if ($mm =~ /\/x-bzip-compressed-tar$/) {
254 # tar+bzip2
255 pb_system("cd $destdir ; tar xfj $path","Extracting $path in $destdir ");
256 } elsif ($mm =~ /\/x-lzma-compressed-tar$/) {
257 # tar+lzma
258 pb_system("cd $destdir ; tar xfY $path","Extracting $path in $destdir ");
259 } elsif ($mm =~ /\/x-compressed-tar$/) {
260 # tar+gzip
261 pb_system("cd $destdir ; tar xfz $path","Extracting $path in $destdir ");
262 } elsif ($mm =~ /\/x-tar$/) {
263 # tar
264 pb_system("cd $destdir ; tar xf $path","Extracting $path in $destdir ");
265 } elsif ($mm =~ /\/zip$/) {
266 # zip
267 pb_system("cd $destdir ; unzip $path","Extracting $path in $destdir ");
268 } else {
269 # simple file: copy it (patch e.g.)
270 copy($path,$destdir);
271 }
272} elsif ($scheme =~ /^hg/) {
273 if (defined $source) {
274 if (-d $source) {
275 $tmp = $destdir;
276 } else {
277 $tmp = "$destdir/".basename($source);
278 }
279 $source = pb_vcs_mod_htftp($source,"hg");
280 pb_system("cd $source ; $vcscmd archive $tmp","Exporting $source from Mercurial to $tmp ");
281 } else {
282 $uri = pb_vcs_mod_htftp($uri,"hg");
283 pb_system("$vcscmd clone $uri $destdir","Exporting $uri from Mercurial to $destdir ");
284 }
285} elsif ($scheme =~ /^git/) {
286 if ($scheme =~ /svn/) {
287 if (defined $source) {
288 if (-d $source) {
289 $tmp = $destdir;
290 } else {
291 $tmp = "$destdir/".basename($source);
292 }
293 $source = pb_vcs_mod_htftp($source,"git");
294 pb_system("cp -a $source $tmp","Exporting $source from $scheme to $tmp ");
295 } else {
296 $uri = pb_vcs_mod_htftp($uri,"git");
297 pb_system("$vcscmd clone $uri $destdir","Exporting $uri from $scheme to $destdir ");
298 }
299 } else {
300 if (defined $source) {
301 if (-d $source) {
302 $tmp = $destdir;
303 } else {
304 $tmp = "$destdir/".basename($source);
305 }
306 $source = pb_vcs_mod_htftp($source,"git");
307 my ($pbpbr) = pb_conf_get_if("pbpbr");
308 if ((defined $pbpbr) && (defined $pbpbr->{$ENV{'PBPROJ'}})) {
309 # The project uses pbr so benefit from it to export data
310 pb_system("cd $source ; mkdir $tmp ; python setup.py sdist --keep-temp --dist-dir $tmp ; cd $tmp ; file=`ls *.tar.gz` ; if [ _\$file = _ ] || [ ! -f \$file ]; then exit -1; fi; tar xfz \$file ; dir=`tar tvfz \$file | head -1 | awk '{print \$6}'` ; if [ ! -d \$dir ] || [ _\$dir = _ ] || [ \$dir = / ]; then exit -1 ; fi ; mv \$dir/* \$dir/.??* . ; rmdir \$dir ; rm -f \$file ; ls -al ","Exporting current $source from GIT with pbr to $tmp ");
311 } else {
312 # no pbr do it ourselves
313 pb_system("cd $source ; stid=`$vcscmd stash create` ; $vcscmd archive --format=tar \$\{stid:=HEAD\} | (mkdir $tmp && cd $tmp && tar xf -)","Exporting current $source from GIT to $tmp ");
314 }
315 } else {
316 $uri = pb_vcs_mod_htftp($uri,"git");
317 pb_system("$vcscmd clone $uri $destdir","Exporting $uri from GIT to $destdir ");
318 }
319 }
320} elsif ($scheme =~ /^cvs/) {
321 # CVS needs a relative path !
322 my $dir=dirname($destdir);
323 my $base=basename($destdir);
324 if (defined $source) {
325 # CVS also needs a modules name not a dir
326 $tmp1 = basename($source);
327 } else {
328 # Probably not right, should be checked, but that way I'll notice it :-)
329 pb_log(0,"You're in an untested part of project-builder.org, please report any result upstream\n");
330 $tmp1 = $uri;
331 }
332 # If we're working on the CVS itself
333 my $cvstag = basename($ENV{'PBROOTDIR'});
334 my $cvsopt = "";
335 if ($cvstag eq "cvs") {
336 my $pbdate = strftime("%Y-%m-%d %H:%M:%S", @date);
337 $cvsopt = "-D \"$pbdate\"";
338 } else {
339 # we're working on a tag which should be the last part of PBROOTDIR
340 $cvsopt = "-r $cvstag";
341 }
342 pb_system("cd $dir ; $vcscmd -d $account\@$host:$path export $cvsopt -d $base $tmp1","Exporting $tmp1 from $source under CVS to $destdir ");
343} elsif (($scheme =~ /http/) || ($scheme eq "ftp")) {
344 my $f = basename($path);
345 unlink "$ENV{'PBTMP'}/$f";
346 pb_system("$vcscmd $ENV{'PBTMP'}/$f $uri","Downloading $uri with $vcscmd to $ENV{'PBTMP'}/$f\n");
347 # We want to preserve the original tar file
348 pb_vcs_export("file://$ENV{'PBTMP'}/$f",$source,$destdir);
349 return("$ENV{'PBTMP'}/$f");
350} else {
351 confess "cms $scheme unknown";
352}
353return(undef);
354}
355
356=item B<pb_vcs_get_uri>
357
358This function is only called with a real VCS system and gives the URL stored in the checked out directory.
359The first parameter is the schema of the VCS systems (svn, cvs, svn+ssh, ...)
360The second parameter is the directory in which it is locally exposed (result of a checkout).
361
362=cut
363
364sub pb_vcs_get_uri {
365
366my $scheme = shift;
367my $dir = shift;
368
369my $res = "";
370my $void = "";
371my $vcscmd = pb_vcs_cmd($scheme);
372
373pb_log(3,"scheme: $scheme - dir: $dir - vcscmd: $vcscmd\n");
374if ($scheme =~ /^svn/) {
375 open(PIPE,"LANGUAGE=C $vcscmd info $dir 2> /dev/null |") || return("");
376 while (<PIPE>) {
377 pb_log(4,"line: $_");
378 if (/^URL[\s]*:/) {
379 ($void,$res) = split(/^URL[\s]*:/);
380 last;
381 }
382 }
383 $res =~ s/^\s*//;
384 close(PIPE);
385 chomp($res);
386 pb_log(3,"res $res\n");
387} elsif ($scheme =~ /^svk/) {
388 open(PIPE,"LANGUAGE=C $vcscmd info $dir 2> /dev/null |") || return("");
389 my $void2 = "";
390 while (<PIPE>) {
391 ($void,$void2,$res) = split(/ /) if (/^Depot/);
392 }
393 $res =~ s/^\s*//;
394 close(PIPE);
395 chomp($res);
396} elsif ($scheme =~ /^hg/) {
397 open(HGRC,".hg/hgrc/") || return("");
398 while (<HGRC>) {
399 ($void,$res) = split(/^default.*=/) if (/^default.*=/);
400 }
401 close(HGRC);
402 chomp($res);
403} elsif ($scheme =~ /^git/) {
404 if ($scheme =~ /svn/) {
405 my $cwd = abs_path();
406 chdir($dir) || return("");;
407 open(PIPE,"LANGUAGE=C $vcscmd info . 2> /dev/null |") || return("");
408 chdir($cwd) || return("");
409 while (<PIPE>) {
410 ($void,$res) = split(/^URL[\s]*:/) if (/^URL[\s]*:/);
411 }
412 $res =~ s/^\s*//;
413 close(PIPE);
414 chomp($res);
415 # We've got an SVN ref so add git in front of it for coherency
416 $res = "git+".$res;
417 } else {
418 # Pure git
419 # First we may deal with a separate git repo under $dir
420 if ( -d "$dir/.git" ) {
421 open(GIT,"LANGUAGE=C $vcscmd --git-dir=$dir/.git remote -v 2> /dev/null |") || return("");
422 } else {
423 # If not, the pbconf dir may be in the pbprojdir so sharing the .git dir
424 my $cwd = abs_path();
425 chdir($ENV{'PBPROJDIR'}) || return("");;
426 open(GIT,"LANGUAGE=C $vcscmd remote -v 2> /dev/null |") || return("");
427 chdir($cwd) || return("");
428 }
429 my ($pborigin) = pb_conf_get("pbgitremote");
430 # Default git remote location called origin by default
431 $pborigin->{$ENV{'PBPROJ'}} = "origin" if ((not defined $pborigin) || (not defined $pborigin->{$ENV{'PBPROJ'}}));
432 while (<GIT>) {
433 next unless (/^$pborigin->{$ENV{'PBPROJ'}}\s+(\S+) \(push\)$/);
434 $res = $1;
435 if (($res =~ /@/) && ($res !~ /:\/\//)) {
436 # we have an ssh connection, return it
437 $res = "ssh://$res";
438 }
439 return $res;
440 }
441 close(GIT);
442 warn "Unable to find a remote git $pborigin->{$ENV{'PBPROJ'}} under $dir nor $ENV{'PBPROJDIR'}";
443 return "";
444 }
445} elsif ($scheme =~ /^cvs/) {
446 # This path is always the root path of CVS, but we may be below
447 open(FILE,"$dir/CVS/Root") || confess "$dir isn't CVS controlled";
448 $res = <FILE>;
449 chomp($res);
450 close(FILE);
451 # Find where we are in the tree
452 my $rdir = $dir;
453 while ((! -d "$rdir/CVSROOT") && ($rdir ne "/")) {
454 $rdir = dirname($rdir);
455 }
456 confess "Unable to find a CVSROOT dir in the parents of $dir" if (! -d "$rdir/CVSROOT");
457 #compute our place under that root dir - should be a relative path
458 $dir =~ s|^$rdir||;
459 my $suffix = "";
460 $suffix = "$dir" if ($dir ne "");
461
462 my $prefix = "";
463 if ($scheme =~ /ssh/) {
464 $prefix = "cvs+ssh://";
465 } else {
466 $prefix = "cvs://";
467 }
468 $res = $prefix.$res.$suffix;
469} else {
470 confess "cms $scheme unknown";
471}
472pb_log(1,"pb_vcs_get_uri returns $res\n");
473return($res);
474}
475
476=item B<pb_vcs_copy>
477
478This function copies a VCS content to another.
479The first parameter is the schema of the VCS systems (svn, cvs, svn+ssh, ...)
480The second parameter is the URL of the original VCS content.
481The third parameter is the URL of the destination VCS content.
482
483Only coded for SVN now as used for pbconf itself not the project
484
485=cut
486
487sub pb_vcs_copy {
488my $scheme = shift;
489my $oldurl = shift;
490my $newurl = shift;
491my $vcscmd = pb_vcs_cmd($scheme);
492$oldurl = pb_vcs_mod_socks($oldurl);
493$newurl = pb_vcs_mod_socks($newurl);
494
495if ($scheme =~ /^svn/) {
496 $oldurl = pb_vcs_mod_htftp($oldurl,"svn");
497 $newurl = pb_vcs_mod_htftp($newurl,"svn");
498 pb_system("$vcscmd copy -m \"Creation of $newurl from $oldurl\" $oldurl $newurl","Copying $oldurl to $newurl ");
499} elsif ($scheme =~ /^(flat)|(ftp)|(http)|(https)|(file)\b/o) {
500 # Nothing to do.
501} else {
502 confess "cms $scheme unknown for project management";
503}
504}
505
506=item B<pb_vcs_checkout>
507
508This function checks a VCS content out to a directory.
509The first parameter is the schema of the VCS systems (svn, cvs, svn+ssh, ...)
510The second parameter is the URL of the VCS content.
511The third parameter is the directory where we want to deliver it (result of export).
512
513=cut
514
515sub pb_vcs_checkout {
516my $scheme = shift;
517my $url = shift;
518my $destination = shift;
519my $vcscmd = pb_vcs_cmd($scheme);
520$url = pb_vcs_mod_socks($url);
521
522if ($scheme =~ /^svn/) {
523 $url = pb_vcs_mod_htftp($url,"svn");
524 pb_system("$vcscmd co $url $destination","Checking out $url to $destination ");
525} elsif ($scheme =~ /^svk/) {
526 $url = pb_vcs_mod_htftp($url,"svk");
527 pb_system("$vcscmd co $url $destination","Checking out $url to $destination ");
528} elsif ($scheme =~ /^hg/) {
529 $url = pb_vcs_mod_htftp($url,"hg");
530 pb_system("$vcscmd clone $url $destination","Checking out $url to $destination ");
531} elsif ($scheme =~ /^git/) {
532 $url = pb_vcs_mod_htftp($url,"git");
533 pb_system("$vcscmd clone $url $destination","Checking out $url to $destination ");
534} elsif (($scheme eq "ftp") || ($scheme =~ /http/)) {
535 return;
536} elsif ($scheme =~ /^cvs/) {
537 my ($scheme, $account, $host, $port, $path) = pb_get_uri($url);
538
539 # If we're working on the CVS itself
540 my $cvstag = basename($ENV{'PBROOTDIR'});
541 my $cvsopt = "";
542 if ($cvstag eq "cvs") {
543 my @date = pb_get_date();
544 my $pbdate = strftime("%Y-%m-%d %H:%M:%S", @date);
545 $cvsopt = "-D \"$pbdate\"";
546 } else {
547 # we're working on a tag which should be the last part of PBROOTDIR
548 $cvsopt = "-r $cvstag";
549 }
550 pb_mkdir_p("$destination");
551 pb_system("cd $destination ; $vcscmd -d $account\@$host:$path co $cvsopt .","Checking out $url to $destination ");
552} elsif ($scheme =~ /^file/) {
553 pb_vcs_export($url,undef,$destination);
554} else {
555 confess "cms $scheme unknown";
556}
557}
558
559=item B<pb_vcs_up>
560
561This function updates a local directory with the VCS content.
562The first parameter is the schema of the VCS systems (svn, cvs, svn+ssh, ...)
563The second parameter is the list of directory to update.
564
565=cut
566
567sub pb_vcs_up {
568my $scheme = shift;
569my @dir = @_;
570my $vcscmd = pb_vcs_cmd($scheme);
571
572if ($scheme =~ /^((svn)|(cvs)|(svk))/o) {
573 pb_system("$vcscmd up ".join(' ',@dir),"Updating ".join(' ',@dir));
574} elsif ($scheme =~ /^((hg)|(git))/o) {
575 foreach my $d (@dir) {
576 pb_system("(cd $d && $vcscmd fetch)", "Updating $d ");
577 }
578} elsif ($scheme =~ /^(flat)|(ftp)|(http)|(https)|(file)\b/o) {
579 # Nothing to do.
580} else {
581 confess "cms $scheme unknown";
582}
583}
584
585=item B<pb_vcs_checkin>
586
587This function updates a VCS content from a local directory.
588The first parameter is the schema of the VCS systems (svn, cvs, svn+ssh, ...)
589The second parameter is the directory to update from.
590The third parameter is the comment to pass during the commit
591
592=cut
593
594sub pb_vcs_checkin {
595my $scheme = shift;
596my $dir = shift;
597my $msg = shift;
598my $vcscmd = pb_vcs_cmd($scheme);
599
600if ($scheme =~ /^((svn)|(cvs)|(svk))/o) {
601 pb_system("cd $dir && $vcscmd ci -m \"$msg\" .","Checking in $dir ");
602} elsif ($scheme =~ /^git/) {
603 pb_system("cd $dir && git commit -m \"$msg\"", "Checking in $dir ");
604} elsif ($scheme =~ /^(flat)|(ftp)|(http)|(https)|(file)\b/o) {
605 # Nothing to do.
606} else {
607 confess "cms $scheme unknown";
608}
609pb_vcs_up($scheme,$dir);
610}
611
612=item B<pb_vcs_add_if_not_in>
613
614This function adds to a VCS content from a local directory if the content wasn't already managed under the VCS.
615The first parameter is the schema of the VCS systems (svn, cvs, svn+ssh, ...)
616The second parameter is a list of directory/file to add.
617
618=cut
619
620sub pb_vcs_add_if_not_in {
621my $scheme = shift;
622my $vcscmd = pb_vcs_cmd($scheme);
623
624if ($scheme =~ /^((hg)|(git)|(svn)|(svk)|(cvs))/o) {
625 for my $f (@_) {
626 my $uri = pb_vcs_get_uri($scheme,$f);
627 pb_log(3,"f: $f - scheme: $scheme - uri: $uri\n");
628 my ($scheme2, $a, $h, $p, $n) = pb_get_uri($uri);
629 pb_vcs_add($scheme,$f) if ("$scheme2" ne "$scheme");
630 }
631} elsif ($scheme =~ /^(flat)|(ftp)|(http)|(https)|(file)\b/o) {
632 # Nothing to do.
633} else {
634 confess "cms $scheme unknown";
635}
636}
637
638=item B<pb_vcs_add>
639
640This function adds to a VCS content from a local directory.
641The first parameter is the schema of the VCS systems (svn, cvs, svn+ssh, ...)
642The second parameter is a list of directory/file to add.
643
644=cut
645
646sub pb_vcs_add {
647my $scheme = shift;
648my @f = @_;
649my $vcscmd = pb_vcs_cmd($scheme);
650
651if ($scheme =~ /^((hg)|(git)|(svn)|(svk)|(cvs))/o) {
652 $vcscmd =~ s/ svn// if (($scheme =~ /git/) && ($scheme =~ /svn/));
653 if ($scheme =~ /git/) {
654 # Here we need to be in the right place to add
655 for my $f (@f) {
656 my $dir = dirname($f);
657 pb_system("cd $dir ; $vcscmd add ".basename($f),"Adding $f to VCS ");
658 }
659 } else {
660 pb_system("$vcscmd add ".join(' ',@f),"Adding ".join(' ',@f)." to VCS ");
661 }
662} elsif ($scheme =~ /^(flat)|(ftp)|(http)|(https)|(file)\b/o) {
663 # Nothing to do.
664} else {
665 confess "cms $scheme unknown";
666}
667my @f1;
668foreach my $f (@f) {
669 push(@f1,$f) if (-d $f);
670 push(@f1,dirname($f)) if (-f $f);
671}
672# Wrong we need to push instead
673pb_vcs_up($scheme,@f1);
674}
675
676=item B<pb_vcs_isdiff>
677
678This function returns a integer indicating the number of differences between the VCS content and the local directory where it's checked out.
679The first parameter is the schema of the VCS systems (svn, cvs, svn+ssh, ...)
680The second parameter is the directory to consider.
681
682=cut
683
684sub pb_vcs_isdiff {
685my $scheme = shift;
686my $dir =shift;
687my $vcscmd = pb_vcs_cmd($scheme);
688my $l = undef;
689
690if ($scheme =~ /^((svn)|(cvs)|(svk)|(git))/o) {
691 $vcscmd =~ s/ svn// if (($scheme =~ /git/) && ($scheme =~ /svn/));
692 open(PIPE,"$vcscmd diff $dir |") || confess "Unable to get $vcscmd diff from $dir";
693 $l = 0;
694 while (<PIPE>) {
695 # Skipping normal messages in case of CVS
696 next if (/^cvs diff:/);
697 $l++;
698 }
699} elsif ($scheme =~ /^(flat)|(ftp)|(http)|(https)|(file)\b/o) {
700 $l = 0;
701} else {
702 confess "cms $scheme unknown";
703}
704pb_log(1,"pb_vcs_isdiff returns $l\n");
705return($l);
706}
707
708sub pb_vcs_mod_htftp {
709
710my $url = shift;
711my $proto = shift;
712
713$url =~ s/^$proto\+((ht|f)tp[s]*):/$1:/;
714pb_log(1,"pb_vcs_mod_htftp returns $url\n");
715return($url);
716}
717
718sub pb_vcs_mod_socks {
719
720my $url = shift;
721
722$url =~ s/^([A-z0-9]+)\+(socks):/$1:/;
723pb_log(1,"pb_vcs_mod_socks returns $url\n");
724return($url);
725}
726
727
728sub pb_vcs_cmd {
729
730my $scheme = shift;
731my $cmd = "";
732my $cmdopt = "";
733
734# If there is a socks proxy to use
735if ($scheme =~ /socks/) {
736 # Get the socks proxy command from the conf file
737 my ($pbsockscmd) = pb_conf_get("pbsockscmd");
738 $cmd = "$pbsockscmd->{$ENV{'PBPROJ'}} ";
739}
740
741if (defined $ENV{'PBVCSOPT'}) {
742 $cmdopt .= " $ENV{'PBVCSOPT'}";
743}
744
745if ($scheme =~ /hg/) {
746 $cmd .= "hg".$cmdopt;
747} elsif ($scheme =~ /git/) {
748 if ($scheme =~ /svn/) {
749 $cmd .= "git svn".$cmdopt;
750 } else {
751 $cmd .= "git".$cmdopt;
752 }
753} elsif ($scheme =~ /svn/) {
754 $cmd .= "svn".$cmdopt;
755} elsif ($scheme =~ /svk/) {
756 $cmd .= "svk".$cmdopt;
757} elsif ($scheme =~ /cvs/) {
758 $cmd .= "cvs".$cmdopt;
759} elsif (($scheme =~ /http/) || ($scheme =~ /ftp/)) {
760 my $command = pb_check_req("wget",1);
761 if (-x $command) {
762 $cmd .= "$command -nv -O ";
763 } else {
764 $command = pb_check_req("curl",1);
765 if (-x $command) {
766 $cmd .= "$command -o ";
767 } else {
768 confess "Unable to handle $scheme.\nNo wget/curl available, please install one of those";
769 }
770 }
771} else {
772 $cmd = "";
773}
774pb_log(3,"pb_vcs_cmd returns $cmd\n");
775return($cmd);
776}
777
778=item B<pb_vcs_mkdir>
779
780This function makes a VCS directory
781The first parameter is the schema of the VCS systems (svn, cvs, svn+ssh, ...)
782The second parameter is the directory to create.
783The third parameter is the comment to pass during the commit
784
785=cut
786
787sub pb_vcs_mkdir {
788my $scheme = shift;
789my $dir = shift;
790my $msg = shift;
791my $vcscmd = pb_vcs_cmd($scheme);
792
793if ($scheme =~ /^((svn)|(cvs)|(svk))/o) {
794 pb_system("$vcscmd mkdir -m \"$msg\" $dir","Making VCS directory $dir");
795} elsif ($scheme =~ /^git/) {
796 #pb_system("cd $dir && git init ", "Making VCS directory $dir");
797 ## + git remote add + git push
798} elsif ($scheme =~ /^(flat)|(ftp)|(http)|(https)|(file)\b/o) {
799 # Nothing to do.
800} else {
801 confess "cms $scheme unknown";
802}
803}
804
805
806
807=item B<pb_vcs_compliant>
808
809This function checks the compliance of the project and the pbconf directory.
810The first parameter is the key name of the value that needs to be read in the configuration file.
811The second parameter is the environment variable this key will populate.
812The third parameter is the location of the pbconf dir.
813The fourth parameter is the URI of the VCS content related to the pbconf dir.
814The fifth parameter indicates whether we should inititate the context or not.
815
816Only called for PBCONFDIR and PBDIR
817=cut
818
819sub pb_vcs_compliant {
820
821my $param = shift;
822my $envar = shift;
823my $defdir = shift;
824my $uri = shift;
825my $pbinit = shift;
826my %pdir;
827
828pb_log(1,"pb_vcs_compliant: envar: $envar - defdir: $defdir - uri: $uri\n");
829my ($pdir) = pb_conf_get_if($param) if (defined $param);
830if (defined $pdir) {
831 %pdir = %$pdir;
832}
833
834if ((defined $pdir) && (%pdir) && (defined $pdir{$ENV{'PBPROJ'}})) {
835 # That's always the environment variable that will be used
836 $ENV{$envar} = $pdir{$ENV{'PBPROJ'}};
837} else {
838 if (defined $param) {
839 pb_log(1,"WARNING: no $param defined, using $defdir\n");
840 pb_log(1," Please create a $param reference for project $ENV{'PBPROJ'} in $ENV{'PBETC'}\n");
841 pb_log(1," if you want to use another directory\n");
842 }
843 $ENV{$envar} = "$defdir";
844}
845
846# Expand potential env variable in it
847eval { $ENV{$envar} =~ s/(\$ENV.+\})/$1/eeg };
848pb_log(2,"$envar: $ENV{$envar}\n");
849my $exportdir = $ENV{$envar};
850
851my ($scheme, $account, $host, $port, $path) = pb_get_uri($uri);
852
853if (($scheme !~ /^cvs/) && ($scheme !~ /^svn/) && ($scheme !~ /^svk/) && ($scheme !~ /^hg/) && ($scheme !~ /^git/)) {
854 # Do not compare if it's not a real cms
855 pb_log(1,"pb_vcs_compliant useless\n");
856 return;
857} elsif ((defined $pbinit) && (! -d "$exportdir")) {
858 # Either we have a version in the uri, and it should be the same
859 # as the one in the envar. Or we should add the version to the uri
860 # But not if it's git as it manages version branches internally
861 if ((basename($uri) ne basename($exportdir)) && ($scheme !~ /git/)) {
862 $uri .= "/".basename($exportdir);
863 }
864
865 pb_mkdir_p("$exportdir");
866 # Should only have pbconf
867 if ((pb_path_nbfiles(dirname($exportdir)) > 1) && ($envar eq 'PBCONFDIR')) {
868 confess("Directory ".dirname($exportdir)." has content.\nPlease remove it if you want to use that directory\n");
869 }
870 if (pb_path_nbfiles("$exportdir") > 0) {
871 confess("Directory $exportdir has content.\nPlease remove it if you want to use that directory\n");
872 }
873 if ($envar eq 'PBCONFDIR') {
874 # Remove the potential pbconf part if we treat pbconfdir
875 $exportdir =~ s|/pbconf[/]*||;
876 $uri =~ s|/pbconf[/]*||;
877 }
878 # Don't add content here as it may conflict later on with PBROOT addition
879 # Done in Env.pm
880 pb_log(0,"Trying to check out (may fail) ".$uri."\n");
881 pb_vcs_checkout($scheme,$uri,$exportdir);
882 if (($envar eq 'PBCONFDIR') && (pb_path_nbfiles("$exportdir/pbconf") == 0)) {
883 # Export failed, because there is nothing yet in the repo
884 # we now need to clean stuff before doing mkdir to avoid conflicts
885 pb_rm_rf($exportdir);
886 pb_log(0,"Pushing first $uri\n");
887 pb_vcs_mkdir($scheme,$uri,"Creating structure directory");
888 pb_vcs_mkdir($scheme,"$uri/pbconf","Creating structure sub directory");
889 pb_log(0,"Re-Trying to check out again $uri\n");
890 pb_vcs_up($scheme,$exportdir);
891 }
892 pb_log(0,"Done\n");
893} else {
894 pb_log(1,"$uri found locally, checking content\n");
895 my $cmsurl = pb_vcs_get_uri($scheme,$ENV{$envar});
896 my ($scheme2, $account2, $host2, $port2, $path2) = pb_get_uri($cmsurl);
897 # For svk, scheme doesn't appear in svk info so remove it here in uri coming from conf file
898 # which needs it to trigger correct behaviour
899 $uri =~ s/^svk://;
900 if ($scheme2 =~ /^git/) {
901 # remove schema from `git+file:` and `git+dir:` urls
902 # TODO: handle query-parameters
903 $uri =~ s/^git\+(file|dir):[\/]*//;
904 # Expand potential env variable in it -- this is required due to the consistency check
905 $uri =~ s/(\$ENV.+\})/$1/eeg;
906 } elsif ($scheme2 =~ /^hg/) {
907 # This VCS manages branches internally not with different tree structures
908 # Assuming it's correct for now.
909 return;
910 }
911 # Remove git+ part if only in scheme
912 $uri =~ s/^git\+// if (($scheme =~ /^git\+/) && ($scheme2 !~ /^git\+/));
913
914 if ($cmsurl ne $uri) {
915 # The local content doesn't correpond to the repository
916 pb_log(0,"ERROR: Inconsistency detected:\n");
917 pb_log(0," * $ENV{$envar} ($envar) refers to $cmsurl but\n");
918 pb_log(0," * $ENV{'PBETC'} refers to $uri\n");
919 die "Project $ENV{'PBPROJ'} is not Project-Builder compliant.";
920 } else {
921 pb_log(1,"Content correct - doing nothing - you may want to update your repository however\n");
922 # they match - do nothing - there may be local changes
923 }
924}
925pb_log(2,"pb_vcs_compliant end\n");
926}
927
928=item B<pb_vcs_conf_update_v0>
929
930This function transform the old configuration v0 file as first param into a new v1 one as second param by calling a COnf.pm function and then adds the resulting file into VCS
931
932=cut
933
934
935sub pb_vcs_conf_update_v0 {
936
937my $orig = shift;
938my $dest = shift;
939
940pb_conf_update_v0($orig,$dest);
941# Adding this new file to VCS (not removing the previous one)
942my ($pbprojurl) = pb_conf_get("pbprojurl");
943my ($scheme, $account, $host, $port, $path) = pb_get_uri($pbprojurl->{$ENV{'PBPROJ'}});
944pb_vcs_add_if_not_in($scheme,$dest);
945}
946
947=back
948
949=head1 WEB SITES
950
951The main Web site of the project is available at L<http://www.project-builder.org/>. Bug reports should be filled using the trac instance of the project at L<http://trac.project-builder.org/>.
952
953=head1 USER MAILING LIST
954
955None exists for the moment.
956
957=head1 AUTHORS
958
959The Project-Builder.org team L<http://trac.project-builder.org/> lead by Bruno Cornec L<mailto:bruno@project-builder.org>.
960
961=head1 COPYRIGHT
962
963Project-Builder.org is distributed under the GPL v2.0 license
964described in the file C<COPYING> included with the distribution.
965
966=cut
967
9681;
Note: See TracBrowser for help on using the repository browser.