Added missing module in remote-at build
[deployable.git] / deployable
blob4823c90bb5afed71a4fe98c4424e45a182fa3618
1 #!/usr/bin/env perl
2 use strict;
3 use warnings;
4 use Carp;
5 use version; our $VERSION = qv('0.1.1');
6 use Fatal qw( close );
7 use Pod::Usage qw( pod2usage );
8 use Getopt::Long qw( :config gnu_getopt );
9 use English qw( -no_match_vars );
10 use File::Basename qw( basename dirname );
11 use File::Spec::Functions qw( file_name_is_absolute catfile );
12 use File::Temp qw( tempfile );
13 use POSIX qw( strftime );
14 use Cwd qw( cwd realpath );
15 use Data::Dumper;
16 use Encode;
18 use File::Find::Rule;
19 use Archive::Tar;
21 my %config = (
22 output => '-',
23 remote => catfile(dirname(realpath(__FILE__)), 'remote'),
24 heredir => [],
25 herefile => [],
26 rootdir => [],
27 root => [],
28 rootfile => [],
29 tarfile => [],
30 deploy => [],
31 xform => [],
32 passthrough => 0,
34 GetOptions(
35 \%config,
36 qw(
37 usage! help! man! version!
39 bundle|all-exec|X!
40 bzip2|bz2|j!
41 cleanup|c!
42 deploy|exec|d=s@
43 gzip|gz|z!
44 heredir|H=s@
45 include-archive-tar|T!
46 no-tar!
47 output|o=s
48 passthrough|P!
49 root|r=s@
50 rootdir|in-root|R=s@
51 tar|t=s
52 tarfile|F=s@
53 tempdir-mode|m=s
54 xform|x=s@
55 workdir|work-directory|deploy-directory|w=s
57 ) or pod2usage(message => "invalid command line", -verbose => 99, -sections => ' ');
58 pod2usage(message => "$0 $VERSION", -verbose => 99, -sections => ' ')
59 if $config{version};
60 pod2usage(-verbose => 99, -sections => 'USAGE') if $config{usage};
61 pod2usage(-verbose => 99, -sections => 'USAGE|EXAMPLES|OPTIONS')
62 if $config{help};
63 pod2usage(-verbose => 2) if $config{man};
65 pod2usage(
66 message => 'working directory must be an absolute path',
67 -verbose => 99,
68 -sections => ''
69 ) if exists $config{workdir} && !file_name_is_absolute($config{workdir});
71 if (@{$config{xform}}) {
72 $config{'no-tar'} = 1; # force internal stuff
73 for my $xform (@{$config{xform}}) {
74 my ($src, $filename) =
75 $xform =~ m{\A((?:[^\\:]|\\.)*) : (.*)}mxs;
76 s{\\(.)}{$1}gmxs for $src, $filename;
77 my $array_name = file_name_is_absolute($filename) ? 'rootfile'
78 : 'herefile';
79 push @{$config{$array_name}}, [$src, $filename];
83 if ($config{'include-archive-tar'}) {
84 $config{remote} = catfile(dirname(realpath(__FILE__)), 'remote-at');
85 if (!-e $config{remote}) { # "make" it
86 print {*STDERR} "### Making remote-at...\n";
87 my $startdir = cwd();
88 chdir dirname realpath __FILE__;
89 system {'make'} qw( make remote-at );
90 chdir $startdir;
91 } ## end if (!-e $config{remote...})
92 } ## end if ($config{'include-archive-tar'...})
94 # Establish output channel
95 my $out_fh = \*STDOUT;
96 if ($config{output} ne '-') {
97 open my $fh, '>', $config{output} ## no critic
98 or croak "open('$config{output}'): $OS_ERROR";
99 $out_fh = $fh;
101 binmode $out_fh;
103 # Emit script code to be executed remotely. It is guaranteed to end
104 # with __END__, so that all what comes next is data
105 print {$out_fh} get_remote_script();
107 # Where all the data will be kept
108 print_configuration($out_fh, \%config);
110 print_here_stuff($out_fh, \%config, @ARGV);
111 print_root_stuff($out_fh, \%config);
113 close $out_fh;
115 # Set as executable
116 if ($config{output} ne '-') {
117 chmod oct(755), $config{output}
118 or carp "chmod(0755, '$config{output}'): $OS_ERROR";
121 sub header {
122 my %params = @_;
123 my $namesize = length $params{name};
124 return "$namesize $params{size}\n$params{name}";
127 sub print_configuration { # FIXME
128 my ($fh, $config) = @_;
129 my %general_configuration;
130 for my $name (
131 qw( workdir cleanup bundle deploy
132 gzip bzip2 passthrough tempdir-mode )
135 $general_configuration{$name} = $config->{$name}
136 if exists $config->{$name};
137 } ## end for my $name (qw( workdir cleanup bundle deploy...))
138 my $configuration = Dumper \%general_configuration;
139 print {$fh} header(name => 'config.pl', size => length($configuration)),
140 "\n", $configuration, "\n\n";
141 } ## end sub print_configuration
143 # Process files and directories. All these will be reported in the
144 # extraction directory, i.e. basename() will be applied to them. For
145 # directories, they will be re-created
146 sub print_here_stuff {
147 my $fh = shift;
148 my $config = shift;
149 my @ARGV = @_;
151 my $ai = Deployable::Tar->new($config);
152 $ai->add(
153 '.' => \@ARGV,
154 '.' => $config->{herefile},
155 map { $_ => ['.'] } @{$config->{heredir}}
158 print {$fh} header(name => 'here', size => $ai->size()), "\n";
159 $ai->copy_to($fh);
160 print {$fh} "\n\n";
162 return;
163 } ## end sub print_here_stuff
165 sub print_root_stuff {
166 my ($fh, $config) = @_;
168 my $ai = Deployable::Tar->new($config);
169 $ai->add(
170 '.' => $config->{rootdir},
171 '.' => $config->{rootfile},
172 (undef, $config->{tarfile}),
173 map { $_ => ['.'] } @{$config->{root}},
176 print {$fh} header(name => 'root', size => $ai->size()), "\n";
177 $ai->copy_to($fh);
178 print {$fh} "\n\n";
180 return;
181 } ## end sub print_root_stuff
183 sub get_remote_script {
184 my $fh;
185 if (-e $config{remote}) {
186 open $fh, '<', $config{remote}
187 or croak "open('$config{remote}'): $OS_ERROR";
189 else {
190 no warnings 'once';
191 $fh = \*DATA;
193 my @lines;
194 while (<$fh>) {
195 last if /\A __END__ \s*\z/mxs;
196 push @lines, $_;
198 close $fh;
199 return join '', @lines, "__END__\n";
200 } ## end sub get_remote_script
202 package Deployable::Tar;
204 sub new {
205 my $package = shift;
206 my $self = {ref $_[0] ? %{$_[0]} : @_};
207 $package = 'Deployable::Tar::Internal';
208 if (!$self->{'no-tar'}) {
209 if ((exists $self->{tar}) || (open my $fh, '-|', 'tar', '--help')) {
210 $package = 'Deployable::Tar::External';
211 $self->{tar} ||= 'tar';
213 } ## end if (!$self->{'no-tar'})
214 bless $self, $package;
215 $self->initialise();
216 return $self;
217 } ## end sub new
219 package Deployable::Tar::External;
220 use File::Temp qw( :seekable );
221 use English qw( -no_match_vars );
222 use Cwd ();
223 use Carp;
224 our @ISA = qw( Deployable::Tar );
226 sub initialise {
227 my $self = shift;
228 $self->{_temp} = File::Temp->new();
229 $self->{_filename} = Cwd::abs_path($self->{_temp}->filename());
230 return $self;
231 } ## end sub initialise
233 sub add {
234 my $self = shift;
235 my $tar = $self->{tar};
236 delete $self->{_compressed};
237 while (@_) {
238 my ($directory, $stuff) = splice @_, 0, 2;
239 my @stuff = @$stuff;
240 if (defined $directory) {
241 while (@stuff) {
242 my @chunk = splice @stuff, 0, 50;
243 system {$tar} $tar, 'rvf', $self->{_filename},
244 '-C', $directory, '--', @chunk;
246 } ## end if (defined $directory)
247 else { # it's another TAR file, concatenate
248 while (@stuff) {
249 my @chunk = splice @stuff, 0, 50;
250 system {$tar} $tar, 'Avf', $self->{_filename}, '--', @chunk;
252 } ## end else [ if (defined $directory)]
253 } ## end while (@_)
254 return $self;
255 } ## end sub add
257 sub _compress {
258 my $self = shift;
259 return if exists $self->{_compressed};
261 $self->{_temp}->sysseek(0, SEEK_SET);
262 if ($self->{bzip2}) {
263 require IO::Compress::Bzip2;
264 $self->{_compressed} = File::Temp->new();
266 # double-quotes needed to force usage of filename
267 # instead of filehandle
268 IO::Compress::Bzip2::bzip2($self->{_temp}, "$self->{_compressed}");
269 } ## end if ($self->{bzip2})
270 elsif ($self->{gzip}) {
271 require IO::Compress::Gzip;
272 $self->{_compressed} = File::Temp->new();
274 # double-quotes needed to force usage of filename
275 # instead of filehandle
276 IO::Compress::Gzip::gzip($self->{_temp}, "$self->{_compressed}");
277 } ## end elsif ($self->{gzip})
278 else {
279 $self->{_compressed} = $self->{_temp};
282 return $self;
283 } ## end sub _compress
285 sub size {
286 my ($self) = @_;
287 $self->_compress();
288 return (stat $self->{_compressed})[7];
291 sub copy_to {
292 my ($self, $out_fh) = @_;
293 $self->_compress();
294 my $in_fh = $self->{_compressed};
295 $in_fh->sysseek(0, SEEK_SET);
296 while ('true') {
297 my $nread = $in_fh->sysread(my $buffer, 4096);
298 croak "sysread(): $OS_ERROR" unless defined $nread;
299 last unless $nread;
300 print {$out_fh} $buffer;
301 } ## end while ('true')
302 return $self;
303 } ## end sub copy_to
305 package Deployable::Tar::Internal;
306 use Archive::Tar ();
307 use Cwd ();
308 use File::Find::Rule ();
309 use Carp qw< croak >;
310 our @ISA = qw( Deployable::Tar );
312 sub initialise {
313 my $self = shift;
314 $self->{_tar} = Archive::Tar->new();
315 return $self;
318 sub add {
319 my $self = shift;
320 delete $self->{_string};
321 my $tar = $self->{_tar};
322 my $cwd = Cwd::getcwd();
323 while (@_) {
324 my ($directory, $stuff) = splice @_, 0, 2;
325 if (defined $directory) {
326 chdir $directory;
327 for my $item (@$stuff) {
328 if (ref $item) {
329 my ($src, $filename) = @$item;
330 my $src_len = length $src;
331 for my $input (File::Find::Rule->in($src)) {
332 my ($atf) = $tar->add_files($input);
333 my $name = $filename . substr $input, $src_len;
334 $atf->rename($name);
337 else {
338 $tar->add_files($_) for File::Find::Rule->in($item);
341 chdir $cwd;
342 } ## end if (defined $directory)
343 else { # It's another TAR file to be concatenated
344 for my $item (@$stuff) {
345 my $iterator = Archive::Tar->iter($item);
346 while (my $f = $iterator->()) {
347 $tar->add_files($f);
351 } ## end while (@_)
352 return $self;
353 } ## end sub add
355 sub size {
356 my ($self) = @_;
357 $self->{_string} = $self->{_tar}->write()
358 unless exists $self->{_string};
359 return length $self->{_string};
360 } ## end sub size
362 sub copy_to {
363 my ($self, $out_fh) = @_;
364 $self->{_string} = $self->{_tar}->write()
365 unless exists $self->{_string};
366 print {$out_fh} $self->{_string};
367 } ## end sub copy_to
369 =head1 NAME
371 deployable - create a deploy script for some files/scripts
373 =head1 VERSION
375 See version at beginning of script, variable $VERSION, or call
377 shell$ deployable --version
379 =head1 USAGE
381 deployable [--usage] [--help] [--man] [--version]
383 deployable [--bundle|--all-exec|-X]
384 [--bzip2|--bz2|-j]
385 [--cleanup|-c]
386 [--deploy|--exec|d <program>]
387 [--gzip|-gz|-z]
388 [--heredir|-H <dirname>]
389 [--include-archive-tar|-T]
390 [--no-tar]
391 [--output|-o <filename>]
392 [--root|-r <dirname>]
393 [--rootdir|--in-root|-R <dirname>]
394 [--tar|-t <program-path>]
395 [--tarfile|-F <filename>]
396 [--tempdir-mode|-m <mode>]
397 [--xform|-x src:filename]
398 [--workdir|-w <path>]
399 [ files or directories... ]
401 =head1 EXAMPLES
403 # pack some files and a deploy script together.
404 shell$ deployable script.sh file.txt some/directory -d script.sh
406 # Use a directory's contents as elements for the target root
407 shell$ ls -1 /path/to/target/root
412 # The above will be deployed as /etc, /opt, /usr and /var
413 shell$ deployable -o dep.pl --root /path/to/target/root
415 # Include sub-directory etc/ for inclusion and extraction
416 # directly as /etc/
417 shell$ deployable -o dep.pl --in-root etc/
419 =head1 DESCRIPTION
421 This is a meta-script to create deploy scripts. The latter ones are
422 suitable to be distributed in order to deploy something.
424 You basically have to provide two things: files to install and programs
425 to be executed. Files can be put directly into the deployed script, or
426 can be included in gzipped tar archives.
428 When called, this script creates a deploy script for you. This script
429 includes all the specified files, and when executed it will extract
430 those files and execute the given programs. In this way, you can ship
431 both files and logic needed to correctly install those files, but this
432 is of course of of scope.
434 All files and archives will be extracted under a configured path
435 (see L<--workdir> below), which we'll call I<workdir> from now on. Under
436 the I<workdir> a temporary directory will be created, and the files
437 will be put in the temporary directory. You can specify if you want to
438 clean up this temporary directory or keep it, at your choice. (You're able
439 to both set a default for this cleanup when invoking deployable, or when
440 invoking the deploy script itself). The temporary directory will be
441 called I<tmpdir> in the following.
443 There are several ways to embed files to be shipped:
445 =over
447 =item *
449 pass the name of an already-prepared tar file via L</--tarfile>. The
450 contents of this file will be assumed to be referred to the root
451 directory;
453 =item *
455 specify the file name directly on the command line. A file given in this
456 way will always be extracted into the I<tmpdir>, whatever its initial path
457 was;
459 =item *
461 specify the name of a directory on the command line. In this case,
462 C<tar> will be used to archive the directory, with the usual option to
463 turn absolute paths into relative ones; this means that directories will
464 be re-created under I<tmpdir> when extraction is performed;
466 =item *
468 give the name of a directory to be used as a "here directory", using
469 the C<--heredir|-H> option. This is much the same as giving the directory
470 name (see above), but in this case C<tar> will be told to change into the
471 directory first, and archive '.'. This means that the contents of the
472 "here-directory" will be extracted directly into I<tmpdir>.
474 =back
476 =head2 Extended Example
478 Suppose you have a few server which have the same configuration, apart
479 from some specific stuff (e.g. the hostname, the IP addresses, etc.).
480 You'd like to perform changes to all with the minimum work possible...
481 so you know you should script something.
483 For example, suppose you want to update a few files in /etc, setting these
484 files equal for all hosts. You would typically do the following:
486 # In your computer
487 shell$ mkdir -p /tmp/newfiles/etc
488 shell$ cd /tmp/newfiles/etc
489 # Craft the new files
490 shell$ cd ..
491 shell$ tar cvzf newetc.tar.gz etc
493 # Now, for each server:
494 shell$ scp newetc.tar.gz $server:/tmp
495 shell$ ssh $server tar xvzf /tmp/newetc.tar.gz -C /
498 So far, so good. But what if you need to kick in a little more logic?
499 For example, if you update some configuration files, you'll most likey
500 want to restart some services. So you could do the following:
502 shell$ mkdir -p /tmp/newfiles/tmp
503 shell$ cd /tmp/newfiles/tmp
504 # craft a shell script to be executed remotely and set the exec bit
505 # Suppose it's called deploy.sh
506 shell$ cd ..
507 shell$ tar cvzf newetc.tar.gz etc tmp
509 # Now, for each server:
510 shell$ scp newetc.tar.gz $server:/tmp
511 shell$ ssh $server tar xvzf /tmp/newetc.tar.gz -C /
512 shell$ ssh $server /tmp/deploy.sh
514 And what if you want to install files depending on the particular machine?
515 Or you have a bundle of stuff to deploy and a bunch of scripts to execute?
516 You can use deployable. In this case, you can do the following:
518 shell$ mkdir -p /tmp/newfiles/etc
519 shell$ cd /tmp/newfiles/etc
520 # Craft the new files
521 shell$ cd ..
522 # craft a shell script to be executed remotely and set the exec bit
523 # Suppose it's called deploy.sh
524 shell$ deployable -o deploy.pl -R etc deploy.sh -d deploy.sh
526 # Now, for each server
527 shell$ scp deploy.pl $server:/tmp
528 shell$ ssh $server /tmp/deploy.pl
530 And you're done. This can be particularly useful if you have another
531 layer of deployment, e.g. if you have to run a script to decide which
532 of a group of archives should be deployed. For example, you could craft
533 a different new "etc" for each server (which is particularly true if
534 network configurations are in the package), and produce a simple script
535 to choose which file to use based on the MAC address of the machine. In
536 this case you could have:
538 =over
540 =item newetc.*.tar.gz
542 a bunch of tar files with the configurations for each different server
544 =item newetc.list
546 a list file with the association between the MAC addresses and the
547 real tar file to deploy from the bunch in the previous bullet
549 =item deploy-the-right-stuff.sh
551 a script to get the real MAC address of the machine, select the right
552 tar file and do the deployment.
554 =back
556 So, you can do the following:
558 shell$ deployable -o deploy.pl newetc.*.tar.gz newetc.list \
559 deploy-the-right-stuff.sh --exec deploy-the-right-stuff.sh
561 # Now, for each server:
562 shell$ scp deploy.pl $server:/tmp
563 shell$ ssh $server /tmp/deploy.pl
565 So, once you have the deploy script on the target machine all you need
566 to do is to execute it. This can come handy when you cannot access the
567 machines from the network, but you have to go there physically: you
568 can prepare all in advance, and just call the deploy script.
571 =head1 OPTIONS
573 Meta-options:
575 =over
577 =item B<--help>
579 print a somewhat more verbose help, showing usage, this description of
580 the options and some examples from the synopsis.
582 =item B<--man>
584 print out the full documentation for the script.
586 =item B<--usage>
588 print a concise usage line and exit.
590 =item B<--version>
592 print the version of the script.
594 =back
596 Real-world options:
598 =over
600 =item B<< --bundle | --all-exec | -X >>
602 Set bundle flag in the produced script. If the bundle flag is set, the
603 I<deploy script> will treat all executables in the main deployment
604 directory as scripts to be executed.
606 By default the flag is not set.
608 =item B<< --bzip2 | --bz2 | -j >>
610 Compress tar archives with bzip2.
612 =item B<< --cleanup | -c >>
614 Set cleanup flag in the produced script. If the cleanup flag is set, the
615 I<deploy script> will clean up after having performed all operations.
617 You can set this flag to C<0> by using C<--no-cleanup>.
619 =item B<< --deploy | --exec | -d <filename> >>
621 Set the name of a program to execute after extraction. You can provide
622 multiple program names, they will be executed in the same order.
624 =item B<< --gzip | --gz | -z >>
626 Compress tar archives with gzip.
628 =item B<< --heredir | -H <path> >>
630 Set the name of a "here directory" (see L<DESCRIPTION>). You can use this
631 option multiple times to provide multiple directories.
633 =item B<< --include-archive-tar | -T >>
635 Embed L<Archive::Tar> (with its dependencies L<Archive::Tar::Constant> and
636 L<Archive::Tar::File>) inside the final script. Use this when you know (or
637 aren't sure) that L<Archive::Tar> will not be available in the target
638 machine.
640 =item B<< --no-tar >>
642 Don't use system C<tar>.
644 =item B<< --output | -o <filename> >>
646 Set the output file name. By default the I<deploy script> will be given
647 out on the standard output; if you provide a filename (different from
648 C<->, of course!) the script will be saved there and the permissions will
649 be set to 0755.
651 =item B<< --root | -r <dirname> >>
653 Include C<dirname> contents for deployment under root directory. The
654 actual production procedure is: hop into C<dirname> and grab a tarball
655 of C<.>. During deployment, hop into C</> and extract the tarball.
657 This is useful if you're already building up the absolute deployment
658 layout under a given directory: just treat that directory as if it were
659 the root of the target system.
661 =item B<< --rootdir | --in-root | -R <filename> >>
663 Include C<filename> as an item that will be extracted under root
664 directory. The actual production procedure is: grab a tarball of
665 C<filename>. During deployment, hop into C</> and extract the tarball.
667 This is useful e.g. if you have a directory (or a group of directories)
668 that you want to deploy directly under the root.
670 Note that the C<--rootdir> alias is kept for backwards compatibility
671 but is not 100% correct - you can specify both a dirname (like it was
672 previously stated) or a single file with this option. This is why it's
673 more readably to use C<--in-root> instead.
675 =item B<< --tar | -t <program-path> >>
677 Set the system C<tar> program to use.
679 =item B<< --tempdir-mode | -m >>
681 set default permissions for temporary directory of deployable script
683 =item B<< --workdir | --deploy-directory | -w <path> >>
685 Set the working directory for the deploy.
687 =back
689 =head1 ROOT OR ROOTDIR?
691 There are two options that allow you to specify things to be deployed
692 in C</>, so what should you use? Thing is... whatever you want!
694 If you have a bunch of directories that have to appear under root, probably
695 your best bet is to put them all inside a directory called C<myroot> and
696 use option C<--root>:
698 shell$ mkdir -p myroot/{etc,opt,var,lib,usr,whatever}
699 # Now put stuff in the directories created above...
700 shell$ deployable --root myroot ...
702 On the other hand, if you just want to put stuff starting from one or
703 two directories that have to show up in C</>, you can avoid creating
704 the extra C<myroot> directory and use C<--in-root> instead:
706 shell$ mkdir -p etc/whatever
707 # Now put stuff in etc/whatever...
708 shell$ deployable --in-root etc ...
710 They are indeed somehow equivalent, the first avoiding you much typing
711 when you have many directories to be deployed starting from root (just
712 put them into the same subdirectory), the second allowing you to avoid
713 putting an extra directory layer.
715 There is indeed an additional catch that makes them quite different. When
716 you use C<root>, the whole content of the directory specified will be
717 used as a base, so you will end up with a listing like this:
719 opt/
720 opt/local/
721 opt/local/application/
722 opt/local/application/myfile.txt
723 opt/local/application/otherfile.txt
725 i.e. all intermediate directories will be saved. On the other hand, when
726 you specify a directory with C<--in-root>, you're not limited to provide
727 a "single-step" directory, so for example:
729 shell$ deployable --in-root opt/local/application
731 will result in the following list of files/directories to be stored:
733 opt/local/application/
734 opt/local/application/myfile.txt
735 opt/local/application/otherfile.txt
737 i.e. the upper level directories will not be included. What is better for
738 you is for you to judge.
740 =head1 THE DEPLOY SCRIPT
742 The net result of calling this script is to produce another script,
743 that we call the I<deploy script>. This script is made of two parts: the
744 code, which is fixed, and the configurations/files, which is what is
745 actually produced. The latter part is put after the C<__END__> marker,
746 as usual.
748 Stuff in the configuration part is always hexified in order to prevent
749 strange tricks or errors. Comments will help you devise what's inside the
750 configurations themselves.
752 The I<deploy script> has options itself, even if they are quite minimal.
753 In particular, it supports the same options C<--workdir|-w> and
754 C<--cleanup> described above, allowing the final user to override the
755 configured values. By default, the I<workdir> is set to C</tmp>
756 and the script will clean up after itself.
758 The following options are supported in the I<deploy script>:
760 =over
762 =item B<--usage | --man | --help>
764 print a minimal help and exit
766 =item B<--version>
768 print script version and exit
770 =item B<--bundle | --all-exec | -X>
772 treat all executables in the main deployment directory as scripts
773 to be executed
775 =item B<--cleanup | --no-cleanup>
777 perform / don't perform temporary directory cleanup after work done
779 =item B<< --deploy | --no-deploy >>
781 deploy scripts are executed by default (same as specifying '--deploy')
782 but you can prevent it.
784 =item B<--dryrun | --dry-run>
786 print final options and exit
788 =item B<< --filelist | --list | -l >>
790 print a list of files that are shipped in the deploy script
792 =item B<< --heretar | --here-tar | -H >>
794 print out the tar file that contains all the files that would be
795 extracted in the temporary directory, useful to redirect to file or
796 pipe to the tar program
798 =item B<< --inspect <dirname> >>
800 just extract all the stuff into <dirname> for inspection. Implies
801 C<--no-deploy>, C<--no-tempdir>, ignores C<--bundle> (as a consequence of
802 C<--no-deploy>), disables C<--cleanup> and sets the working directory
803 to C<dirname>
805 =item B<< --no-tar >>
807 don't use system C<tar>
809 =item B<< --rootar | --root-tar | -R >>
811 print out the tar file that contains all the files that would be
812 extracted in the root directory, useful to redirect to file or
813 pipe to the tar program
815 =item B<--show | --show-options | -s>
817 print configured options and exit
819 =item B<< --tar | -t <program-path> >>
821 set the system C<tar> program to use.
823 =item B<< --tarfile | -F <filename> >>
825 add the specified C<filename> (assumed to be an uncompressed
826 TAR file) to the lot for root extraction. This can come handy
827 when you already have all the files backed up in a TAR archive
828 and you're not willing to expand them (e.g. because your
829 filesystem is case-insensitive...).
831 =item B<< --tempdir | --no-tempdir >>
833 by default a temporary directory is created (same as specifying
834 C<--tempdir>), but you can execute directly in the workdir (see below)
835 without creating it.
837 =item B<< --tempdir-mode | -m >>
839 temporary directories (see C<--tempdir>) created by File::Temp have
840 permission 600 that prevents group/others from even looking at the
841 contents. You might want to invoke some of the internal scripts
842 from another user (e.g. via C<su>), so you can pass a mode to be
843 set on the temporary directory.
845 Works only if C<--tempdir> is active.
847 =item B<< --xform | -x <src:filename> >>
849 include file or directory C<src> as path C<filename>. The latter can be
850 either an absolute path (included in C<root>) or a relative one
851 (included in the working directory).
853 =item B<--workdir | --work-directory | --deploy-directory | -w>
855 working base directory (a temporary subdirectory will be created
856 there anyway)
858 =back
860 Note the difference between C<--show> and C<--dryrun>: the former will
861 give you the options that are "embedded" in the I<deploy script> without
862 taking into account other options given on the command line, while the
863 latter will give you the final options that would be used if the script
864 were called without C<--dryrun>.
866 =head2 Deploy Script Example Usage
868 In the following, we'll assume that the I<deploy script> is called
869 C<deploy.pl>.
871 To execute the script with the already configured options, you just have
872 to call it:
874 shell$ ./deploy.pl
876 If you just want to see which configurations are in the I<deploy script>:
878 shell$ ./deploy.pl --show
880 To see which files are included, you have two options. One is asking the
881 script:
883 shell$ ./deploy.pl --filelist
885 the other is piping to tar:
887 shell$ ./deploy.pl --tar | tar tvf -
889 Extract contents of the script in a temp directory and simply inspect
890 what's inside:
892 # extract stuff into subdirectory 'inspect' for... inspection
893 shell$ ./deploy.pl --no-tempdir --no-deploy --workdir inspect
895 =head2 Deploy Script Requirements
897 You'll need a working Perl with version at least 5.6.2.
899 If you specify L</--include-archive-tar>, the module L<Archive::Tar> will
900 be included as well. This should ease your life and avoid you to have
901 B<tar> on the target machine. On the other hand, if you already know
902 that B<tar> will be available, you can avoid including C<Archive::Tar>
903 and have the generated script use it (it could be rather slower anyway).
905 =head1 DIAGNOSTICS
907 Each error message should be enough explicit to be understood without the
908 need for furter explainations. Which is another way to say that I'm way
909 too lazy to list all possible ways that this script has to fail.
912 =head1 CONFIGURATION AND ENVIRONMENT
914 deployable requires no configuration files or environment variables.
916 Please note that deployable B<needs> to find its master B<remote> file
917 to produce the final script. This must be put in the same directory where
918 deployable is put. You should be able to B<symlink> deployable where you
919 think it's better, anyway - it will go search for the original file
920 and look for B<remote> inside the same directory. This does not apply to
921 hard links, of course.
924 =head1 DEPENDENCIES
926 All core modules, apart the following:
928 =over
930 =item B<< Archive::Tar >>
932 =item B<< File::Find::Rule >>
934 =back
936 =head1 BUGS AND LIMITATIONS
938 No bugs have been reported.
940 Please report any bugs or feature requests to the AUTHOR below.
942 Be sure to read L<CONFIGURATION AND ENVIRONMENT> for a slight limitation
943 about the availability of the B<remote> script.
945 =head1 AUTHOR
947 Flavio Poletti C<flavio [AT] polettix.it>
950 =head1 LICENSE AND COPYRIGHT
952 Copyright (c) 2008, Flavio Poletti C<flavio [AT] polettix.it>. All rights reserved.
954 This script is free software; you can redistribute it and/or
955 modify it under the same terms as Perl itself. See L<perlartistic>
956 and L<perlgpl>.
958 =head1 DISCLAIMER OF WARRANTY
960 BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
961 FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
962 OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
963 PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
964 EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
965 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
966 ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
967 YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
968 NECESSARY SERVICING, REPAIR, OR CORRECTION.
970 IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
971 WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
972 REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
973 LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
974 OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
975 THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
976 RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
977 FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
978 SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
979 SUCH DAMAGES.
981 =cut
983 package main; # ensure DATA is main::DATA
984 __DATA__