The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!perl

#
# (c) Jan Gehring <jan.gehring@gmail.com>
#
# vim: set ts=3 sw=3 tw=0:
# vim: set expandtab:

use strict;
use warnings;

our $VERSION = '1.3.2'; # VERSION

$|++;

use LWP::UserAgent;
use YAML;
use Data::Dumper;
use Rex::Config;
use Rex::Logger;
use Rex::Commands::Fs;
use Rex::Commands::File;
use JSON::XS;
use Cwd qw(getcwd);
use Carp;
use URI;
use URI::QueryParam;
use File::Spec;
use File::Basename;
use Rex::Helper::Misc;
use Rex::Helper::URI;
use HTTP::Request;
use HTTP::Request::Common;

require Rex;

$Rex::Logger::silent = 1;

my ($major_minor) = ( $Rex::VERSION =~ m/^(\d*\.\d*)/ );
my $opts = {};

######
# default server
my $SEARCH_SERVER = "http://modules.rexify.org/api/$major_minor/get/recipes";
my $RECIPE_SERVER = "http://modules.rexify.org/api/$major_minor/get/mod/%s";
my $DEPEND_SERVER = "http://modules.rexify.org/api/$major_minor/get/dep/%s";
my $PERL_DEPEND_SERVER =
  "http://modules.rexify.org/api/$major_minor/get/perldep/%s";

my $AUTH_USER;
my $AUTH_PASSWORD;
my $AUTH_REALM;

Rex::Config->register_config_handler(
  "module_server",
  sub {
    my ($param) = @_;

    if ( exists $param->{search} ) {
      $SEARCH_SERVER = $param->{search};
    }

    if ( exists $param->{recipes} ) {
      $RECIPE_SERVER = $param->{recipes};
    }

    if ( exists $param->{dependencies} ) {
      $DEPEND_SERVER = $param->{dependencies};
    }

    if ( exists $param->{perl_dependencies} ) {
      $PERL_DEPEND_SERVER = $param->{perl_dependencies};
    }

    if ( exists $param->{username} ) {
      $AUTH_USER = $param->{username};
    }

    if ( exists $param->{password} ) {
      $AUTH_PASSWORD = $param->{password};
    }

    if ( exists $param->{realm} ) {
      $AUTH_REALM = $param->{realm};
    }

  }
);

for ( my $i = 0 ; $i < @ARGV ; $i++ ) {

  if ( $ARGV[$i] =~ m/^\-\-([a-z0-9\-_]+)=/ ) {
    my $key = $1;
    my ( $c_key, $val ) = split( /=/, $ARGV[$i], 2 );

    if ( exists $opts->{$key} ) {
      $opts->{$key} = [ $opts->{$key} ] if ( !ref $opts->{$key} );
      push( @{ $opts->{$key} }, $val );
    }
    else {
      $opts->{$key} = $val || 0;
    }
  }
  elsif ( $ARGV[$i] =~ m/^\-\-([a-z0-9\-_]+)/ ) {
    my $key = $1;
    if ( !$ARGV[ $i + 1 ] || $ARGV[ $i + 1 ] =~ m/^\-\-/ ) {
      $opts->{$key} = 1;
    }
    else {
      if ( exists $opts->{$key} ) {
        $opts->{$key} = [ $opts->{$key} ] if ( !ref $opts->{$key} );

        push( @{ $opts->{$key} }, $ARGV[ ++$i ] );
      }
      else {
        $opts->{$key} = $ARGV[ ++$i ];
      }
    }
  }

}

if ( !$ARGV[0]
  || join( ",", @ARGV ) =~ m/\-h,|\-\-help,/
  || $ARGV[0] eq "--help" )
{
  print STDERR "Usage: rexify [<project-name> [<directory>]] [<options>]\n";
  print STDERR "\n";
  print STDERR "Options:";
  print STDERR "\n";
  print STDERR "\t--search=value\t\t\tWill search community recipes\n";
  print STDERR "\t--use=recipe\t\t\tWill download community recipe\n";
  print STDERR
    "\t--init=git-url\t\t\tWill download and initialize the given repo\n";
  print STDERR "\t--update-from-git\t\tUpdate the cloned repository.\n";
  print STDERR
    "\t--template=template\t\tUse a custom template to create the Rexfile skeleton\n";
  print STDERR "\t--create-module\t\t\tCreate a module skeleton.\n";
  print STDERR
    "\t--create-module=Mod::Name\tCreate a module skeleton inside a Rex project.\n";
  print STDERR "\t--sudo\t\t\t\tTo use sudo for Perl Module installation.\n";
  print STDERR
    "\t--resolve-deps\t\t\tRead meta.yml and try to resolve project dependencies\n";
  print STDERR
    "\t--no-install-perl-deps\t\tUse this if you don't want that rexify tries to install Perl Modules.\n";

  print STDERR "\n";
  print STDERR "Custom Templates:\n";
  print STDERR "   box - Template to use for Rex::Commands::Box projects.\n";

  print STDERR "\n";
  print STDERR "Application Bundle Commands:\n";
  print STDERR "\n";
  print STDERR
    "\t--bundle\tCreate an all-containing redistributable binary application.\n";
  print STDERR
    "\t--task=taskname\tWhich task should be executed. default: setup\n";
  print STDERR "\n";
  print STDERR "Rex-JobControl Commands:\n";
  print STDERR "\n";
  print STDERR "\t--upload\tUpload Rexfile to JobControl server.\n";
  print STDERR
    "\t\t--project=<project>\t\tProject where the Rexfile should be registered.\n";
  print STDERR "\t\t--name=<name>\t\t\tThe name for the Rexfile.\n";
  print STDERR
    "\t\t--description='<description>'\tA small description for the Rexfile.\n";
  print STDERR "\n";
  print STDERR "\t--execute\tExecute Job on JobControl server.\n";
  print STDERR
    "\t\t--project=<project>\t\tProject where the Rexfile should be registered.\n";
  print STDERR "\t\t--job=<job>\t\t\tThe name of the job to execute.\n";
  print STDERR
    "\t\t--hosts='<server list>'\tA comma seperated list of servers.\n";
  print STDERR "\n";

  print STDERR "General options:";
  print STDERR "\n";
  print STDERR "\t--server=<server>\t\tThe URL to JobControl server.\n";
  print STDERR
    "\t--username=<username>\t\tThe username to use for login to JobControl server.\n";
  print STDERR "\t--password=<password>\t\tThe password for the user.\n";
  print STDERR "\n";
  exit 1;
}

sub print_found {
  my ( $name, $data ) = @_;

  $name =~ s/\//::/g;
  $name =~ s/\.pm$//;
  print "* $name\n";
  print "    Author     : " . $data->{Author} . "\n";
  print "    Requires   : " . join( ", ", @{ $data->{Requires} } ) . "\n"
    if ( $data->{Requires} );
  print "    License    : " . $data->{License} . "\n" if ( $data->{License} );
  print "    Description: " . $data->{Description} . "\n";
}

sub update_from_git {
  system "git pull origin";
  resolve_deps("meta.yml");
}

sub download_recipe_git {
  my ($url) = @_;
  $Rex::Logger::silent = 0;

  my $u      = URI->new($url);
  my $branch = $u->query_param("branch");

  my @splitted_path = split /\//, $u->path;
  $splitted_path[-1] =~ s/\.git$//;

  $branch ||= "master";
  my $path = File::Spec->catdir( File::Spec->rel2abs( File::Spec->curdir() ),
    $splitted_path[-1] );

  my $parent_path = dirname $path;
  mkdir $parent_path;

  my $clone_url = $u->scheme . '://' . $u->host . $u->path;

  Rex::Logger::info("Cloning $clone_url to $path. Using branch: $branch.");

  system "git clone $url -b $branch '$path'";

  chdir $path;
  resolve_deps("meta.yml");
}

sub download_recipe_local_tar_gz {
  my ($url) = @_;
  $Rex::Logger::silent = 0;

  system "tar xzf $url";

  my $path = basename($url);
  $path =~ s/\.tar\.gz$//;

  chdir $path;
  resolve_deps("meta.yml");
}

# upload rexfile to rex-jobcontrol (the complete directory)
sub upload_rexfile {
  my (%option) = @_;

  my $tmp_dir = File::Spec->tmpdir;
  my $tmp_file = File::Spec->catfile( $tmp_dir, basename(getcwd) . ".tar.gz" );

  my $login_url = $option{server} . "/login";
  my $upload_url =
      $option{server}
    . "/project/"
    . Rex::Helper::URI::encode( $option{project} )
    . "/rexfile/new";

  Rex::Logger::info("Creating tar.gz file out of this directory.");
  my $dir = basename( getcwd() );
  system "cd .. ; tar czf $tmp_file $dir";

  # upload the file
  my $ua = LWP::UserAgent->new( cookie_jar => {} );

#my $request = HTTP::Request->new(POST 'http://example.com', Content_Type => 'multipart/form-data', Content => [file_0 => ['options2.txt']]);
# first login
  my $res = $ua->post( $login_url,
    { username => $option{username}, password => $option{password} } );

  if ( $res->code != 302 ) {
    print "Server not found or authentication wrong.\n";
    exit 1;
  }

  my $up_request = POST(
    $upload_url,
    Content_Type => 'form-data',
    Content      => [
      rexfile_archive     => [$tmp_file],
      rexfile_name        => $option{name},
      rexfile_description => $option{description}
    ]
  );

  my $up_res = $ua->request($up_request);

  if ( $up_res->code != 302 ) {
    print "Upload of Rexfile failed.\n";
    exit 1;
  }

  unlink $tmp_file;
}

# execute job on Job-Control server
sub dispatch_execute_job {
  my (%option) = @_;

  my $login_url = $option{server} . "/login";
  my $execute_url =
      $option{server}
    . "/project/"
    . Rex::Helper::URI::encode( $option{project} ) . "/job/"
    . Rex::Helper::URI::encode( $option{job} )
    . "/execute";

  # execute a job
  my $ua = LWP::UserAgent->new( cookie_jar => {} );

  # first login
  my $res = $ua->post( $login_url,
    { username => $option{username}, password => $option{password} } );

  if ( $res->code != 302 ) {
    print "Server not found or authentication wrong.\n";
    exit 1;
  }

  # then send the execute command
  my $ex_request = POST(
    $execute_url,
    Content => [
      sel_server => [ split( /[ ,]/, $option{hosts} ) ],
    ]
  );

  my $ex_res = $ua->request($ex_request);

  if ( $ex_res->code != 302 ) {
    print "Execute of job failed.\n";
    exit 1;
  }

}

sub download_recipe {
  my ($name) = @_;

  if ( $name =~ m/^(https|git):\/\// ) {

    # seems to be a git link
    download_recipe_git($name);
    return;
  }

  if ( !-f "Rexfile" ) {
    print STDERR "This is not a Rex project directory. There is no Rexfile.\n";
    exit 1;
  }

  if ( !-d "lib" ) { mkdir "lib"; }

  print "Getting dependencies for $name...\n";
  my $deps = decode_json( get( sprintf( $DEPEND_SERVER, $name ) ) );
  if ( scalar( @{$deps} ) > 0 ) {
    print "   Found: \n      - " . join( "\n      - ", @{$deps} ) . "\n";
    for my $dep ( @{$deps} ) {
      download_recipe($dep);
    }
  }
  else {
    print "   None found.\n";
  }

  if ( !exists $opts->{"no-install-perl-deps"} ) {
    print "Getting perl dependencies for $name...\n";
    my $perl_deps = decode_json( get( sprintf( $PERL_DEPEND_SERVER, $name ) ) );
    if ( scalar( @{$perl_deps} ) > 0 ) {
      print "   Found: \n      - " . join( "\n      - ", @{$perl_deps} ) . "\n";
      for my $dep ( @{$perl_deps} ) {
        install_perl_module($dep);
      }
    }
    else {
      print "   None found.\n";
    }
  }

  print "Downloading $name...   ";
  $name =~ s/::/\//g;
  my $content = get( sprintf( $RECIPE_SERVER, $name ) );
  open( my $fh, ">", "tmp-mod.tar.gz" ) or die($!);
  binmode $fh;
  print $fh $content;
  close($fh);
  chdir("lib");
  system("tar xvzf ../tmp-mod.tar.gz >dl.log 2>&1");
  unlink("dl.log");
  chdir("..");
  unlink("tmp-mod.tar.gz");

  print "done.\n";

}

sub resolve_deps {
  my ($file) = @_;
  $Rex::Logger::silent = 0;

  $file ||= "meta.yml";

  if ( !-f $file ) {
    return;
  }

  my $ref = YAML::LoadFile($file);

  my ( %deps, %perl_deps );

  if ( exists $ref->{Require} ) {
    if ( ref $ref->{Require} eq "ARRAY" ) {
      $deps{$_} = $_ for @{ $ref->{Require} };
    }
    else {
      %deps = %{ $ref->{Require} };
    }
  }

  if ( exists $ref->{PerlRequire} ) {
    if ( ref $ref->{PerlRequire} eq "ARRAY" ) {
      $perl_deps{$_} = $_ for @{ $ref->{PerlRequire} };
    }
    else {
      %perl_deps = %{ $ref->{PerlRequire} };
    }
  }

  Rex::Logger::debug("Found dependencies: ");
  Rex::Logger::debug( Dumper( \%deps ) );

  for my $req ( keys %deps ) {
    if ( ref $deps{$req} ) {
      if ( exists $deps{$req}->{git} ) {

        # git dep
        my $branch = "master";
        if ( exists $deps{$req}->{branch} ) {
          $branch = $deps{$req}->{branch};
        }

        my @path_parts = split /::/, $req;
        my $path =
          File::Spec->catdir( File::Spec->rel2abs( File::Spec->curdir() ),
          "lib", @path_parts );

        my $parent_path = dirname $path;
        if ( !-d $parent_path ) {
          mkdir $parent_path;
        }

        if ( -d $path && !-d "$path/.git" ) {
          Rex::Logger::info( "$req not under git control. Skipping.", "warn" );
          next;
        }

        if ( -d "$path/.git" ) {
          system "rm -rf '$path'";
        }

        Rex::Logger::info("Cloning $deps{$req}->{git}#$branch to $path");
        system "git clone $deps{$req}->{git} -b $branch '$path'";
        resolve_deps("$path/meta.yml");
      }
    }
    else {
      download_recipe($req);
    }
  }

  Rex::Logger::debug("Found perl dependencies: ");
  Rex::Logger::debug( Dumper( \%perl_deps ) );

  for my $req ( keys %perl_deps ) {
    if ( ref $perl_deps{$req} ) {
      if ( exists $perl_deps{$req}->{git} ) {

        # git dep
        my $branch = "master";
        if ( exists $perl_deps{$req}->{branch} ) {
          $branch = $perl_deps{$req}->{branch};
        }

        my $curdir = getcwd;
        my $path = File::Spec->catdir( File::Spec->tmpdir, "tmp-build-$$" );

        my $lib_path =
          File::Spec->catdir( File::Spec->rel2abs( File::Spec->curdir() ),
          "lib", "perl" );

        my $parent_path = dirname $lib_path;
        if ( !-d $parent_path ) {
          mkdir $parent_path;
        }

        Rex::Logger::info("Cloning $perl_deps{$req}->{git}#$branch to $path");
        system "git clone $perl_deps{$req}->{git} -b $branch '$path'";

        chdir $path;
        system "cpanm -l '$lib_path' .";
        chdir $curdir;

        system "rm -rf '$path'";
      }
    }
    else {
      # we need relative directories, because auf a cpanm bug on windows.
      my $lib_path = File::Spec->catdir( File::Spec->curdir(), "lib", "perl" );

      eval "use $req";
      if ($@) {
        system "cpanm -l '$lib_path' $req";
      }
    }
  }

}

sub install_perl_module {
  my ($mod) = @_;

  print "Checking $mod: ";
  eval "use $mod";
  if ($@) {
    print "[failed]\n";
  }
  else {
    print "[ok]\n";
    return;
  }

  print "Trying to install $mod... ";

  my $cmd = "cpanm";
  my $out = qx($cmd --help 2>&1);
  if ( $? != 0 ) {
    $cmd = "cpan";
    $out = qx($cmd -h 2>&1);

    if ( $? != 0 ) {
      print "[failed]\n";
      print "Can't find cpanm or cpan. Please install $mod manually.\n";
      return;
    }

  }

  my $cpanm_opts = "";
  if ( exists $opts->{sudo} ) {
    $cmd = "sudo $cmd";
  }

  $out = qx($cmd $cpanm_opts $mod 2>&1);
  open( my $log, ">>", "rexify-install.log" ) or die($!);
  print $log $out;
  close($log);
  if ( $? != 0 ) {
    print "[failed]\n";
    print
      "!! Please install $mod manually. See rexify-install.log for more details.\n";
  }
  else {
    print "[ok]\n";
  }
}

if ( exists $opts->{bundle} ) {

  # bundle everything to an application

  CORE::mkdir("rex.payload");
  system "cp -vR * rex.payload/ 2>/dev/null";
  system "rm -rf rex.payload/rex.payload";

  my $rex = File::Spec->catfile( dirname($0), "rex" );
  my $tmp_args_file =
    File::Spec->catfile( File::Spec->tmpdir(), "pp.args.$$.tmp" );
  open( my $fh, ">", $tmp_args_file ) or die($!);
  print $fh template('@pp.args.tpl');
  close($fh);

  # build the perl and application
  system "pp \@$tmp_args_file -o rex.payload/rex.bin $rex";

  # find libssh2.so and libexpat.so
  my $arch = qx{uname -m};
  chomp $arch;

  my $elf = "ELF32";
  if ( $arch eq "x86_64" ) {
    $elf = "ELF64";
  }

  CORE::mkdir( File::Spec->catdir( "rex.payload", "lib.bin" ) );
  my @libexpat =
    qx{find /usr/lib /usr/lib32 /usr/lib64 /lib -name 'libexpat.so*' -type f 2>/dev/null};
  my @libssh2 =
    qx{find /usr/lib /usr/lib32 /usr/lib64 /lib -name 'libssh2.so*' -type f 2>/dev/null};
  chomp @libexpat;
  chomp @libssh2;

  for my $f ( @libexpat, @libssh2 ) {
    my $elf_class = qx{readelf -h $f | grep Class:};

    # only copy architecture specific files
    # currently no multi arch support
    if ( $elf_class =~ m/$elf/ ) {
      system "cp -va $f " . File::Spec->catdir( "rex.payload", "lib.bin" );
    }
  }

  system "cd rex.payload/lib.bin ; ln -s "
    . basename( $libssh2[0] )
    . " libssh2.so.1";
  system "cd rex.payload/lib.bin ; ln -s "
    . basename( $libexpat[0] )
    . " libexpat.so.1";

  # create wrapper script.
  open( my $fh2, ">", "rex.sh" ) or die($!);
  print $fh2 template( '@bundle.sh.tpl', task => 'setup' );
  close($fh2);

  system "cd rex.payload ; tar czf ../rex.payload.tar.gz *";
  system
    "cat rex.sh rex.payload.tar.gz >rex.selfextract.sbx ; chmod 755 rex.selfextract.sbx";

  CORE::unlink($tmp_args_file);
  CORE::unlink("rex.sh");
  system "rm -rf rex.payload";
  CORE::unlink("rex.payload.tar.gz");
  exit 0;
}

if ( exists $opts->{upload}
  && exists $opts->{server}
  && exists $opts->{username}
  && exists $opts->{password}
  && exists $opts->{project}
  && exists $opts->{name} )
{
  $opts->{description} ||= "";

  upload_rexfile( %{$opts} );

  exit 0;
}

if ( exists $opts->{execute}
  && exists $opts->{server}
  && exists $opts->{username}
  && exists $opts->{password}
  && exists $opts->{project}
  && exists $opts->{hosts}
  && exists $opts->{job} )
{
  dispatch_execute_job( %{$opts} );

  exit 0;
}

if ( exists $opts->{search} ) {
  my $search_string = $opts->{search};

  # only a search
  print "Downloading recipes.yml ... ";
  my $recipes = get($SEARCH_SERVER);
  print " done.\n";

  print "Searching...\n\n";

  my $data = Load($recipes);

  for my $mod ( keys %{$data} ) {
    if ( $mod =~ qr{$search_string}i ) {
      print_found( $mod, $data->{$mod} );
      next;
    }

    if ( $data->{$mod}->{Description} =~ qr{$search_string}i ) {
      print_found( $mod, $data->{$mod} );
    }
  }

  exit 0;
}
if ( exists $opts->{"update-from-git"} ) {
  update_from_git();
  exit 0;
}

if ( exists $opts->{init} ) {
  if ( -f $opts->{init} ) {
    download_recipe_local_tar_gz( $opts->{init} );
  }
  else {
    download_recipe_git( $opts->{init} );
  }
  exit 0;
}

if ( exists $opts->{use} && $ARGV[0] =~ /^\-\-use/ ) {
  if ( $opts->{use} ) {
    if ( !ref $opts->{use} ) {
      $opts->{use} = [ $opts->{use} ];
    }

    for my $use_mod ( @{ $opts->{use} } ) {
      download_recipe($use_mod);
    }
  }

  exit 0;
}
if ( exists $opts->{"resolve-deps"} && $opts->{"resolve-deps"} ) {
  resolve_deps("meta.yml");
  exit 0;
}

if ( exists $opts->{"create-module"} ) {
  my $dir         = $ARGV[0];
  my $module_name = $dir;

  if ( $dir !~ m/^[a-zA-Z][a-zA-Z_:0-9]+$/ ) {
    print "USAGE: $0 Module::Name --create-module\n";
    print "       Allowed characters: a-z, A-Z, _, 0-9, '::'.\n";
    exit 1;
  }

  if ( -f "Rexfile" && $opts->{"create-module"} ) {
    $dir = "lib/$dir";
  }

  if ( $dir =~ m/\-\-create\-module/ ) {
    print "USAGE: $0 Module::Name --create-module\n";
    print "       Allowed characters: a-z, A-Z, _, 0-9, '::'.\n";
    exit 1;
  }

  $dir =~ s/::/\//g;

  print "Creating module $module_name...\n";

  print "   mkdir $dir\n";
  mkdir $dir, recursive => 1;
  chdir $dir;

  print "   Creating template file: __module__.pm\n";
  file "__module__.pm",
    content =>
    template( '@module.pm.tpl', dir => $dir, module_name => $module_name );

  print "   Creating template file: meta.yml\n";
  file "meta.yml",
    content =>
    template( '@meta.yml.tpl', dir => $dir, module_name => $module_name );

  print "\n";
  print "Your module has been created in $dir.\n";

  exit 0;
}

my $dir = $ARGV[0];

if ( defined $ARGV[1] && $ARGV[1] !~ m/^\-\-/ ) {
  $dir = $ARGV[1];
}

if ( $dir !~ m/^[a-zA-Z][a-zA-Z_:0-9]+$/ ) {
  print "USAGE: $0 Project::Name\n";
  print "       Allowed characters: a-z, A-Z, 0-9, _, '::'.\n";
  exit 1;
}

if ( exists $opts->{template}
  && -f $opts->{template}
  && $opts->{template} !~ m/^\// )
{
  my $cwd = getcwd;
  $opts->{template} = "$cwd/" . $opts->{template};
}
elsif ( exists $opts->{template} && $opts->{template} !~ m/^\// ) {
  $opts->{template} = '@' . $opts->{template};
}

unless ( -d $dir ) {
  print "Created $dir\n";
  mkdir($dir);
}
print "chdir to $dir\n";
chdir($dir);

unless ( -d 'lib' ) {
  mkdir('lib');
}

unless ( -f 'lib' . $ARGV[0] . '.pm' ) {
  open( my $fh, ">", "lib/$ARGV[0].pm" ) or die($!);
  print $fh template( '@libfile', lib => $ARGV[0] );
  close($fh);

  print STDERR "Created lib/Rex/$ARGV[0].pm\n";

  if ( $opts->{template} ) {
    open( $fh, ">", "Rexfile" ) or die($!);
    print $fh template( $opts->{template}, lib => $ARGV[0] );
    close($fh);
  }
  else {
    open( $fh, ">", "Rexfile" ) or die($!);
    print $fh template( '@rexfile', lib => $ARGV[0] );
    close($fh);
  }

  if ( $opts->{use} ) {
    if ( !ref $opts->{use} ) {
      $opts->{use} = [ $opts->{use} ];
    }

    for my $use_mod ( @{ $opts->{use} } ) {
      download_recipe($use_mod);
    }
  }

  print STDERR "Created Rexfile.\n";
  print STDERR "Done.\n\nNow edit Rexfile to suite your needs.\n";
  print STDERR "You can edit $dir/lib/$ARGV[0].pm to define tasks.\n";
  print STDERR
    "\n\nIf you have any questions or wishes\n\n\tjust join #rex on freenode\n\nor post them here:\n\n\thttps://github.com/RexOps/Rex/issues\n\n";
}
else {

  if ( $opts->{use} ) {
    if ( !ref $opts->{use} ) {
      $opts->{use} = [ $opts->{use} ];
    }

    for my $use_mod ( @{ $opts->{use} } ) {
      download_recipe($use_mod);
    }
  }

  exit;
}

sub get {
  my ($url) = @_;

  my $ua = LWP::UserAgent->new;
  $ua->env_proxy;

  if ( $AUTH_USER && $AUTH_PASSWORD ) {
    my ($netloc) = ( $RECIPE_SERVER =~ m/^https?:\/\/([^\/]+)\// );
    unless ( $netloc =~ m/\:\d+$/ ) {
      if ( $netloc =~ m/^https/ ) {
        $netloc .= ":443";
      }
      else {
        $netloc .= ":80";
      }
    }
    $ua->credentials( $netloc, $AUTH_REALM, $AUTH_USER, $AUTH_PASSWORD );
  }

  my $resp = $ua->get($url);
  if ( $resp->is_success ) {
    return $resp->decoded_content;
  }
}

__DATA__

@rexfile
# enable new Features
use Rex -feature => 0.40;

# set your username
set user => "<user>";

# set your password
set password => "<password>";

# enable password authentication
set -passauth;

# put your server in this group
set group => "servers" => "server1", "server2";


# now load every module via ,,require''
require <%= $::lib %>;

@end

@libfile
package <%= $::lib %>;

use Rex -base;

desc "Get uptime of server";
task "uptime", group => 'servers', sub {
   say run "uptime";
};

1;
@end

@box
use strict;
use warnings;

use Rex -feature => 0.40;
use Rex::Commands::Box;

set user => '<user>';
set password => '<password>';
set -passauth;

#
# CALL:
# rex init --name=<%= $::lib %> --url=http://box.rexify.org/box/ubuntu-server-12.10-amd64.ova
desc "Initialize and start the VM: rex init --name=vmname [--url=http://...]";
task "init", sub {

   my $param = shift;

   box {
      my ($box) = @_;
      $box->name($param->{name});

      # where to download the base image
      $box->url($param->{url});

      # default is nat
      #$box->network(1 => {
      #   type => "bridged",
      #   bridge => "eth0",
      #});

      # only works with network type = nat
      # if a key ssh is present, rex will use this to log into the vm
      # you need this if you run VirtualBox not on your local host.
      $box->forward_port(ssh => [2222 => 22]);

      # share a folder from the host system
      #$box->share_folder("sharename" => "/path/on/host/system");

      # define the authentication to the box
      # if you're downloading one from box.rexify.org this is the default.
      $box->auth(
         user => "root",
         password => "box",
      );

      # if you want to provision the machine,
      # you can define the tasks to do that
      $box->setup(qw/install_webserver/);
   };

};

#
# CALL:
# rex down --name=<%= $::lib %>
desc "Stop the VM: rex down --name=vmname";
task "down", sub {

   my $param = shift;

   my $box = Rex::Commands::Box->new(name => $param->{name});
   $box->stop;
};


task "install_webserver", sub {

   # update package db
   update_package_db;

   # install packages / customize the vm
   install "apache2";

};

require <%= $::lib %>;

@end

@module.pm.tpl
package <%= $::module_name %>;

use Rex -base;

task example => sub {
   my $output = run "uptime";
   say $output;
};

1;

=pod

=head1 NAME

$::module_name - {{ SHORT DESCRIPTION }}

=head1 DESCRIPTION

{{ LONG DESCRIPTION }}

=head1 USAGE

{{ USAGE DESCRIPTION }}

 include qw/<%= $::module_name %>/;

 task yourtask => sub {
    <%= $::module_name %>::example();
 };

=head1 TASKS

=over 4

=item example

This is an example Task. This task just output's the uptime of the system.

=back

=cut

@end

@meta.yml.tpl
Name: <%= $::module_name %>
Description: {{ DESCRIPTION }}
Author: {{ your name <your.name@email.com> }}
License: {{ THE LICENSE }}

# Only if you have dependencies to other Rex Modules.
Require:
   - Other::Rex::Module
   - 2nd::Rex::Module
@end


@pp.args.tpl
-M Net::OpenSSH::ShellQuoter::POSIX 
-M Net::SFTP::Foreign::Backend::Unix
-M Sys::Syslog
-M Net::OpenSSH
-M Net::SFTP::Foreign
-M UNIVERSAL
-M Rex::Virtualization
-M Rex::User::Linux
-M Rex::User::FreeBSD
-M Rex::User::SunOS
-M Rex::User::OpenWrt
-M Rex::User::NetBSD
-M Rex::User::OpenBSD
-M Rex::Virtualization::VBox::delete
-M Rex::Virtualization::VBox::list
-M Rex::Virtualization::VBox::info
-M Rex::Virtualization::VBox::shutdown
-M Rex::Virtualization::VBox::reboot
-M Rex::Virtualization::VBox::status
-M Rex::Virtualization::VBox::import
-M Rex::Virtualization::VBox::forward_port
-M Rex::Virtualization::VBox::create
-M Rex::Virtualization::VBox::bridge
-M Rex::Virtualization::VBox::option
-M Rex::Virtualization::VBox::start
-M Rex::Virtualization::VBox::share_folder
-M Rex::Virtualization::VBox::destroy
-M Rex::Virtualization::VBox::guestinfo
-M Rex::Virtualization::VBox
-M Rex::Virtualization::LibVirt
-M Rex::Virtualization::Docker
-M Rex::Virtualization::LibVirt::delete
-M Rex::Virtualization::LibVirt::blklist
-M Rex::Virtualization::LibVirt::list
-M Rex::Virtualization::LibVirt::info
-M Rex::Virtualization::LibVirt::shutdown
-M Rex::Virtualization::LibVirt::reboot
-M Rex::Virtualization::LibVirt::dumpxml
-M Rex::Virtualization::LibVirt::status
-M Rex::Virtualization::LibVirt::import
-M Rex::Virtualization::LibVirt::create
-M Rex::Virtualization::LibVirt::option
-M Rex::Virtualization::LibVirt::start
-M Rex::Virtualization::LibVirt::vncdisplay
-M Rex::Virtualization::LibVirt::clone
-M Rex::Virtualization::LibVirt::iflist
-M Rex::Virtualization::LibVirt::destroy
-M Rex::Virtualization::LibVirt::hypervisor
-M Rex::Virtualization::LibVirt::guestinfo
-M Rex::Virtualization::Base
-M Rex::Virtualization::Docker::delete
-M Rex::Virtualization::Docker::list
-M Rex::Virtualization::Docker::daemon
-M Rex::Virtualization::Docker::info
-M Rex::Virtualization::Docker::shutdown
-M Rex::Virtualization::Docker::reboot
-M Rex::Virtualization::Docker::create
-M Rex::Virtualization::Docker::start
-M Rex::Virtualization::Docker::destroy
-M Rex::Shared::Var::Scalar
-M Rex::Shared::Var::Array
-M Rex::Shared::Var::Hash
-M Rex::Shared::Var
-M Rex::Hardware
-M Rex::Args::Single
-M Rex::Args::Integer
-M Rex::Args::String
-M Rex::Inventory::DMIDecode
-M Rex::Inventory::Proc::Cpuinfo
-M Rex::Inventory::SMBios::SystemInformation
-M Rex::Inventory::SMBios::CPU
-M Rex::Inventory::SMBios::Memory
-M Rex::Inventory::SMBios::BaseBoard
-M Rex::Inventory::SMBios::Section
-M Rex::Inventory::SMBios::MemoryArray
-M Rex::Inventory::SMBios::Bios
-M Rex::Inventory::Hal
-M Rex::Inventory::SMBios
-M Rex::Inventory::Bios
-M Rex::Inventory::HP::ACU
-M Rex::Inventory::Hal::Object::Net
-M Rex::Inventory::Hal::Object::Storage
-M Rex::Inventory::Hal::Object::Volume
-M Rex::Inventory::Hal::Object
-M Rex::Inventory::DMIDecode::SystemInformation
-M Rex::Inventory::DMIDecode::CPU
-M Rex::Inventory::DMIDecode::Memory
-M Rex::Inventory::DMIDecode::BaseBoard
-M Rex::Inventory::DMIDecode::Section
-M Rex::Inventory::DMIDecode::MemoryArray
-M Rex::Inventory::DMIDecode::Bios
-M Rex::Inventory::Proc
-M Rex::File::Parser::Data
-M Rex::File::Parser::Ini
-M Rex::Notify
-M Rex::Logger
-M Rex::Commands
-M Rex::Hook
-M Rex::Exporter
-M Rex::Box::VBox
-M Rex::Box::Amazon
-M Rex::Box::Base
-M Rex::Box::KVM
-M Rex::Output::JUnit
-M Rex::Output::Base
-M Rex::Report::YAML
-M Rex::Report::Base
-M Rex::Inventory
-M Rex::FS::File
-M Rex::Output
-M Rex::Cron
-M Rex::Batch
-M Rex::CMDB
-M Rex::TaskList
-M Rex::Pkg::SuSE
-M Rex::Pkg::Debian
-M Rex::Pkg::Ubuntu
-M Rex::Pkg::Gentoo
-M Rex::Pkg::FreeBSD
-M Rex::Pkg::SunOS
-M Rex::Pkg::OpenWrt
-M Rex::Pkg::Redhat
-M Rex::Pkg::NetBSD
-M Rex::Pkg::Base
-M Rex::Pkg::ALT
-M Rex::Pkg::Mageia
-M Rex::Pkg::OpenBSD
-M Rex::Pkg::SunOS::pkg
-M Rex::Pkg::SunOS::OpenCSW
-M Rex::Template
-M Rex::Profiler
-M Rex::User
-M Rex::Box
-M Rex::Resource::Common
-M Rex::Cloud
-M Rex::SCM::Subversion
-M Rex::SCM::Git
-M Rex::Report
-M Rex::Value
-M Rex::Group
-M Rex::CMDB::YAML
-M Rex::CMDB::Base
-M Rex::Group::Entry::Server
-M Rex::Group::Lookup::Command
-M Rex::Group::Lookup::File
-M Rex::Group::Lookup::DBI
-M Rex::Group::Lookup::YAML
-M Rex::Group::Lookup::XML
-M Rex::Group::Lookup::INI
-M Rex::Task
-M Rex::Resource
-M Rex::Interface::Fs::Sudo
-M Rex::Interface::Fs::HTTP
-M Rex::Interface::Fs::Local
-M Rex::Interface::Fs::Base
-M Rex::Interface::Fs::SSH
-M Rex::Interface::Fs::OpenSSH
-M Rex::Interface::File::Sudo
-M Rex::Interface::File::HTTP
-M Rex::Interface::File::Local
-M Rex::Interface::File::Base
-M Rex::Interface::File::SSH
-M Rex::Interface::File::OpenSSH
-M Rex::Interface::Cache::YAML
-M Rex::Interface::Cache::Base
-M Rex::Interface::Connection::Fake
-M Rex::Interface::Connection::HTTP
-M Rex::Interface::Connection::Local
-M Rex::Interface::Connection::HTTPS
-M Rex::Interface::Connection::Base
-M Rex::Interface::Connection::SSH
-M Rex::Interface::Connection::OpenSSH
-M Rex::Interface::File
-M Rex::Interface::Connection
-M Rex::Interface::Executor
-M Rex::Interface::Shell
-M Rex::Interface::Exec::Sudo
-M Rex::Interface::Exec::HTTP
-M Rex::Interface::Exec::Local
-M Rex::Interface::Exec::Base
-M Rex::Interface::Exec::SSH
-M Rex::Interface::Exec::OpenSSH
-M Rex::Interface::Executor::Default
-M Rex::Interface::Executor::Base
-M Rex::Interface::Exec
-M Rex::Interface::Fs
-M Rex::Interface::Shell::Ksh
-M Rex::Interface::Shell::Zsh
-M Rex::Interface::Shell::Ash
-M Rex::Interface::Shell::Default
-M Rex::Interface::Shell::Sh
-M Rex::Interface::Shell::Bash
-M Rex::Interface::Shell::Tcsh
-M Rex::Interface::Shell::Base
-M Rex::Interface::Shell::Csh
-M Rex::Interface::Cache
-M Rex::Fork::Task
-M Rex::Fork::Manager
-M Rex::CLI
-M Rex::Test::Base
-M Rex::Test::Base::has_content
-M Rex::Test::Base::has_file
-M Rex::Test::Base::has_package
-M Rex::Test::Base::has_service_stopped
-M Rex::Test::Base::has_service_running
-M Rex::Constants
-M Rex::Hardware::Memory
-M Rex::Hardware::Network
-M Rex::Hardware::VirtInfo
-M Rex::Hardware::Swap
-M Rex::Hardware::Kernel
-M Rex::Hardware::Host
-M Rex::Hardware::Network::Linux
-M Rex::Hardware::Network::FreeBSD
-M Rex::Hardware::Network::Solaris
-M Rex::Hardware::Network::NetBSD
-M Rex::Hardware::Network::OpenBSD
-M Rex::Hardware::Network::Darwin
-M Rex::Cloud::Amazon
-M Rex::Cloud::Base
-M Rex::Cloud::Jiffybox
-M Rex::Cloud::OpenStack
-M Rex::Transaction
-M Rex::Commands::LVM
-M Rex::Commands::Virtualization
-M Rex::Commands::Sysctl
-M Rex::Commands::SimpleCheck
-M Rex::Commands::Rsync
-M Rex::Commands::Notify
-M Rex::Commands::Tail
-M Rex::Commands::Inventory
-M Rex::Commands::Cron
-M Rex::Commands::DB
-M Rex::Commands::File
-M Rex::Commands::Gather
-M Rex::Commands::Network
-M Rex::Commands::User
-M Rex::Commands::Box
-M Rex::Commands::Upload
-M Rex::Commands::JobControl
-M Rex::Commands::Cloud
-M Rex::Commands::Run
-M Rex::Commands::Download
-M Rex::Commands::Process
-M Rex::Commands::Sync
-M Rex::Commands::Kernel
-M Rex::Commands::MD5
-M Rex::Commands::Iptables
-M Rex::Commands::Partition
-M Rex::Commands::Service
-M Rex::Commands::Pkg
-M Rex::Commands::Fs
-M Rex::Commands::Host
-M Rex::Commands::SCM
-M Rex::Service
-M Rex::Pkg
-M Rex::Cron::Linux
-M Rex::Cron::SunOS
-M Rex::Cron::Base
-M Rex::Config
-M Rex::Helper::System
-M Rex::Helper::Run
-M Rex::Helper::SSH2
-M Rex::Helper::DBI
-M Rex::Helper::Encode
-M Rex::Helper::Array
-M Rex::Helper::INI
-M Rex::Helper::Misc
-M Rex::Helper::SSH2::Expect
-M Rex::Helper::UserAgent
-M Rex::Helper::Hash
-M Rex::Helper::URI
-M Rex::Helper::Path
-M Rex::TaskList::Parallel_ForkManager
-M Rex::TaskList::Base
-M Rex::Require
-M Rex::Args
-M Rex::Sudo::File
-M Rex::Test
-M Rex::Service::SuSE
-M Rex::Service::Debian
-M Rex::Service::ALT::systemd
-M Rex::Service::Gentoo::systemd
-M Rex::Service::Mageia::systemd
-M Rex::Service::Ubuntu
-M Rex::Service::Gentoo
-M Rex::Service::FreeBSD
-M Rex::Service::SunOS
-M Rex::Service::Redhat::systemd
-M Rex::Service::OpenWrt
-M Rex::Service::Redhat
-M Rex::Service::NetBSD
-M Rex::Service::Base
-M Rex::Service::ALT
-M Rex::Service::Mageia
-M Rex::Service::OpenBSD
-M Rex::Service::SunOS::svcadm
-M Rex::Service::SuSE::systemd
-M Rex
-M Net::SSH2
@end

@bundle.sh.tpl
#!/bin/bash
echo ""
echo "Self Extracting Rex Installer"
echo ""

export TMPDIR=`mktemp -d /tmp/rex.bundle.XXXXXX`

ARCHIVE=`awk '/^__ARCHIVE_BELOW__/ {print NR + 1; exit 0; }' $0`

echo -n "Extracting archive...  "
echo

tail -n+$ARCHIVE $0 | tar xz -C $TMPDIR

if [ "$?" != "0" ]; then
  echo "failed."
  exit 1
fi

echo "done."

CDIR=`pwd`
cd $TMPDIR
export LD_LIBRARY_PATH=`pwd`/lib.bin:$LD_LIBRARY_PATH
./rex.bin -f `pwd`/Rexfile $*

cd $CDIR
rm -rf $TMPDIR

exit 0

__ARCHIVE_BELOW__
@end