[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

Hacky script: mirror mod_dav into subversion & out again

From: Blair Zajac <blair_at_orcaware.com>
Date: 2002-11-15 21:14:35 CET

Here's a script I hacked together to fix a client's problem.

A client has a staging and production web server. The client wants to
remotely update the content on the staging server and then push that
content to the production server when the updated content is checked
out and ready. I didn't want to use FTP for security reasons.
Additionally, I wanted to add the ability to track changes to the
content and send out commit emails. Also, I wanted to use WebDAV over
HTTPS to allow the client's Microsoft Windows OS and FrontPage to
modify the content.

Since Subversion isn't a full WebDAV server that operates with
Microsoft, I hacked together this solution.

I set up a DAV directory using Apache 2's mod_dav that the clients
connect to over HTTPS. This directory is the DocumentRoot for an
Apache 1 server (under a different username). Any changes to this
WebDAV directory are shown immediately in the staging server. When
everything looks fine, they use the attached CGI script which calls
svn_load_dirs.pl to mirror the staging directory exactly into the
Subversion repository. The CGI script uses the REMOTE_USER as the
name to perform the 'svn commit' as for tracking purposes. The
post-commit hooks then send out the diff email. After the commit
succeeds, the CGI script then checks out the repository into the
production web server directory.

Let me know if people want this in the svn repository.

While having Subversion support arbitrary WebDAV clients would be
great, this solution is nice because there's only one commit after the
new web site is entirely set up.

When Subversion supports autoversioning or more complicated schemes,
would there be more than one commit? This is something I'd like to
avoid, as multiple commits would make monitoring the commit emails a
lot more work for the people monitoring the site.

Best,
Blair

-- 
Blair Zajac <blair@orcaware.com>
Web and OS performance plots - http://www.orcaware.com/orca/

#!/opt/i386-linux/perl/bin/perl -wT
#
# copy_devel_to_production.cgi
#
# Copyright (C) 2002 Blair Zajac. All rights reserved.
# Author: Blair Zajac <blair@orcaware.com>.
#
# Distributed under the terms of the GNU General Public License,
# version 2, which is available for download at
# http://www.gnu.org/licenses/gpl.html. I, Blair Zajac, am willing to
# consider licensing this script under different terms. If you are
# interested, please feel free to contact me.
#
# This script mirrors the contents of a specified directory into a
# Subversion repository, and then checks out the directory as stored
# in Subversion repository into another directory. The Subversion
# repository can be set up to send out emails showing the changes made
# to the directory when the repository is updated, which allows for
# tracking of changes to the directory structure and file contents.

$| = 1;

use strict;
use CGI 2.89;
use CGI::Carp 1.24 qw(fatalsToBrowser carpout);
use vars qw($query);

# Get a CGI object now and send the HTTP headers out now so that
# anything else printed will appear in the output, include compile
# errors.
BEGIN {
  $query = CGI->new;
  print $query->header('text/plain');
  carpout(\*STDOUT);
}

# Protect the PATH environmental variable for safe system calls.
$ENV{PATH} = '/usr/bin:/bin';

# Configuration settings.

# The svn program.
my $svn = '/opt/i386-linux/subversion/bin/svn';

# The svn_load_dirs.pl script.
my $svn_load_dirs = '/export/home2/svn/bin/svn_load_dirs.pl';

# The source directory.
my $source_dirname = '/export/home2/svn/public_html/www-devel/webdav';

# The target directory.
my $target_dirname = '/export/home1/apache/htdocs/www';

# The URL for the Subversion repository.
my $repos_base_uri = 'file:///export/home2/svn/repos-www/trunk';

# Verbosity level.
my $opt_verbose = 1;

# Use this die instead of the normal die so that messages are sent to
# STDOUT instead of STDERR so that the browser can see them.
sub my_die ($@)
{
  print "@_\n" if @_;
  exit 1;
}

# For permissions information, print my actual and effective UID and
# GID.
if ($opt_verbose)
  {
    my $real_uid = getpwuid($<) || $<;
    my $effective_uid = getpwuid($>) || $>;
    my $real_gid = getgrgid($() || $(;
    my $effective_gid = getgrgid($)) || $);

    print "My real uid is $real_uid and my effective uid is $effective_uid.\n";
    print "My real gid is $real_gid and my effective gid is $effective_gid.\n";
  }

# Determine the authentication username for commit privileges.
# Untaint the REMOTE_USER environmental variable.
my $username;
if (defined $ENV{REMOTE_USER})
  {
    ($username) = $ENV{REMOTE_USER} =~ m/(\w+)/;
    unless (defined $username and length $username)
      {
        my_die "$0: REMOTE_USER set to `$ENV{REMOTE_USER}' but no valid ",
               "string extracted from it.\n";
      }
  }
else
  {
    my_die "$0: the REMOTE_USER environmental variable is not set.\n";
  }

if ($opt_verbose)
  {
    print "I am logged in as `$username'.\n";
  }

# Check the configuration settings.
-e $source_dirname
  or my_die "$0: source directory `$source_dirname' does not exist.\n";
-d _
  or my_die "$0: source directory `$source_dirname' is not a directory.\n";
-e $target_dirname
  or my_die "$0: target directory `$target_dirname' does not exist.\n";
-d _
  or my_die "$0: target directory `$target_dirname' is not a directory.\n";

# Since the path to svnlook depends upon the local installation
# preferences, check that the required program exists to insure that
# the administrator has set up the script properly.
{
  my $ok = 1;
  foreach my $program ($svn, $svn_load_dirs)
    {
      if (-e $program)
        {
          unless (-x $program)
            {
              warn "$0: required program `$program' is not executable, ",
                   "edit $0.\n";
              $ok = 0;
            }
        }
      else
        {
          warn "$0: required program `$program' does not exist, edit $0.\n";
          $ok = 0;
        }
    }
  exit 1 unless $ok;
}

# Check that the svn base URL works by running svn log on it.
&read_from_process($svn, 'log', $repos_base_uri);

# Load the source directory into Subversion.
my_system($svn_load_dirs,
          '-v',
          '-no_user_input',
          '-svn_username', $username,
          '-p', '/opt/i386-linux/installed/svn_load_dirs_property_table.cfg',
          $repos_base_uri,
          '.',
          $source_dirname) == 0
  or my_die "$0: system failed. Quitting.\n";

chdir $target_dirname
  or my_die "$0: chdir `$target_dirname' failed: $!\n";

print "\nNow updating production web site.\n";
my_system($svn, 'update', '.') == 0
  or my_die "$0: system failed. Quitting.\n";

print "\nPRODUCTION WEBSITE UPDATED.\n";

exit 0;

# Start a child process safely without using /bin/sh.
sub safe_read_from_pipe
{
  unless (@_)
    {
      croak "$0: safe_read_from_pipe passed no arguments.\n";
    }

  if ($opt_verbose)
    {
      print "Running @_\n";
    }

  my $pid = open(SAFE_READ, '-|');
  unless (defined $pid)
    {
      my_die "$0: cannot fork: $!\n";
    }
  unless ($pid)
    {
      open(STDERR, ">&STDOUT")
        or my_die "$0: cannot dup STDOUT: $!\n";
      exec(@_)
        or my_die "$0: cannot exec `@_': $!\n";
    }
  my @output;
  while (<SAFE_READ>)
    {
      chomp;
      push(@output, $_);
    }
  close(SAFE_READ);
  my $result = $?;
  my $exit = $result >> 8;
  my $signal = $result & 127;
  my $cd = $result & 128 ? "with core dump" : "";
  if ($signal or $cd)
    {
      warn "$0: pipe from `@_' failed $cd: exit=$exit signal=$signal\n";
    }
  if (wantarray)
    {
      return ($result, @output);
    }
  else
    {
      return $result;
    }
}

# Use safe_read_from_pipe to start a child process safely and exit the
# script if the child failed for whatever reason.
sub read_from_process
  {
  unless (@_)
    {
      croak "$0: read_from_process passed no arguments.\n";
    }
  my ($status, @output) = &safe_read_from_pipe(@_);
  if ($status)
    {
      my_die "$0: @_ failed with this output:\n", join("\n", @output), "\n";
    }
  else
    {
      return @output;
    }
}

# Run system() and print warnings on system's return values.
sub my_system
{
  unless (@_)
    {
      confess "$0: my_system passed incorrect number of arguments.\n";
    }

  if ($opt_verbose)
    {
      print "Running @_\n";
    }

  my $result = system(@_);
  if ($result == -1)
    {
      warn "$0: system(@_) call itself failed: $!\n";
    }
  elsif ($result)
    {
      my $exit_value = $? >> 8;
      my $signal_num = $? & 127;
      my $dumped_core = $? & 128;

      my $message = "$0: system(@_) exited with status $exit_value";
      if ($signal_num)
        {
          $message .= " caught signal $signal_num";
        }
      if ($dumped_core)
        {
          $message .= " and dumped core";
        }
      warn "$message\n";
    }

  $result;
}

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Fri Nov 15 21:15:07 2002

This is an archived mail posted to the Subversion Dev mailing list.

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.