2 # *** NOTE *** LEAVE THIS MODULE LIST AS A PARAGRAPH
6 our $VERSION = '0.2.0';
7 use English
qw( -no_match_vars );
8 use Fatal
qw( close chdir opendir closedir );
9 use File
::Temp
qw( tempdir );
10 use File
::Path
qw( mkpath );
11 use File
::Spec
::Functions
qw( file_name_is_absolute catfile );
12 use File
::Basename
qw( basename dirname );
13 use POSIX
qw( strftime );
14 use Getopt
::Long
qw( :config gnu_getopt );
16 use Fcntl
qw( :seek );
18 # *** NOTE *** LEAVE EMPTY LINE ABOVE
19 my %default_config = ( # default values
28 my $DATA_POSITION = tell DATA
; # GLOBAL VARIABLE
29 my %script_config = (%default_config, get_config
());
31 my %config = %script_config;
32 if ($ENV{DEPLOYABLE_DISABLE_PASSTHROUGH
} || (!$config{passthrough
})) {
54 workdir|work-directory|deploy-directory|w=s
57 %config = (%config, %cmdline_config);
58 } ## end if ($ENV{DEPLOYABLE_DISABLE_PASSTHROUGH...})
60 usage
() if $config{usage
};
61 version
() if $config{version
};
63 if ($config{roottar
}) {
65 my ($fh, $size) = locate_file
('root');
66 copy
($fh, \
*STDOUT
, $size);
68 } ## end if ($config{roottar})
70 if ($config{heretar
}) {
72 my ($fh, $size) = locate_file
('here');
73 copy
($fh, \
*STDOUT
, $size);
75 } ## end if ($config{heretar})
79 print {*STDOUT
} Data
::Dumper
::Dumper
(\
%script_config);
83 if ($config{inspect
}) {
85 $config{'no-exec'} = 1;
86 $config{'tempdir'} = 0;
87 $config{workdir
} = $config{inspect
};
88 } ## end if ($config{inspect})
90 if ($config{dryrun
}) {
92 print {*STDOUT
} Data
::Dumper
::Dumper
(\
%config);
96 if ($config{filelist
}) {
97 my $root_tar = get_sub_tar
('root');
99 $root_tar->print_filelist();
100 my $here_tar = get_sub_tar
('here');
102 $here_tar->print_filelist();
104 } ## end if ($config{filelist})
106 # here we have to do things for real... probably, so save the current
107 # working directory for consumption by the scripts
108 $ENV{OLD_PWD
} = getcwd
();
110 # go into the working directory, creating any intermediate if needed
111 mkpath
($config{workdir
});
112 chdir($config{workdir
});
113 print {*STDERR
} "### Got into working directory '$config{workdir}'\n\n"
117 if ($config{'tempdir'}) { # Only if allowed
118 my $me = basename
(__FILE__
) || 'deploy';
119 my $now = strftime
('%Y-%m-%d_%H-%M-%S', localtime);
121 join('-', $me, $now, ('X' x
10)),
123 CLEANUP
=> $config{cleanup
}
126 if ($config{'tempdir-mode'}) {
127 chmod oct($config{'tempdir-mode'}), $tempdir
128 or die "chmod('$tempdir'): $OS_ERROR\n";
132 or die "chdir('$tempdir'): $OS_ERROR\n";
134 if ($config{verbose
}) {
136 "### Created and got into temporary directory '$tempdir'\n";
137 print {*STDERR
} "### (will clean it up later)\n" if $config{cleanup
};
138 print {*STDERR
} "\n";
139 } ## end if ($config{verbose})
140 } ## end if ($config{'tempdir'})
142 eval { # Not really needed, but you know...
143 $ENV{PATH
} = '/bin:/usr/bin:/sbin:/usr/sbin';
145 execute_deploy_programs
() unless $config{'no-exec'};
147 warn "$EVAL_ERROR\n" if $EVAL_ERROR;
149 # Get back so that cleanup can successfully happen, if requested
150 chdir '..' if defined $tempdir;
155 seek $fh, $DATA_POSITION, SEEK_SET
;
157 chomp(my $sizes = <$fh>);
158 my ($name_size, $file_size) = split /\s+/, $sizes;
159 my $name = full_read
($fh, $name_size);
160 full_read
($fh, 1); # "\n"
161 return ($fh, $file_size) if $name eq $filename;
162 seek $fh, $file_size + 2, SEEK_CUR
; # includes "\n\n"
163 } ## end while (!eof $fh)
164 die "could not find '$filename'";
165 } ## end sub locate_file
168 my ($fh, $size) = @_;
172 my $nread = read $fh, $buffer, $size;
173 die "read(): $OS_ERROR" unless defined $nread;
174 die "unexpected end of file" unless $nread;
177 } ## end while ($size)
179 } ## end sub full_read
182 my ($ifh, $ofh, $size) = @_;
185 my $nread = read $ifh, $buffer, ($size < 4096 ?
$size : 4096);
186 die "read(): $OS_ERROR" unless defined $nread;
187 die "unexpected end of file" unless $nread;
188 print {$ofh} $buffer;
190 } ## end while ($size)
196 my ($fh, $size) = locate_file
($filename);
197 return Deployable
::Tar
->new(%config, fh
=> $fh, size
=> $size);
201 my ($fh, $size) = locate_file
('config.pl');
202 my $config_text = full_read
($fh, $size);
203 my $config = eval 'my ' . $config_text or return;
204 return $config unless wantarray;
206 } ## end sub get_config
209 my $here_tar = get_sub_tar
('here');
210 $here_tar->extract();
212 my $root_dir = $config{inspect
} ?
'root' : '/';
213 mkpath
$root_dir unless -d
$root_dir;
216 my $root_tar = get_sub_tar
('root');
217 $root_tar->extract();
221 } ## end sub save_files
223 sub execute_deploy_programs
{
224 my @deploy_programs = @
{$config{deploy
} || []};
226 if ($config{bundle
}) { # add all executable scripts in current directory
227 print {*STDERR
} "### Auto-deploying all executables in main dir\n\n"
229 my %flag_for = map { $_ => 1 } @deploy_programs;
231 for my $item (sort readdir $dh) {
232 next if $flag_for{$item};
233 next unless ((-f
$item) || (-l
$item)) && (-x
$item);
234 $flag_for{$item} = 1;
235 push @deploy_programs, $item;
236 } ## end for my $item (sort readdir...)
238 } ## end if ($config{bundle})
241 for my $deploy (@deploy_programs) {
242 $deploy = catfile
('.', $deploy)
243 unless file_name_is_absolute
($deploy);
245 print {*STDERR
} "### Skipping '$deploy', not executable\n\n"
249 print {*STDERR
} "### Executing '$deploy'...\n"
251 system {$deploy} $deploy, @ARGV;
254 } ## end DEPLOY: for my $deploy (@deploy_programs)
257 } ## end sub execute_deploy_programs
260 my $progname = basename
($0);
261 print {*STDOUT
} <<"END_OF_USAGE" ;
263 $progname version $VERSION - for help on calling and options, run:
268 } ## end sub short_usage
271 my $progname = basename($0);
272 print {*STDOUT} <<"END_OF_USAGE
" ;
273 $progname version $VERSION
275 More or less, this script is intended to be launched without parameters.
276 Anyway, you can also set the following options, which will override any
277 present configuration (except in "--show
-options
"):
279 * --usage | --man | --help
280 print these help lines and exit
283 print script version and exit
285 * --bundle | --all-exec | -X
286 treat all executables in the main deployment directory as scripts
289 * --cleanup | -c | --no-cleanup
290 perform / don't perform temporary directory cleanup after work done
292 * --deploy | --no-deploy
293 deploy scripts are executed by default (same as specifying '--deploy')
294 but you can prevent it.
296 * --dryrun | --dry-run
297 print final options and exit
299 * --filelist | --list | -l
300 print a list of files that are shipped in the deploy script
302 * --heretar | --here-tar | -H
303 print out the tar file that contains all the files that would be
304 extracted in the temporary directory, useful to redirect to file or
305 pipe to the tar program
307 * --inspect | -i <dirname>
308 just extract all the stuff into <dirname> for inspection. Implies
309 --no-deploy, --no-tempdir, ignores --bundle (as a consequence of
310 --no-deploy), disables --cleanup and sets the working directory
314 don't use system "tar
"
316 * --roottar | --root-tar | -R
317 print out the tar file that contains all the files that would be
318 extracted in the root directory, useful to redirect to file or
319 pipe to the tar program
321 * --show | --show-options | -s
322 print configured options and exit
324 * --tar | -t <program-path>
325 set the system "tar
" program to use.
327 * --tempdir | --no-tempdir
328 by default a temporary directory is created (same as specifying
329 '--tempdir'), but you can execute directly in the workdir (see below)
332 * --tempdir-mode | -m
333 set permissions of temporary directory (octal string)
335 * --workdir | --work-directory | --deploy-directory | -w
336 working base directory (a temporary subdirectory will be created
344 print "$0 version
$VERSION\n";
348 package Deployable::Tar;
352 my $self = {ref $_[0] ? %{$_[0]} : @_};
353 $package = 'Deployable::Tar::Internal';
354 if (!$self->{'no-tar'}) {
355 if ((exists $self->{tar}) || (open my $fh, '-|', 'tar', '--help')) {
356 $package = 'Deployable::Tar::External';
357 $self->{tar} ||= 'tar';
359 } ## end if (!$self->{'no-tar'})
360 bless $self, $package;
361 $self->initialise() if $self->can('initialise');
365 package Deployable::Tar::External;
366 use English qw( -no_match_vars );
372 : $self->{gzip
} ?
'z'
374 $self->{_list_command
} = 'tv' . $compression . 'f';
375 $self->{_extract_command
} = 'x' . $compression . 'f';
376 } ## end sub initialise
381 open my $tfh, '|-', $self->{tar
}, $self->{_list_command
}, '-'
382 or die "open() on pipe to tar: $OS_ERROR";
383 main
::copy
($self->{fh
}, $tfh, $self->{size
});
386 } ## end sub print_filelist
391 open my $tfh, '|-', $self->{tar
}, $self->{_extract_command
}, '-'
392 or die "open() on pipe to tar: $OS_ERROR";
393 main
::copy
($self->{fh
}, $tfh, $self->{size
});
398 package Deployable
::Tar
::Internal
;
399 use English
qw( -no_match_vars );
405 my $data = main
::full_read
($self->{fh
}, $self->{size
});
406 open my $fh, '<', \
$data
407 or die "open() on internal variable: $OS_ERROR";
409 require Archive
::Tar
;
410 $self->{_tar
} = Archive
::Tar
->new();
411 $self->{_tar
}->read($fh);
412 } ## end if ($self->{size})
415 } ## end sub initialise
420 print {*STDOUT
} " $_\n" for $self->{_tar
}->list_files();
423 } ## end sub print_filelist
428 $self->{_tar
}->extract();