hopefully made tar concatenation work with Archive::Tar
[deployable.git] / deployable
blobdf271abe0c5bea8e78dee7ddf2c0a6af5567ffb4
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 Archive::Tar;
16 use File::Find::Rule;
17 use Data::Dumper;
18 use Encode;
20 my %config = (
21 output => '-',
22 remote => catfile(dirname(realpath(__FILE__)), 'remote'),
23 tarfile => [],
24 heredir => [],
25 rootdir => [],
26 root => [],
27 tarfile => [],
28 deploy => [],
29 passthrough => 0,
31 GetOptions(
32 \%config,
33 qw(
34 usage! help! man! version!
36 bundle|all-exec|X!
37 bzip2|bz2|j!
38 cleanup|c!
39 deploy|exec|d=s@
40 gzip|gz|z!
41 heredir|H=s@
42 include-archive-tar|T!
43 no-tar!
44 output|o=s
45 passthrough|P!
46 root|r=s@
47 rootdir|R=s@
48 tar|t=s
49 tarfile|F=s@
50 tempdir-mode|m=s
51 workdir|work-directory|deploy-directory|w=s
54 pod2usage(message => "$0 $VERSION", -verbose => 99, -sections => '')
55 if $config{version};
56 pod2usage(-verbose => 99, -sections => 'USAGE') if $config{usage};
57 pod2usage(-verbose => 99, -sections => 'USAGE|EXAMPLES|OPTIONS')
58 if $config{help};
59 pod2usage(-verbose => 2) if $config{man};
61 pod2usage(
62 message => 'working directory must be an absolute path',
63 -verbose => 99,
64 -sections => ''
65 ) if exists $config{workdir} && !file_name_is_absolute($config{workdir});
67 if ($config{'include-archive-tar'}) {
68 $config{remote} = catfile(dirname(realpath(__FILE__)), 'remote-at');
69 if (!-e $config{remote}) { # "make" it
70 print {*STDERR} "### Making remote-at...\n";
71 my $startdir = cwd();
72 chdir dirname realpath __FILE__;
73 system {'make'} qw( make remote-at );
74 chdir $startdir;
75 } ## end if (!-e $config{remote...})
76 } ## end if ($config{'include-archive-tar'...})
78 # Establish output channel
79 my $out_fh = \*STDOUT;
80 if ($config{output} ne '-') {
81 open my $fh, '>', $config{output} ## no critic
82 or croak "open('$config{output}'): $OS_ERROR";
83 $out_fh = $fh;
85 binmode $out_fh;
87 # Emit script code to be executed remotely. It is guaranteed to end
88 # with __END__, so that all what comes next is data
89 print {$out_fh} get_remote_script();
91 # Where all the data will be kept
92 print_configuration($out_fh, \%config);
94 print_here_stuff($out_fh, \%config, @ARGV);
95 print_root_stuff($out_fh, \%config);
97 close $out_fh;
99 # Set as executable
100 if ($config{output} ne '-') {
101 chmod oct(755), $config{output}
102 or carp "chmod(0755, '$config{output}'): $OS_ERROR";
105 sub header {
106 my %params = @_;
107 my $namesize = length $params{name};
108 return "$namesize $params{size}\n$params{name}";
111 sub print_configuration { # FIXME
112 my ($fh, $config) = @_;
113 my %general_configuration;
114 for my $name (
115 qw( workdir cleanup bundle deploy
116 gzip bzip2 passthrough tempdir-mode )
119 $general_configuration{$name} = $config->{$name}
120 if exists $config->{$name};
121 } ## end for my $name (qw( workdir cleanup bundle deploy...))
122 my $configuration = Dumper \%general_configuration;
123 print {$fh} header(name => 'config.pl', size => length($configuration)),
124 "\n", $configuration, "\n\n";
125 } ## end sub print_configuration
127 # Process files and directories. All these will be reported in the
128 # extraction directory, i.e. basename() will be applied to them. For
129 # directories, they will be re-created
130 sub print_here_stuff {
131 my $fh = shift;
132 my $config = shift;
133 my @ARGV = @_;
135 my $ai = Deployable::Tar->new($config);
136 $ai->add(
137 '.' => \@ARGV,
138 map { $_ => ['.'] } @{$config->{heredir}}
141 print {$fh} header(name => 'here', size => $ai->size()), "\n";
142 $ai->copy_to($fh);
143 print {$fh} "\n\n";
145 return;
146 } ## end sub print_here_stuff
148 sub print_root_stuff {
149 my ($fh, $config) = @_;
151 my $ai = Deployable::Tar->new($config);
152 $ai->add(
153 '.' => $config->{rootdir},
154 (undef, $config->{tarfile}),
155 map { $_ => ['.'] } @{$config->{root}}
158 print {$fh} header(name => 'root', size => $ai->size()), "\n";
159 $ai->copy_to($fh);
160 print {$fh} "\n\n";
162 return;
163 } ## end sub print_root_stuff
165 sub get_remote_script {
166 open my $fh, '<', $config{remote}
167 or croak "open('$config{remote}'): $OS_ERROR";
168 my @lines;
169 while (<$fh>) {
170 last if /\A __END__ \s*\z/mxs;
171 push @lines, $_;
173 close $fh;
174 return join '', @lines, "__END__\n";
175 } ## end sub get_remote_script
177 package Deployable::Tar;
179 sub new {
180 my $package = shift;
181 my $self = {ref $_[0] ? %{$_[0]} : @_};
182 $package = 'Deployable::Tar::Internal';
183 if (!$self->{'no-tar'}) {
184 if ((exists $self->{tar}) || (open my $fh, '-|', 'tar', '--help')) {
185 $package = 'Deployable::Tar::External';
186 $self->{tar} ||= 'tar';
188 } ## end if (!$self->{'no-tar'})
189 bless $self, $package;
190 $self->initialise();
191 return $self;
192 } ## end sub new
194 package Deployable::Tar::External;
195 use File::Temp qw( :seekable );
196 use English qw( -no_match_vars );
197 use Cwd ();
198 use Carp;
199 our @ISA = qw( Deployable::Tar );
201 sub initialise {
202 my $self = shift;
203 $self->{_temp} = File::Temp->new();
204 $self->{_filename} = Cwd::abs_path($self->{_temp}->filename());
205 return $self;
206 } ## end sub initialise
208 sub add {
209 my $self = shift;
210 my $tar = $self->{tar};
211 delete $self->{_compressed};
212 while (@_) {
213 my ($directory, $stuff) = splice @_, 0, 2;
214 my @stuff = @$stuff;
215 if (defined $directory) {
216 while (@stuff) {
217 my @chunk = splice @stuff, 0, 50;
218 system {$tar} $tar, 'rvf', $self->{_filename},
219 '-C', $directory, '--', @chunk;
221 } ## end if (defined $directory)
222 else { # it's another TAR file, concatenate
223 while (@stuff) {
224 my @chunk = splice @stuff, 0, 50;
225 system {$tar} $tar, 'Avf', $self->{_filename}, '--', @chunk;
227 } ## end else [ if (defined $directory)]
228 } ## end while (@_)
229 return $self;
230 } ## end sub add
232 sub _compress {
233 my $self = shift;
234 return if exists $self->{_compressed};
236 $self->{_temp}->sysseek(0, SEEK_SET);
237 if ($self->{bzip2}) {
238 require IO::Compress::Bzip2;
239 $self->{_compressed} = File::Temp->new();
241 # double-quotes needed to force usage of filename
242 # instead of filehandle
243 IO::Compress::Bzip2::bzip2($self->{_temp}, "$self->{_compressed}");
244 } ## end if ($self->{bzip2})
245 elsif ($self->{gzip}) {
246 require IO::Compress::Gzip;
247 $self->{_compressed} = File::Temp->new();
249 # double-quotes needed to force usage of filename
250 # instead of filehandle
251 IO::Compress::Gzip::gzip($self->{_temp}, "$self->{_compressed}");
252 } ## end elsif ($self->{gzip})
253 else {
254 $self->{_compressed} = $self->{_temp};
257 return $self;
258 } ## end sub _compress
260 sub size {
261 my ($self) = @_;
262 $self->_compress();
263 return (stat $self->{_compressed})[7];
266 sub copy_to {
267 my ($self, $out_fh) = @_;
268 $self->_compress();
269 my $in_fh = $self->{_compressed};
270 $in_fh->sysseek(0, SEEK_SET);
271 while ('true') {
272 my $nread = $in_fh->sysread(my $buffer, 4096);
273 croak "sysread(): $OS_ERROR" unless defined $nread;
274 last unless $nread;
275 print {$out_fh} $buffer;
276 } ## end while ('true')
277 return $self;
278 } ## end sub copy_to
280 package Deployable::Tar::Internal;
281 use Archive::Tar ();
282 use Cwd ();
283 use File::Find::Rule ();
284 use Carp qw< croak >;
285 our @ISA = qw( Deployable::Tar );
287 sub initialise {
288 my $self = shift;
289 $self->{_tar} = Archive::Tar->new();
290 return $self;
293 sub add {
294 my $self = shift;
295 delete $self->{_string};
296 my $tar = $self->{_tar};
297 my $cwd = Cwd::getcwd();
298 while (@_) {
299 my ($directory, $stuff) = splice @_, 0, 2;
300 if (defined $directory) {
301 chdir $directory;
302 for my $item (@$stuff) {
303 $tar->add_files($_) for File::Find::Rule->in($item);
305 chdir $cwd;
306 } ## end if (defined $directory)
307 else { # It's another TAR file to be concatenated
308 for my $item (@$stuff) {
309 my $iterator = Archive::Tar->iter($item);
310 while (my $f = $iterator->next()) {
311 $tar->add_files($f);
315 } ## end while (@_)
316 return $self;
317 } ## end sub add
319 sub size {
320 my ($self) = @_;
321 $self->{_string} = $self->{_tar}->write()
322 unless exists $self->{_string};
323 return length $self->{_string};
324 } ## end sub size
326 sub copy_to {
327 my ($self, $out_fh) = @_;
328 $self->{_string} = $self->{_tar}->write()
329 unless exists $self->{_string};
330 print {$out_fh} $self->{_string};
331 } ## end sub copy_to
333 __END__
335 =head1 NAME
337 deployable - create a deploy script for some files/scripts
339 =head1 VERSION
341 See version at beginning of script, variable $VERSION, or call
343 shell$ deployable --version
345 =head1 USAGE
347 deployable [--usage] [--help] [--man] [--version]
349 deployable [--bundle|--all-exec|-X] [--bzip2|--bz2|-j] [--cleanup|-c]
350 [--deploy|--exec|d <program>] [--gzip|-gz|-z]
351 [--heredir|-H <dirname>] [--include-archive-tar|-T]
352 [--no-tar] [--output|-o <filename>] [--root|-r <dirname>]
353 [--rootdir|-R <dirname>] [--tar|-t <program-path>]
354 [--tarfile|-F <filename>] [--tempdir-mode|-m <mode>]
355 [--workdir|-w <path>] [ files or directories... ]
357 =head1 EXAMPLES
359 # pack some files and a deploy script together.
360 shell$ deployable script.sh file.txt some/directory -d script.sh
362 # Use a directory's contents as elements for the target root
363 shell$ ls -1 /path/to/target/root
368 # The above will be deployed as /etc, /opt, /usr and /var
369 shell$ deployable -o dep.pl --root /path/to/target/root
371 # Include directory /path/to/etc for inclusion and extraction
372 # directly as /etc
373 shell$ deployable -o dep.pl --rootdir /path/to/etc
375 =head1 DESCRIPTION
377 This is a meta-script to create deploy scripts. The latter ones are
378 suitable to be distributed in order to deploy something.
380 You basically have to provide two things: files to install and programs
381 to be executed. Files can be put directly into the deployed script, or
382 can be included in gzipped tar archives.
384 When called, this script creates a deploy script for you. This script
385 includes all the specified files, and when executed it will extract
386 those files and execute the given programs. In this way, you can ship
387 both files and logic needed to correctly install those files, but this
388 is of course of of scope.
390 All files and archives will be extracted under a configured path
391 (see L<--workdir> below), which we'll call I<workdir> from now on. Under
392 the I<workdir> a temporary directory will be created, and the files
393 will be put in the temporary directory. You can specify if you want to
394 clean up this temporary directory or keep it, at your choice. (You're able
395 to both set a default for this cleanup when invoking deployable, or when
396 invoking the deploy script itself). The temporary directory will be
397 called I<tmpdir> in the following.
399 There are several ways to embed files to be shipped:
401 =over
403 =item *
405 pass the name of an already-prepared tar file via L</--tarfile>. The
406 contents of this file will be assumed to be referred to the root
407 directory;
409 =item *
411 specify the file name directly on the command line. A file given in this
412 way will always be extracted into the I<tmpdir>, whatever its initial path
413 was;
415 =item *
417 specify the name of a directory on the command line. In this case,
418 C<tar> will be used to archive the directory, with the usual option to
419 turn absolute paths into relative ones; this means that directories will
420 be re-created under I<tmpdir> when extraction is performed;
422 =item *
424 give the name of a directory to be used as a "here directory", using
425 the C<--heredir|-H> option. This is much the same as giving the directory
426 name (see above), but in this case C<tar> will be told to change into the
427 directory first, and archive '.'. This means that the contents of the
428 "here-directory" will be extracted directly into I<tmpdir>.
430 =back
432 =head2 Extended Example
434 Suppose you have a few server which have the same configuration, apart
435 from some specific stuff (e.g. the hostname, the IP addresses, etc.).
436 You'd like to perform changes to all with the minimum work possible...
437 so you know you should script something.
439 For example, suppose you want to update a few files in /etc, setting these
440 files equal for all hosts. You would typically do the following:
442 # In your computer
443 shell$ mkdir -p /tmp/newfiles/etc
444 shell$ cd /tmp/newfiles/etc
445 # Craft the new files
446 shell$ cd ..
447 shell$ tar cvzf newetc.tar.gz etc
449 # Now, for each server:
450 shell$ scp newetc.tar.gz $server:/tmp
451 shell$ ssh $server tar xvzf /tmp/newetc.tar.gz -C /
454 So far, so good. But what if you need to kick in a little more logic?
455 For example, if you update some configuration files, you'll most likey
456 want to restart some services. So you could do the following:
458 shell$ mkdir -p /tmp/newfiles/tmp
459 shell$ cd /tmp/newfiles/tmp
460 # craft a shell script to be executed remotely and set the exec bit
461 # Suppose it's called deploy.sh
462 shell$ cd ..
463 shell$ tar cvzf newetc.tar.gz etc tmp
465 # Now, for each server:
466 shell$ scp newetc.tar.gz $server:/tmp
467 shell$ ssh $server tar xvzf /tmp/newetc.tar.gz -C /
468 shell$ ssh $server /tmp/deploy.sh
470 And what if you want to install files depending on the particular machine?
471 Or you have a bundle of stuff to deploy and a bunch of scripts to execute?
472 You can use deployable. In this case, you can do the following:
474 shell$ mkdir -p /tmp/newfiles/etc
475 shell$ cd /tmp/newfiles/etc
476 # Craft the new files
477 shell$ cd ..
478 # craft a shell script to be executed remotely and set the exec bit
479 # Suppose it's called deploy.sh
480 shell$ deployable -o deploy.pl etc deploy.sh --exec deploy.sh
482 # Now, for each server
483 shell$ scp deploy.pl $server:/tmp
484 shell$ ssh $server /tmp/deploy.pl
486 And you're done. This can be particularly useful if you have another
487 layer of deployment, e.g. if you have to run a script to decide which
488 of a group of archives should be deployed. For example, you could craft
489 a different new "etc" for each server (which is particularly true if
490 network configurations are in the package), and produce a simple script
491 to choose which file to use based on the MAC address of the machine. In
492 this case you could have:
494 =over
496 =item newetc.*.tar.gz
498 a bunch of tar files with the configurations for each different server
500 =item newetc.list
502 a list file with the association between the MAC addresses and the
503 real tar file to deploy from the bunch in the previous bullet
505 =item deploy-the-right-stuff.sh
507 a script to get the real MAC address of the machine, select the right
508 tar file and do the deployment.
510 =back
512 So, you can do the following:
514 shell$ deployable -o deploy.pl newetc.*.tar.gz newetc.list \
515 deploy-the-right-stuff.sh --exec deploy-the-right-stuff.sh
517 # Now, for each server:
518 shell$ scp deploy.pl $server:/tmp
519 shell$ ssh $server /tmp/deploy.pl
521 So, once you have the deploy script on the target machine all you need
522 to do is to execute it. This can come handy when you cannot access the
523 machines from the network, but you have to go there physically: you
524 can prepare all in advance, and just call the deploy script.
527 =head1 OPTIONS
529 Meta-options:
531 =over
533 =item B<--help>
535 print a somewhat more verbose help, showing usage, this description of
536 the options and some examples from the synopsis.
538 =item B<--man>
540 print out the full documentation for the script.
542 =item B<--usage>
544 print a concise usage line and exit.
546 =item B<--version>
548 print the version of the script.
550 =back
552 Real-world options:
554 =over
556 =item B<< --bundle | --all-exec | -X >>
558 Set bundle flag in the produced script. If the bundle flag is set, the
559 I<deploy script> will treat all executables in the main deployment
560 directory as scripts to be executed.
562 By default the flag is not set.
564 =item B<< --bzip2 | --bz2 | -j >>
566 Compress tar archives with bzip2.
568 =item B<< --cleanup | -c >>
570 Set cleanup flag in the produced script. If the cleanup flag is set, the
571 I<deploy script> will clean up after having performed all operations.
573 You can set this flag to C<0> by using C<--no-cleanup>.
575 =item B<< --deploy | --exec | -d <filename> >>
577 Set the name of a program to execute after extraction. You can provide
578 multiple program names, they will be executed in the same order.
580 =item B<< --gzip | --gz | -z >>
582 Compress tar archives with gzip.
584 =item B<< --heredir | -H <path> >>
586 Set the name of a "here directory" (see L<DESCRIPTION>). You can use this
587 option multiple times to provide multiple directories.
589 =item B<< --include-archive-tar | -T >>
591 Embed L<Archive::Tar> (with its dependencies L<Archive::Tar::Constant> and
592 L<Archive::Tar::File>) inside the final script. Use this when you know (or
593 aren't sure) that L<Archive::Tar> will not be available in the target
594 machine.
596 =item B<< --no-tar >>
598 Don't use system C<tar>.
600 =item B<< --output | -o <filename> >>
602 Set the output file name. By default the I<deploy script> will be given
603 out on the standard output; if you provide a filename (different from
604 C<->, of course!) the script will be saved there and the permissions will
605 be set to 0755.
607 =item B<< --root | -r <dirname> >>
609 Include C<dirname> contents for deployment under root directory. The
610 actual production procedure is: hop into C<dirname> and grab a tarball
611 of C<.>. During deployment, hop into C</> and extract the tarball.
613 This is useful if you're already building up the absolute deployment
614 layout under a given directory: just treat that directory as if it were
615 the root of the target system.
617 =item B<< --rootdir | -R <dirname >>
619 Include C<dirname> as a directory that will be extracted under root
620 directory. The actual production procedure is: grab a tarball of
621 C<dirname>. During deployment, hop into C</> and extract the tarball.
623 This is useful if you have a directory (or a group of directories) that
624 you want to deploy directly under the root.
626 =item B<< --tar | -t <program-path> >>
628 Set the system C<tar> program to use.
630 =item B<< --tempdir-mode | -m >>
632 set default permissions for temporary directory of deployable script
634 =item B<< --workdir | --deploy-directory | -w <path> >>
636 Set the working directory for the deploy.
638 =back
640 =head1 THE DEPLOY SCRIPT
642 The net result of calling this script is to produce another script,
643 that we call the I<deploy script>. This script is made of two parts: the
644 code, which is fixed, and the configurations/files, which is what is
645 actually produced. The latter part is put after the C<__END__> marker,
646 as usual.
648 Stuff in the configuration part is always hexified in order to prevent
649 strange tricks or errors. Comments will help you devise what's inside the
650 configurations themselves.
652 The I<deploy script> has options itself, even if they are quite minimal.
653 In particular, it supports the same options C<--workdir|-w> and
654 C<--cleanup> described above, allowing the final user to override the
655 configured values. By default, the I<workdir> is set to C</tmp>
656 and the script will clean up after itself.
658 The following options are supported in the I<deploy script>:
660 =over
662 =item B<--usage | --man | --help>
664 print a minimal help and exit
666 =item B<--version>
668 print script version and exit
670 =item B<--bundle | --all-exec | -X>
672 treat all executables in the main deployment directory as scripts
673 to be executed
675 =item B<--cleanup | --no-cleanup>
677 perform / don't perform temporary directory cleanup after work done
679 =item B<< --deploy | --no-deploy >>
681 deploy scripts are executed by default (same as specifying '--deploy')
682 but you can prevent it.
684 =item B<--dryrun | --dry-run>
686 print final options and exit
688 =item B<< --filelist | --list | -l >>
690 print a list of files that are shipped in the deploy script
692 =item B<< --heretar | --here-tar | -H >>
694 print out the tar file that contains all the files that would be
695 extracted in the temporary directory, useful to redirect to file or
696 pipe to the tar program
698 =item B<< --inspect <dirname> >>
700 just extract all the stuff into <dirname> for inspection. Implies
701 C<--no-deploy>, C<--no-tempdir>, ignores C<--bundle> (as a consequence of
702 C<--no-deploy>), disables C<--cleanup> and sets the working directory
703 to C<dirname>
705 =item B<< --no-tar >>
707 don't use system C<tar>
709 =item B<< --rootar | --root-tar | -R >>
711 print out the tar file that contains all the files that would be
712 extracted in the root directory, useful to redirect to file or
713 pipe to the tar program
715 =item B<--show | --show-options | -s>
717 print configured options and exit
719 =item B<< --tar | -t <program-path> >>
721 set the system C<tar> program to use.
723 =item B<< --tempdir | --no-tempdir >>
725 by default a temporary directory is created (same as specifying
726 C<--tempdir>), but you can execute directly in the workdir (see below)
727 without creating it.
729 =item B<< --tempdir-mode | -m >>
731 temporary directories (see C<--tempdir>) created by File::Temp have
732 permission 600 that prevents group/others from even looking at the
733 contents. You might want to invoke some of the internal scripts
734 from another user (e.g. via C<su>), so you can pass a mode to be
735 set on the temporary directory.
737 Works only if C<--tempdir> is active.
739 =item B<--workdir | --work-directory | --deploy-directory | -w>
741 working base directory (a temporary subdirectory will be created
742 there anyway)
744 =back
746 Note the difference between C<--show> and C<--dryrun>: the former will
747 give you the options that are "embedded" in the I<deploy script> without
748 taking into account other options given on the command line, while the
749 latter will give you the final options that would be used if the script
750 were called without C<--dryrun>.
752 =head2 Deploy Script Example Usage
754 In the following, we'll assume that the I<deploy script> is called
755 C<deploy.pl>.
757 To execute the script with the already configured options, you just have
758 to call it:
760 shell$ ./deploy.pl
762 If you just want to see which configurations are in the I<deploy script>:
764 shell$ ./deploy.pl --show
766 To see which files are included, you have two options. One is asking the
767 script:
769 shell$ ./deploy.pl --filelist
771 the other is piping to tar:
773 shell$ ./deploy.pl --tar | tar tvf -
775 Extract contents of the script in a temp directory and simply inspect
776 what's inside:
778 # extract stuff into subdirectory 'inspect' for... inspection
779 shell$ ./deploy.pl --no-tempdir --no-deploy --workdir inspect
781 =head2 Deploy Script Requirements
783 You'll need a working Perl with version at least 5.6.2.
785 If you specify L</--include-archive-tar>, the module L<Archive::Tar> will
786 be included as well. This should ease your life and avoid you to have
787 B<tar> on the target machine. On the other hand, if you already know
788 that B<tar> will be available, you can avoid including C<Archive::Tar>
789 and have the generated script use it (it could be rather slower anyway).
791 =head1 DIAGNOSTICS
793 Each error message should be enough explicit to be understood without the
794 need for furter explainations. Which is another way to say that I'm way
795 too lazy to list all possible ways that this script has to fail.
798 =head1 CONFIGURATION AND ENVIRONMENT
800 deployable requires no configuration files or environment variables.
802 Please note that deployable B<needs> to find its master B<remote> file
803 to produce the final script. This must be put in the same directory where
804 deployable is put. You should be able to B<symlink> deployable where you
805 think it's better, anyway - it will go search for the original file
806 and look for B<remote> inside the same directory. This does not apply to
807 hard links, of course.
810 =head1 DEPENDENCIES
812 All core modules, apart the following:
814 =over
816 =item B<< Archive::Tar >>
818 =item B<< File::Find::Rule >>
820 =back
822 =head1 BUGS AND LIMITATIONS
824 No bugs have been reported.
826 Please report any bugs or feature requests to the AUTHOR below.
828 Be sure to read L<CONFIGURATION AND ENVIRONMENT> for a slight limitation
829 about the availability of the B<remote> script.
831 =head1 AUTHOR
833 Flavio Poletti C<flavio [AT] polettix.it>
836 =head1 LICENSE AND COPYRIGHT
838 Copyright (c) 2008, Flavio Poletti C<flavio [AT] polettix.it>. All rights reserved.
840 This script is free software; you can redistribute it and/or
841 modify it under the same terms as Perl itself. See L<perlartistic>
842 and L<perlgpl>.
844 Questo script è software libero: potete ridistribuirlo e/o
845 modificarlo negli stessi termini di Perl stesso. Vedete anche
846 L<perlartistic> e L<perlgpl>.
849 =head1 DISCLAIMER OF WARRANTY
851 BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
852 FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
853 OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
854 PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
855 EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
856 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
857 ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
858 YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
859 NECESSARY SERVICING, REPAIR, OR CORRECTION.
861 IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
862 WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
863 REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
864 LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
865 OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
866 THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
867 RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
868 FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
869 SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
870 SUCH DAMAGES.
872 =head1 NEGAZIONE DELLA GARANZIA
874 Poiché questo software viene dato con una licenza gratuita, non
875 c'è alcuna garanzia associata ad esso, ai fini e per quanto permesso
876 dalle leggi applicabili. A meno di quanto possa essere specificato
877 altrove, il proprietario e detentore del copyright fornisce questo
878 software "così com'è" senza garanzia di alcun tipo, sia essa espressa
879 o implicita, includendo fra l'altro (senza però limitarsi a questo)
880 eventuali garanzie implicite di commerciabilità e adeguatezza per
881 uno scopo particolare. L'intero rischio riguardo alla qualità ed
882 alle prestazioni di questo software rimane a voi. Se il software
883 dovesse dimostrarsi difettoso, vi assumete tutte le responsabilità
884 ed i costi per tutti i necessari servizi, riparazioni o correzioni.
886 In nessun caso, a meno che ciò non sia richiesto dalle leggi vigenti
887 o sia regolato da un accordo scritto, alcuno dei detentori del diritto
888 di copyright, o qualunque altra parte che possa modificare, o redistribuire
889 questo software così come consentito dalla licenza di cui sopra, potrà
890 essere considerato responsabile nei vostri confronti per danni, ivi
891 inclusi danni generali, speciali, incidentali o conseguenziali, derivanti
892 dall'utilizzo o dall'incapacità di utilizzo di questo software. Ciò
893 include, a puro titolo di esempio e senza limitarsi ad essi, la perdita
894 di dati, l'alterazione involontaria o indesiderata di dati, le perdite
895 sostenute da voi o da terze parti o un fallimento del software ad
896 operare con un qualsivoglia altro software. Tale negazione di garanzia
897 rimane in essere anche se i dententori del copyright, o qualsiasi altra
898 parte, è stata avvisata della possibilità di tali danneggiamenti.
900 Se decidete di utilizzare questo software, lo fate a vostro rischio
901 e pericolo. Se pensate che i termini di questa negazione di garanzia
902 non si confacciano alle vostre esigenze, o al vostro modo di
903 considerare un software, o ancora al modo in cui avete sempre trattato
904 software di terze parti, non usatelo. Se lo usate, accettate espressamente
905 questa negazione di garanzia e la piena responsabilità per qualsiasi
906 tipo di danno, di qualsiasi natura, possa derivarne.
908 =cut