File Coverage

blib/lib/ShipIt/VC/SVN.pm
Criterion Covered Total %
statement 21 77 27.2
branch 4 24 16.6
condition 2 8 25.0
subroutine 5 13 38.4
pod 5 8 62.5
total 37 130 28.4


line stmt bran cond sub pod time code
1             package ShipIt::VC::SVN;
2 2     2   14783 use strict;
  2         6  
  2         66  
3 2     2   8 use base 'ShipIt::VC';
  2         4  
  2         868  
4 2     2   12 use File::Temp ();
  2         2  
  2         2171  
5              
6 0     0 0 0 sub command { 'svn' }
7              
8             sub new {
9 0     0 0 0 my ($class, $conf) = @_;
10 0         0 my $self = bless {}, $class;
11 0         0 $self->{tagpattern} = $conf->value( $self->command . ".tagpattern" );
12              
13 0         0 my $command = $self->command;
14 0 0       0 my $url = $self->find_url
15             or die "Failed to run $command, or this isn't an $command working copy";
16 0         0 $self->{url} = $url;
17              
18 0         0 $self->{dir_exists} = {}; # url -> 1 (url exists, learned from svn ls)
19 0         0 $self->{dir_listed} = {}; # url -> 1 (have we do svn ls on url?)
20 0         0 return $self;
21             }
22              
23             sub find_url {
24 0     0 0 0 my $self = shift;
25 0         0 my $info = `svn info`;
26 0         0 ($info =~ /^URL: (.+)/m)[0];
27             }
28              
29             =head1 NAME
30              
31             ShipIt::VC::SVN -- ShipIt's subversion support
32              
33             =head1 CONFIGURATION
34              
35             In your .shipit configuration file, the following options are recognized:
36              
37             =over
38              
39             =item B
40              
41             A pattern which ultimately expands into the absolute subversion URL for a tagged version. If the pattern isn't already absolute, the conventional "tags" directory is used as a base. The pattern has one magic variable, %v, which expands to the version number being tagged. If no %v is found, it's placed at the end.
42              
43             Example legit values:
44              
45             =over 8
46              
47             =item http://example.com/svn/tags/MyProject-%v
48              
49             =item MyProject-%v
50              
51             Both the above are equivalent.
52              
53             =item (nothing)
54              
55             Will automatically add %v to the end (of nothing), then auto-find your
56             'tags' URL, resulting in a final URL of:
57              
58             http://example.com/svn/tags/%v
59              
60             If your svn repo hosts more than one project, this default URL could
61             be bad, as the tagged directory has no project name in it.
62              
63             =back
64              
65             =back
66              
67             =cut
68              
69             sub exists_tagged_version {
70 0     0 1 0 my ($self, $ver) = @_;
71              
72 0         0 my $command = $self->command;
73 0         0 my $url = $self->_tag_url_of_version($ver);
74 0 0       0 die "bogus chars in $command url" if $url =~ /[ \`\&\;]/;
75              
76 0         0 my $tag_base = $url;
77 0 0       0 $tag_base =~ s!/[^/]+$!! or die;
78 0 0       0 unless ($self->{dir_listed}{$tag_base}++) {
79 0         0 foreach my $f (`$command ls $tag_base`) {
80 0         0 chomp $f;
81 0         0 $self->{dir_exists}{"$tag_base/$f"} = 1;
82             }
83             }
84              
85 0   0     0 return $self->{dir_exists}{$url} || $self->{dir_exists}{"$url/"};
86             }
87              
88             # returns tag url of a given version, with no trailing slash
89             sub _tag_url_of_version {
90 4     4   20 my ($self, $ver) = @_;
91 4   100     15 my $url = $self->{tagpattern} || '';
92 4 100       36 unless ($url =~ m!^[\w\+]+://!) {
93 3         6 $url = $self->_tag_base . $url;
94             }
95 4 100       29 $url .= "%v" unless $url =~ /\%v/i;
96 4         13 $url =~ s/\%v/$ver/ig;
97 4         11 $url =~ s!/+$!!;
98 4         14 return $url;
99             }
100              
101             sub _tag_base {
102 4     4   6 my ($self) = @_;
103 4         7 my $url = $self->{url};
104 4         22 $url =~ s!/trunk.*!/tags/!;
105 4         12 return $url;
106             }
107              
108             sub commit {
109 0     0 1   my ($self, $msg) = @_;
110              
111 0           my $command = $self->command;
112              
113             # any locally-added files not in svn?
114 0           my $unk;
115 0           my $changed = 0;
116 0           foreach (`$command st`) {
117 0           $changed++;
118 0 0         next unless /^\?/;
119 0           $unk .= $_;
120             }
121 0 0         if ($unk) {
122 0           die "Unknown local files:\n$unk\n\nUpdate $command:ignore with:\n\t$command pe svn:ignore .\n";
123 0           exit(1);
124             }
125              
126 0 0         unless ($changed) {
127 0           warn "No locally changed files, skipping commit\n";
128 0           return;
129             }
130              
131             # commit
132 0           my $tmp_fh = File::Temp->new(UNLINK => 1, SUFFIX => '.msg');
133 0           print $tmp_fh $msg;
134 0           my $tmp_fn = "$tmp_fh";
135 0 0         system($command, "ci", "--file", $tmp_fn) and die "Commit failed.\n";
136             }
137              
138             sub local_diff {
139 0     0 1   my ($self, $file) = @_;
140 0           my $command = $self->command;
141 0           return `$command diff $file`;
142             }
143              
144             sub tag_version {
145 0     0 1   my ($self, $ver, $msg) = @_;
146 0   0       $msg ||= "Tagging version $ver.\n";
147 0           my $tmp_fh = File::Temp->new(UNLINK => 1, SUFFIX => '.msg');
148 0           print $tmp_fh $msg;
149 0           my $tmp_fn = "$tmp_fh";
150 0           my $tag_url = $self->_tag_url_of_version($ver);
151 0 0         system($self->command, "copy", "--file", $tmp_fn, $self->{url}, $tag_url)
152             and die "Tagging of version '$ver' failed.\n";
153             }
154              
155             sub are_local_diffs {
156 0     0 1   my ($self) = @_;
157 0           my $command = $self->command;
158 0           my $diff = `$command diff`;
159 0 0         return $diff =~ /\S/ ? 1 : 0;
160             }
161              
162             1;
163              
164