The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

SVN::Deploy - audit conform building/deploying releases to/from an SVN deploy repository

SYNOPSIS

use SVN::Deploy;

# creating a SVN::Deploy object
my $obj = SVN::Deploy->new(
    repo        => 'svn:://deploy_srv/deploy_repo',
    cleanup_tmp => 1,
);

# adding a category
$obj->category_add(category => 'Cat1')

# defining a product
my %cfg = (
    build  => [
        '[os]perl build1.pl',
        '[os]perl build2.pl',
    ],
    source => [
        'svn://source_srv/source_repo/trunk/mypath1',
        'svn://source_srv/source_repo/trunk/mypath2',
    ],
    qa => {
        dest => [
            '[none]',
            '/mypath/to/qa/environment',
        ],
        pre  => ['[os]perl pre.pl'],
        post => ['[os]perl post.pl'],
    },
    prod => {
        dest => [
            '[none]',
            '/mypath/to/prod/environment',
        ],
        pre  => ['[os]perl pre.pl'],
        post => ['[os]perl post.pl'],
    },
);

$obj->product_add(
    category => 'Cat1',
    product  => 'Prod1',
    [cfg      => \%cfg,]
);


# exporting data from source repos
# and importing into deploy repo
my $rev = $obj->build_version(
    category => 'Cat1',
    product  => 'Prod1',
    versions => {
        "svn://source_srv/source_repo/trunk/mypath1" => 42,
        "svn://source_srv/source_repo/trunk/mypath2" => 42,
    },
    comment => 'some log message',
);

print "Built version has revision $rev in deploy repo\n";

# deploying the newly created release
# to the specified target
$obj->deploy_version(
    category        => 'Cat1',
    product         => 'Prod1',
    version         => $rev,
    target          => 'qa',
    reference_id    => 'Version 1.02',
    reference_data  => {
        requested_from => 'Bill',
        tested_by      => 'Bob',
        pumpking       => 'Beth',
    },
    comment         => "Lets hope it'll work :-)",
);

DESCRIPTION

SVN::Deploy implements an interface to handle release data held within a separate SVN repository. You can define categorized products where each product consists of multiple sources (SVN repositories and directories or files from a filesystem) and multiple destinations (filesystem directories).

It was designed for situations where the build and deploy steps should not be performed by the developers of a product but by operators with only read access to the developers repository, while the developers have no access to the deploy repository.

The overall outline looks like this:

(dev:developers, op:operating, usr:users/testers):

  - (dev) define a product
          (location of sources from the devel repo and/or files,
          providing build procedures, etc)
  - (op)  define the product in the deploy repository
  - (dev) order a new release (give source revision numbers to op)
  - (op)  build the release
          (resulting in a new revision in the deploy repo)
  - (op)  deploy the new release to QA environment giving
          release revision info to testers
  - (usr) approve/reject the release
  - (op)  on approval deploy the new release to
          production environment

All information and the build/deploy history is held in the deploy repository and can be easily exported for auditing purposes.

The deploy repository will look like this:

repo_root
  \-- Category1
  \-- Category2
    \-- Product1
      \-- 0
        \-- subdir1
          \-- file1
          \-- file2
        \--file1
    \-- Product2
      \-- 0
      \-- 1
      \-- 2
  ...

All product information is saved as properties of the product nodes. So an:

svn proplist -v <repo>/Category2/Product1

will show the product properties. If the latest commit was a result of a deployment task, deployment information will be visible (properties with a leading 'D:').

To get full deployment information you have to retrieve the properties for all revisions of the product.

There are of course history methods provided to automate the process.

Constructor new

my $obj = SVN::Deploy->new(
    repo         => <repo_url>,
    [cleanup_tmp => <0|1>,]
    [debug       => <0|1>,]
    [pwd_sub     => <code_ref>,]
);

'repo', 'cleanup_tmp' and 'debug' should be obvious. 'pwd_sub' can point to a reference to a subroutine returning username and password for the repository. It will only be called when credentials for a user cannot be obtained from the svn cache. A successful logon will be cached.

Returns the created object.

METHODS

All methods will return undef on errors. They will return 1 on success unless another return value is documented. Calling the lasterr() method will return a printable error description.

build_version

$obj->build_version(
    category  => <category_name>,
    product   => <product_name>,
    [versions => {
        [<svn_source> => <svn_version>,]
        [...,]
    },]
    [comment      => <log_message>,]
);

Export the sources defined by a product to a temporary directory, run optional build scripts and import everything as new version for the product in the deploy repository. Each defined source will result in a numbered subdirectory (starting at 0) of the product node.

Build scripts can create additional numbered directories in the temporary directory (e.g. for putting created binaries into). The build script will be run with the temporary directory as working directory.

For sources from SVN repositories (beginning with 'svn://' or 'file://'), providing the revision number is mandatory.

Returns the revision number of the last commit to the deploy repository (every subdirectory is committed separately).

category_add

$obj->category_add(
    category => <category_name>,
);

Trying to add an already existing category will result in an error.

category_delete

$obj->category_delete(
    category => <category_name>,
);

Trying to delete a non existing category or deleting a category with defined products will result in an error.

category_history

$obj->category_history(
    category => <category_name>,
    from     => <revision>,
    to       => <revision>,
    [csv     => <separator>,]
    [build   => <0|1>,]
);

Returns a reference to an array with history data. If the paramter 'csv' evaluates to false the elemets of the array will be hash references looking like this:

{
  'props' => {
    'source' => 'svn://source_srv/source_repo/trunk/mypath1',
    'prod_post' => '[os]perl post.pl',
    'qa_dest' => '/mypath/to/qa/environment',
    'qa_pre' => '[os]perl pre.pl',
    'D:version' => '11',
    'D:target' => 'qa',
    'prod_pre' => '[os]perl pre.pl',
    'D:action' => 'deploy start',
    'prod_dest' => '/mypath/to/prod/environment',
    'build' => '[os]perl build.pl',
    'qa_post' => '[os]perl post.pl',
    'D:reference_id' => '08/15',
    'D:reference_data' => {
      'requested_from' => 'Bill',
      'tested_by'      => 'Bob',
      'pumpking'       => 'Beth',
    },
  },
  'time' => '11:06:33',
  'date' => '2008-05-06',
  'rev' => 12,
  'log' => 'first qa rollout',
  'category' => 'Cat1',
  'product' => 'Product1',
}

When 'csv' is specified the array will contain strings with concatenated data (with the value of 'csv' as concatenator).

The first string will contain concatenated header names.

The 'from' and 'to' parameters will acept all the formats the commandline svn client accepts.

When 'build' is set the build instead of the deploy history will be returned.

category_list

$obj->category_list(
    [category => <category_name>,]
);

Returns a hashref with category names as keys and a reference to an array of products as values. Specifying a category will return information for this category only.

category_update

$obj->category_update(
    category => <category_name>,
    new_name => <new_name>,
);

Rename a category. Defined products will not be touched.

deploy_version

$obj->deploy_version(
    category       => <category_name>,
    product        => <product_name>,
    version        => <revision>,
    target         => 'qa'|'prod',
    [reference_id   => <string data>,]
    [reference_data => <reference to serialize>,]
    [comment        => <log message>,]
);

Deploy a previously build revision of a product to the specified target.

Defined pre and post scripts (see "product_add") are run before respectively after deploy.

The reference parameters exist for storing external references that can later be retrieved by the history functions for auditing purposes. Typicaly this would be information on who did what on whose request.

get_methods

$obj->get_methods();

Returns a reference to a hash with all available method names as keys and a hashref for the parameters as values. The parameter hashes have the parameters as keys and the value will consist of 'm' for mandatory and 'o' for optional parameters.

lasterr

$obj->lasterr();

Returns the text error message for the last encountered error.

output

$obj->output();

Returns the output from external scripts after a call to $obj->build_version() or $obj->deploy_version.

product_add

my %cfg = (
    build  => [
        '[os]perl build1.pl',
        '[os]perl build2.pl',
    ],
    source => [
        'svn://source_srv/source_repo/trunk/mypath1',
        'svn://source_srv/source_repo/trunk/mypath2',
    ],
    qa => {
        dest => [
            '[none]',
            '/mypath/to/qa/environment',
        ],
        pre  => ['[os]perl pre.pl'],
        post => ['[os]perl post.pl'],
    },
    prod => {
        dest => [
            '[none]',
            '/mypath/to/prod/environment',
        ],
        pre  => ['[os]perl pre.pl'],
        post => ['[os]perl post.pl'],
    },
);

$obj->product_add(
    category => <category_name>,
    product  => <product_name>,
    [cfg      => \%cfg,]
);

Add a new product to a category. When specifying a destination, you have to provide a destination for each specified source. '[none]' is a valid destination, meaning the corresponding path of the deploy repository will not be exported when calling $obj->deploy_version. You can have more destinations than sources, e.g. when the build scripts create additional directories.

You can create a product without a configuration, but you have to call $obj->product_update with a valid configuration before calling build or deploy methods.

The 'pre', 'post' and 'build' parameters have to be references to arrays with commands. The commands must be prefixed by '[os]' and will be run with qx// (backticks). This is to be able to add other types of commands in later versions.

product_delete

$obj->product_add(
    category => <category_name>,
    product  => <product_name>,
);

Deletes an existing product.

product_history

$obj->product_history(
    category => <category_name>,
    product  => <product_name>,
    from     => <revision>,
    to       => <revision>,
    [csv     => <separator>,]
    [build   => <0|1>,]
);

See "category_history" for a description. product_history just returns the history for one product.

product_list

$obj->product_list(
    category => <category_name>,
    [product  => <product_name>,]
);

Returns a reference to a hash with product names as keys and a reference to the product's configuration hash as values. The structure is the same as the one specified for the parameter cfg in $obj->product_add or $obj->product_update.

product_update

$obj->product_update(
    category => <category_name>,
    product  => <product_name>,
    [cfg      => \%cfg,]
    [new_name => <product_name>,]
);

Rename an existing Product and/or change its configuration. See $obj->product_add for the description of the configuration hash.

AUTHOR

Thomas Kratz <tomk@cpan.org>

Copyright (c) 2008 Thomas Kratz. All rights reserved.

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.8 or, at your option, any later version of Perl 5 you may have available.