ShipIt::VC::SVN - ShipIt's subversion support


ShipIt documentation Contained in the ShipIt distribution.

Index


Code Index:

NAME

Top

ShipIt::VC::SVN -- ShipIt's subversion support

CONFIGURATION

Top

In your .shipit configuration file, the following options are recognized:

svn.tagpattern

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.

Example legit values:

http://example.com/svn/tags/MyProject-%v
MyProject-%v

Both the above are equivalent.

(nothing)

Will automatically add %v to the end (of nothing), then auto-find your 'tags' URL, resulting in a final URL of:

 http://example.com/svn/tags/%v

If your svn repo hosts more than one project, this default URL could be bad, as the tagged directory has no project name in it.


ShipIt documentation Contained in the ShipIt distribution.

package ShipIt::VC::SVN;
use strict;
use base 'ShipIt::VC';
use File::Temp ();

sub command { 'svn' }

sub new {
    my ($class, $conf) = @_;
    my $self = bless {}, $class;
    $self->{tagpattern} = $conf->value( $self->command . ".tagpattern" );

    my $command = $self->command;
    my $url = $self->find_url
        or die "Failed to run $command, or this isn't an $command working copy";
    $self->{url} = $url;

    $self->{dir_exists} = {}; # url -> 1 (url exists, learned from svn ls)
    $self->{dir_listed} = {}; # url -> 1 (have we do svn ls on url?)
    return $self;
}

sub find_url {
    my $self = shift;
    my $info = `svn info`;
    ($info =~ /^URL: (.+)/m)[0];
}

sub exists_tagged_version {
    my ($self, $ver) = @_;

    my $command = $self->command;
    my $url = $self->_tag_url_of_version($ver);
    die "bogus chars in $command url" if $url =~ /[ \`\&\;]/;

    my $tag_base = $url;
    $tag_base =~ s!/[^/]+$!! or die;
    unless ($self->{dir_listed}{$tag_base}++) {
        foreach my $f (`$command ls $tag_base`) {
            chomp $f;
            $self->{dir_exists}{"$tag_base/$f"} = 1;
        }
    }

    return $self->{dir_exists}{$url} || $self->{dir_exists}{"$url/"};
}

# returns tag url of a given version, with no trailing slash
sub _tag_url_of_version {
    my ($self, $ver) = @_;
    my $url = $self->{tagpattern} || '';
    unless ($url =~ m!^[\w\+]+://!) {
        $url = $self->_tag_base . $url;
    }
    $url .= "%v" unless $url =~ /\%v/i;
    $url =~ s/\%v/$ver/ig;
    $url =~ s!/+$!!;
    return $url;
}

sub _tag_base {
    my ($self) = @_;
    my $url = $self->{url};
    $url =~ s!/trunk.*!/tags/!;
    return $url;
}

sub commit {
    my ($self, $msg) = @_;

    my $command = $self->command;

    # any locally-added files not in svn?
    my $unk;
    my $changed = 0;
    foreach (`$command st`) {
        $changed++;
        next unless /^\?/;
        $unk .= $_;
    }
    if ($unk) {
        die "Unknown local files:\n$unk\n\nUpdate $command:ignore with:\n\t$command pe svn:ignore .\n";
        exit(1);
    }

    unless ($changed) {
        warn "No locally changed files, skipping commit\n";
        return;
    }

    # commit
    my $tmp_fh = File::Temp->new(UNLINK => 1, SUFFIX => '.msg');
    print $tmp_fh $msg;
    my $tmp_fn = "$tmp_fh";
    system($command, "ci", "--file", $tmp_fn) and die "Commit failed.\n";
}

sub local_diff {
    my ($self, $file) = @_;
    my $command = $self->command;
    return `$command diff $file`;
}

sub tag_version {
    my ($self, $ver, $msg) = @_;
    $msg ||= "Tagging version $ver.\n";
    my $tmp_fh = File::Temp->new(UNLINK => 1, SUFFIX => '.msg');
    print $tmp_fh $msg;
    my $tmp_fn = "$tmp_fh";
    my $tag_url = $self->_tag_url_of_version($ver);
    system($self->command, "copy", "--file", $tmp_fn, $self->{url}, $tag_url)
        and die "Tagging of version '$ver' failed.\n";
}

sub are_local_diffs {
    my ($self) = @_;
    my $command = $self->command;
    my $diff = `$command diff`;
    return $diff =~ /\S/ ? 1 : 0;
}

1;