The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
Doit - an experimental scripting framework
==========================================

Doit is a framework for writing imperative scripts which feel in the
execution like declarative ones.

The commands implemented in this framework follow these priciples:

* A failing command throws an exception and thus stops the script
  (unless caught). This is like a Perl script using "use autodie", but
  in Doit it is implemented consistently for all framework commands.
  "use autodie" knows only about Perl builtins, not about module
  functions

* Before executing a command a check is done whether the command has
  to run at all. This makes it possible to write "converging" scripts:
  a first run would do the changes, subsequent runs would not do at
  all.

* Command execution is logged --- so one actually see what changes are
  done to a system. A command which doesn't have to run won't output
  anything. So "convergent" scripts are typically silent after the
  first run.

There's a dry-run mode built-in, which would just show what commands
would run, similar like make's -n switch or puppet's --noop --test
options.

Status
------

This module is experimental, but already used in productive scripts.
Backward-incompatible changes to the module are possible, but will be
kept to a low number.

Examples
--------

Here's an example using a command from the git component. It just
either clones the given git repository to the given directory, or does
a fetch if the repository is already cloned:

    use Doit;
    my $doit = Doit->init;
    $doit->add_component("git");
    $doit->git_repo_update(
        repository => "git://github.com/eserte/Doit",
        directory  => "$ENV{HOME}/src/Doit",
    );

An excerpt from a real-world system setup script (for setup of
FreeBSD and Linux based smokers for CPAN Testers):

    use Doit;
    my $doit = Doit->init;
    $d->add_component('user');

    my $root = $doit->do_sudo; # for running commands/scripts as root
    $root->write_binary('/etc/sudoers.d/20_cpansand', <<'EOF'); # for auto-installing system depencies with CPAN::Plugin::Sysdeps
    cpansand ALL=(ALL) NOPASSWD: /usr/bin/apt-get install *
    cpansand ALL=(ALL) NOPASSWD: /usr/bin/apt-get -y install *
    EOF
    $root->chmod(0440, '/etc/sudoers.d/20_cpansand');
    chomp(my $zsh = `which zsh`);
    $root->user_account(
        username => 'cpansand',
        uid      => 1234,
        shell    => $zsh,
        ssh_keys => ['ssh-rsa ... '],
    );

    my $cpansand = $d->do_sudo(sudo_opts => [qw(-u cpansand)]); # for running commands as cpansand user
    $cpansand->make_path("/home/cpansand/.cpan/build");
    $cpansand->mkdir("/home/cpansand/.cpanreporter");
    $cpansand->write_binary("/home/cpansand/.cpanreporter/config.ini", <<'EOF');
    edit_report=default:no
    email_from=somebody@example.com
    send_report=default:yes fail:ask/yes
    transport = Metabase uri http://metabase.cpantesters.org/beta/ id_file /home/cpansand/.cpanreporter/somebody_metabase_id.json
    EOF
    ...

Installation
------------

The distribution is installed like a normal CPAN distribution. You
don't have to download it but use CPAN.pm or cpanm:

    cpan Doit

or

    cpanm Doit

An extracted tarball or git clone may be installed like this:

    perl Build.PL
    ./Build
    ./Build test
    ./Build install

(on Windows use just "Build" instead of "./Build")

The Build script itself is a Doit script and not implementing all
features of a Module::Build-based script, but it provides some
interesting actions like

    test_installed
    test_in_docker
    debian_package
    debian_package_with_docker

You don't have to install the distribution at all --- just leave the
extracted or cloned directory where it is and specify in your script

    use lib "/path/to/Doit/lib";
    use Doit;

Or if the extracted is relative to a script, say, in an "inc"
subdirectory:

    use FindBin;
    use lib "$FindBin::RealBin/inc/Doit/lib";
    use Doit;