2 # *** NOTE *** LEAVE THIS MODULE LIST AS A PARAGRAPH
6 our $VERSION = '0.0.3';
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( dirname );
13 use POSIX
qw( strftime );
14 use Getopt
::Long
qw( :config gnu_getopt );
17 # *** NOTE *** LEAVE EMPTY LINE ABOVE
19 my %default_config = ( # default values
20 workdir
=> '/tmp/our-deploy',
43 workdir|work-directory|deploy-directory|w=s
47 usage
() if $config{usage
};
48 version
() if $config{version
};
53 while (read DATA
, my $buffer, 4096) {
54 print {*STDOUT
} $buffer;
57 } ## end if ($config{tar})
59 my $TAR_PACKAGE = eval {
62 } || 'Pseudo::Archive::Tar';
63 my $tar = $TAR_PACKAGE->new();
66 if ($config{roottar
}) {
67 my ($root_tar) = $tar->get_files('root.tar');
69 print {*STDOUT
} $root_tar->get_content();
71 } ## end if ($config{roottar})
73 if ($config{heretar
}) {
74 my ($here_tar) = $tar->get_files('here.tar');
76 print {*STDOUT
} $here_tar->get_content();
78 } ## end if ($config{heretar})
80 if ($config{filelist
}) {
81 my $root_tar = get_sub_tar
($tar, 'root.tar');
82 print "root $_\n" for $root_tar->list_files();
83 my $here_tar = get_sub_tar
($tar, 'here.tar');
84 print "here $_\n" for $here_tar->list_files();
86 } ## end if ($config{filelist})
88 my %script_config = (%default_config, get_config
($tar));
91 print {*STDOUT
} Data
::Dumper
::Dumper
(\
%script_config);
95 # Merge configurations and go on
96 %config = (%script_config, %config);
98 if ($config{inspect
}) {
100 $config{'deploy'} = 0;
101 $config{'tempdir'} = 0;
102 $config{workdir
} = $config{inspect
};
103 } ## end if ($config{inspect})
105 if ($config{dryrun
}) {
106 require Data
::Dumper
;
107 print {*STDOUT
} Data
::Dumper
::Dumper
(\
%config);
111 # go into the working directory, creating any intermediate if needed
112 mkpath
($config{workdir
});
113 chdir($config{workdir
});
114 print {*STDERR
} "### Got into working directory '$config{workdir}'\n\n";
117 if ($config{'tempdir'}) { # Only if allowed
118 my $now = strftime
('%Y-%m-%d_%H-%M-%S', localtime);
120 tempdir
($now . 'X' x
10, DIR
=> '.', CLEANUP
=> $config{cleanup
});
124 "### Created and got into temporary directory '$tempdir'\n";
125 print {*STDERR
} "### (will clean it up later)\n" if $config{cleanup
};
126 print {*STDERR
} "\n";
127 } ## end if ($config{'tempdir'})
129 eval { # Not really needed, but you know...
130 $ENV{PATH
} = '/bin:/usr/bin:/sbin:/usr/sbin';
132 execute_deploy_programs
() if $config{'deploy'};
134 carp
$EVAL_ERROR if $EVAL_ERROR;
136 # Get back so that cleanup can successfully happen, if requested
137 chdir '..' if defined $tempdir;
140 my ($tar, $filename) = @_;
141 my ($file) = $tar->get_files($filename);
142 my $contents = $file->get_content();
143 open my $fh, '<', \
$contents
144 or die "open() on internal variable: $OS_ERROR";
146 my $subtar = $TAR_PACKAGE->new();
149 } ## end sub get_sub_tar
151 sub execute_deploy_programs
{
152 my @deploy_programs = @
{$config{deploy
} || []};
154 if ($config{bundle
}) { # add all executable scripts in current directory
155 print {*STDERR
} "### Auto-deploying all executables in main dir\n\n";
156 my %flag_for = map { $_ => 1 } @deploy_programs;
158 for my $item (sort readdir $dh) {
159 next if $flag_for{$item};
160 next unless ((-f
$item) || (-l
$item)) && (-x
$item);
161 $flag_for{$item} = 1;
162 push @deploy_programs, $item;
163 } ## end for my $item (sort readdir...
165 } ## end if ($config{bundle})
168 for my $deploy (@deploy_programs) {
169 $deploy = catfile
('.', $deploy)
170 unless file_name_is_absolute
($deploy);
172 print {*STDERR
} "### Skipping '$deploy', not executable\n\n";
175 print {*STDERR
} "### Executing '$deploy'...\n";
176 system {$deploy} $deploy;
177 print {*STDERR
} "\n";
178 } ## end for my $deploy (@deploy_programs)
181 } ## end sub execute_deploy_programs
186 my ($file) = $tar->get_files('config.pl');
187 return unless $file && $file->has_content();
189 my $config = eval 'my ' . $file->get_content() or return;
190 return $config unless wantarray;
192 } ## end sub get_config
197 my $here_tar = get_sub_tar
($tar, 'here.tar');
198 $here_tar->extract();
200 my $root_dir = $config{inspect
} ?
'root' : '/';
201 mkpath
$root_dir unless -d
$root_dir;
204 my $root_tar = get_sub_tar
($tar, 'root.tar');
205 $root_tar->extract();
209 } ## end sub save_files
212 print {*STDOUT
} <<"END_OF_USAGE" ;
215 More or less, this script is intended to be launched without parameters.
216 Anyway, you can also set the following options, which will override any
217 present configuration (except in "--show-options"):
219 * --usage | --man | --help
220 print these help lines and exit
223 print script version and exit
225 * --bundle | --all-exec | -X
226 treat all executables in the main deployment directory as scripts
229 * --cleanup | -c | --no-cleanup
230 perform / don't perform temporary directory cleanup after work done
232 * --deploy | --no-deploy
233 deploy scripts are executed by default (same as specifying '--deploy')
234 but you can prevent it.
236 * --dryrun | --dry-run
237 print final options and exit
239 * --filelist | --list | -l
240 print a list of files that are shipped in the deploy script
242 * --heretar | --here-tar | -H
243 print out the tar file that contains all the files that would be
244 extracted in the temporary directory, useful to redirect to file or
245 pipe to the tar program
247 * --inspect | -i <dirname>
248 just extract all the stuff into <dirname> for inspection. Implies
249 --no-deploy, --no-tempdir, ignores --bundle (as a consequence of
250 --no-deploy), disables --cleanup and sets the working directory
253 * --roottar | --root-tar | -R
254 print out the tar file that contains all the files that would be
255 extracted in the root directory, useful to redirect to file or
256 pipe to the tar program
258 * --show | --show-options | -s
259 print configured options and exit
262 print out the tar file that contains all the shipped files, useful
263 to redirect to file or pipe to the tar program
265 * --tempdir | --no-tempdir
266 by default a temporary directory is created (same as specifying
267 '--tempdir'), but you can execute directly in the workdir (see below)
270 * --workdir | --work-directory | --deploy-directory | -w
271 working base directory (a temporary subdirectory will be created
279 print "$0 version $VERSION\n";
283 package Pseudo::Archive::Tar;
286 use IPC::Open3 qw( open3 );
287 use Symbol
qw( gensym );
288 use English
qw( -no_match_vars );
293 my $self = bless {@_}, $package;
297 sub _interact_with_program
{
298 my ($self, @command) = @_;
300 my ($in, $out, $err);
302 my $pid = open3
($in, $out, $err, @command) or die "open3(): $OS_ERROR";
306 my $offset = 0; # for taking stuff from $self->{_tarfile}
307 my $to_select = IO
::Select
->new($in);
308 my $from_select = IO
::Select
->new($out, $err);
310 while ($from_select->exists($out)) {
311 if ($to_select->can_write(0)) {
312 my $nwritten = syswrite $in, $self->{_tarfile
}, 4096, $offset
313 or die "print(): $OS_ERROR";
315 $offset += $nwritten;
316 if ($offset == length($self->{_tarfile
})) {
317 $to_select->remove($in);
320 } ## end if ($to_select->can_write...
321 for my $reader ($from_select->can_read(0)) {
322 my $nread = sysread $reader, my $buffer, 4096;
323 die "read(): $OS_ERROR" unless defined $nread;
325 $from_select->remove($reader);
328 if ($reader == $err) {
329 warn "got something from tar's STDERR: $buffer...";
332 push @result, $buffer;
334 } ## end for my $reader ($from_select...
335 } ## end while ($from_select->exists...
336 die 'could not pipe all data to tar' if $in;
338 return join '', @result;
339 } ## end sub _interact_with_program
341 sub _transform_mode
{
343 my ($sticky, $user, $group, $other) = unpack 'A A3 A3 A3', $mode;
346 for my $rwx ($user, $group, $other) {
347 my ($r, $w, $x) = split //, $rwx;
349 $v |= 1 if $x eq 'x';
350 $v |= 1 && $sticky |= $sticky_mask if lc($x) eq 's';
351 $v |= 2 if $w eq 'w';
352 $v |= 4 if $r eq 'r';
355 } ## end for my $rwx ($user, $group...
356 return oct(join '', $sticky, $user, $group, $other);
357 } ## end sub _transform_mode
362 my $filelist = $self->_interact_with_program(qw( tar tvf - ));
365 for my $line (split /\n/, $filelist) {
366 my ($mode, $ug, $size, $date1, $date2, $filename) =
367 split /\s+/, $line, 6;
369 Pseudo
::Archive
::Tar
::File
->new(
370 mode
=> _transform_mode
($mode),
371 is_dir
=> (substr($mode, 0, 1) eq 'd'),
376 } ## end for my $line (split /\n/...
378 $self->{_files
} = \
@files;
380 } ## end sub _refresh_list
383 my ($self, $handle) = @_;
386 $self->{_tarfile
} = <$handle>;
387 $self->_refresh_list();
393 my @files = map { $_->full_path() } @
{$self->{_files
}};
394 return @files if wantarray;
396 } ## end sub list_files
400 return @
{$self->{_files
}} unless @_;
402 return grep { $target eq $_->full_path() } @
{$self->{_files
}};
403 } ## end sub get_files
405 package Pseudo
::Archive
::Tar
::File
;
406 use Scalar
::Util
qw( weaken );
410 my $self = bless {@_}, $package;
411 weaken
$self->{_parent
};
415 sub full_path
{ return $_[0]->{name
}; }
416 sub mode
{ return $_[0]->{mode
}; }
417 sub has_content
{ return $_[0]->{size
}; }
418 sub is_dir
{ return $_[0]->{is_dir
}; }
422 return $self->{_parent
}
423 ->_interact_with_program(qw( tar xOf - ), $self->{name
});